Added Android code
[wl-app.git] / Android / folioreader / src / main / java / com / folioreader / ui / folio / fragment / FolioPageFragment.java
diff --git a/Android/folioreader/src/main/java/com/folioreader/ui/folio/fragment/FolioPageFragment.java b/Android/folioreader/src/main/java/com/folioreader/ui/folio/fragment/FolioPageFragment.java
new file mode 100755 (executable)
index 0000000..e5bdbfd
--- /dev/null
@@ -0,0 +1,1010 @@
+package com.folioreader.ui.folio.fragment;\r
+\r
+import android.app.Activity;\r
+import android.annotation.SuppressLint;\r
+import android.content.Intent;\r
+import android.content.res.Configuration;\r
+import android.graphics.Color;\r
+import android.graphics.PorterDuff;\r
+import android.graphics.drawable.Drawable;\r
+import android.net.Uri;\r
+import android.os.Build;\r
+import android.os.Bundle;\r
+import android.support.v4.app.Fragment;\r
+import android.support.v4.content.ContextCompat;\r
+import android.text.TextUtils;\r
+import android.util.Log;\r
+import android.view.LayoutInflater;\r
+import android.view.View;\r
+import android.view.ViewGroup;\r
+import android.view.animation.Animation;\r
+import android.view.animation.AnimationUtils;\r
+import android.webkit.JavascriptInterface;\r
+import android.webkit.JsResult;\r
+import android.webkit.WebChromeClient;\r
+import android.webkit.WebResourceRequest;\r
+import android.webkit.WebResourceResponse;\r
+import android.webkit.WebView;\r
+import android.webkit.WebViewClient;\r
+import android.widget.TextView;\r
+import android.widget.Toast;\r
+\r
+import com.bossturban.webviewmarker.TextSelectionSupport;\r
+import com.folioreader.Config;\r
+import com.folioreader.Constants;\r
+import com.folioreader.R;\r
+import com.folioreader.model.HighLight;\r
+import com.folioreader.model.HighlightImpl;\r
+import com.folioreader.model.event.AnchorIdEvent;\r
+import com.folioreader.model.event.BusOwner;\r
+import com.folioreader.model.event.MediaOverlayHighlightStyleEvent;\r
+import com.folioreader.model.event.MediaOverlayPlayPauseEvent;\r
+import com.folioreader.model.event.MediaOverlaySpeedEvent;\r
+import com.folioreader.model.event.ReloadDataEvent;\r
+import com.folioreader.model.event.RewindIndexEvent;\r
+import com.folioreader.model.event.WebViewPosition;\r
+import com.folioreader.model.quickaction.ActionItem;\r
+import com.folioreader.model.quickaction.QuickAction;\r
+import com.folioreader.model.sqlite.HighLightTable;\r
+import com.folioreader.ui.base.HtmlTask;\r
+import com.folioreader.ui.base.HtmlTaskCallback;\r
+import com.folioreader.ui.base.HtmlUtil;\r
+import com.folioreader.ui.folio.activity.FolioActivity;\r
+import com.folioreader.ui.folio.mediaoverlay.MediaController;\r
+import com.folioreader.ui.folio.mediaoverlay.MediaControllerCallbacks;\r
+import com.folioreader.util.AppUtil;\r
+import com.folioreader.util.FolioReader;\r
+import com.folioreader.util.HighlightUtil;\r
+import com.folioreader.util.SMILParser;\r
+import com.folioreader.util.UiUtil;\r
+import com.folioreader.view.ObservableWebView;\r
+import com.folioreader.view.VerticalSeekbar;\r
+import com.squareup.otto.Bus;\r
+import com.squareup.otto.Subscribe;\r
+\r
+import org.readium.r2_streamer.model.publication.link.Link;\r
+\r
+import java.io.UnsupportedEncodingException;\r
+import java.net.URLDecoder;\r
+import java.util.Locale;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+/**\r
+ * Created by mahavir on 4/2/16.\r
+ */\r
+@SuppressWarnings("PMD.AvoidDuplicateLiterals")\r
+public class FolioPageFragment extends Fragment implements HtmlTaskCallback, MediaControllerCallbacks, ObservableWebView.SeekBarListener {\r
+\r
+    public static final String KEY_FRAGMENT_FOLIO_POSITION = "com.folioreader.ui.folio.fragment.FolioPageFragment.POSITION";\r
+    public static final String KEY_FRAGMENT_FOLIO_BOOK_TITLE = "com.folioreader.ui.folio.fragment.FolioPageFragment.BOOK_TITLE";\r
+    public static final String KEY_FRAGMENT_EPUB_FILE_NAME = "com.folioreader.ui.folio.fragment.FolioPageFragment.EPUB_FILE_NAME";\r
+    private static final String KEY_IS_SMIL_AVAILABLE = "com.folioreader.ui.folio.fragment.FolioPageFragment.IS_SMIL_AVAILABLE";\r
+    public static final String TAG = FolioPageFragment.class.getSimpleName();\r
+\r
+    private static final int ACTION_ID_COPY = 1001;\r
+    private static final int ACTION_ID_SHARE = 1002;\r
+    private static final int ACTION_ID_HIGHLIGHT = 1003;\r
+    private static final int ACTION_ID_DEFINE = 1004;\r
+\r
+    private static final int ACTION_ID_HIGHLIGHT_COLOR = 1005;\r
+    private static final int ACTION_ID_DELETE = 1006;\r
+\r
+    private static final int ACTION_ID_HIGHLIGHT_YELLOW = 1007;\r
+    private static final int ACTION_ID_HIGHLIGHT_GREEN = 1008;\r
+    private static final int ACTION_ID_HIGHLIGHT_BLUE = 1009;\r
+    private static final int ACTION_ID_HIGHLIGHT_PINK = 1010;\r
+    private static final int ACTION_ID_HIGHLIGHT_UNDERLINE = 1011;\r
+    private static final String KEY_TEXT_ELEMENTS = "text_elements";\r
+    private static final String SPINE_ITEM = "spine_item";\r
+\r
+    private String mHtmlString = null;\r
+    private boolean hasMediaOverlay = false;\r
+    private String mAnchorId;\r
+    private String rangy = "";\r
+    private String highlightId;\r
+\r
+    public interface FolioPageFragmentCallback {\r
+\r
+        void setPagerToPosition(String href);\r
+\r
+        void setLastWebViewPosition(int position);\r
+\r
+        void goToChapter(String href);\r
+    }\r
+\r
+    private View mRootView;\r
+\r
+    private VerticalSeekbar mScrollSeekbar;\r
+    private ObservableWebView mWebview;\r
+    private TextSelectionSupport mTextSelectionSupport;\r
+    private TextView mPagesLeftTextView, mMinutesLeftTextView;\r
+    private FolioPageFragmentCallback mActivityCallback;\r
+\r
+    private int mScrollY;\r
+    private int mTotalMinutes;\r
+    private String mSelectedText;\r
+    private Animation mFadeInAnimation, mFadeOutAnimation;\r
+\r
+    private Link spineItem;\r
+    private int mPosition = -1;\r
+    private String mBookTitle;\r
+    private String mEpubFileName = null;\r
+    private int mPos;\r
+    private boolean mIsPageReloaded;\r
+    private int mLastWebviewScrollpos;\r
+\r
+    private String highlightStyle;\r
+\r
+    private MediaController mediaController;\r
+    private Config mConfig;\r
+    private String mBookId;\r
+\r
+    public static FolioPageFragment newInstance(int position, String bookTitle, Link spineRef, String bookId) {\r
+        FolioPageFragment fragment = new FolioPageFragment();\r
+        Bundle args = new Bundle();\r
+        args.putInt(KEY_FRAGMENT_FOLIO_POSITION, position);\r
+        args.putString(KEY_FRAGMENT_FOLIO_BOOK_TITLE, bookTitle);\r
+        args.putString(FolioReader.INTENT_BOOK_ID, bookId);\r
+        args.putSerializable(SPINE_ITEM, spineRef);\r
+        fragment.setArguments(args);\r
+        return fragment;\r
+    }\r
+\r
+    @Override\r
+    public View onCreateView(LayoutInflater inflater,\r
+                             ViewGroup container, Bundle savedInstanceState) {\r
+        if ((savedInstanceState != null)\r
+                && savedInstanceState.containsKey(KEY_FRAGMENT_FOLIO_POSITION)\r
+                && savedInstanceState.containsKey(KEY_FRAGMENT_FOLIO_BOOK_TITLE)) {\r
+            mPosition = savedInstanceState.getInt(KEY_FRAGMENT_FOLIO_POSITION);\r
+            mBookTitle = savedInstanceState.getString(KEY_FRAGMENT_FOLIO_BOOK_TITLE);\r
+            mEpubFileName = savedInstanceState.getString(KEY_FRAGMENT_EPUB_FILE_NAME);\r
+            mBookId = getArguments().getString(FolioReader.INTENT_BOOK_ID);\r
+            spineItem = (Link) savedInstanceState.getSerializable(SPINE_ITEM);\r
+        } else {\r
+            mPosition = getArguments().getInt(KEY_FRAGMENT_FOLIO_POSITION);\r
+            mBookTitle = getArguments().getString(KEY_FRAGMENT_FOLIO_BOOK_TITLE);\r
+            mEpubFileName = getArguments().getString(KEY_FRAGMENT_EPUB_FILE_NAME);\r
+            spineItem = (Link) getArguments().getSerializable(SPINE_ITEM);\r
+            mBookId = getArguments().getString(FolioReader.INTENT_BOOK_ID);\r
+        }\r
+        if (spineItem != null) {\r
+            if (spineItem.properties.contains("media-overlay")) {\r
+                mediaController = new MediaController(getActivity(), MediaController.MediaType.SMIL, this);\r
+                hasMediaOverlay = true;\r
+            } else {\r
+                mediaController = new MediaController(getActivity(), MediaController.MediaType.TTS, this);\r
+                mediaController.setTextToSpeech(getActivity());\r
+            }\r
+        }\r
+        highlightStyle = HighlightImpl.HighlightStyle.classForStyle(HighlightImpl.HighlightStyle.Normal);\r
+        mRootView = View.inflate(getActivity(), R.layout.folio_page_fragment, null);\r
+        mPagesLeftTextView = (TextView) mRootView.findViewById(R.id.pagesLeft);\r
+        mMinutesLeftTextView = (TextView) mRootView.findViewById(R.id.minutesLeft);\r
+\r
+        Activity activity = getActivity();\r
+\r
+        mConfig = AppUtil.getSavedConfig(activity);\r
+\r
+        if (activity instanceof FolioPageFragmentCallback)\r
+            mActivityCallback = (FolioPageFragmentCallback) activity;\r
+\r
+        if (activity instanceof BusOwner)\r
+            ((BusOwner) activity).getBus().register(this);\r
+\r
+        initSeekbar();\r
+        initAnimations();\r
+        initWebView();\r
+        updatePagesLeftTextBg();\r
+\r
+        return mRootView;\r
+    }\r
+\r
+\r
+    private String getWebviewUrl() {\r
+        return Constants.LOCALHOST + mBookTitle + "/" + spineItem.href;\r
+    }\r
+\r
+    @Override\r
+    public void onConfigurationChanged(Configuration newConfig) {\r
+        super.onConfigurationChanged(newConfig);\r
+        float positionTopView = mWebview.getTop();\r
+        float contentHeight = mWebview.getContentHeight();\r
+        float currentScrollPosition = mScrollY;\r
+        float percentWebview = (currentScrollPosition - positionTopView) / contentHeight;\r
+        float webviewsize = mWebview.getContentHeight() - mWebview.getTop();\r
+        float positionInWV = webviewsize * percentWebview;\r
+        int positionY = Math.round(mWebview.getTop() + positionInWV);\r
+        mScrollY = positionY;\r
+    }\r
+\r
+    /**\r
+     * [EVENT BUS FUNCTION]\r
+     * Function triggered from {@link FolioActivity#initAudioView()} when pause/play\r
+     * button is clicked\r
+     *\r
+     * @param event of type {@link MediaOverlayPlayPauseEvent} contains if paused/played\r
+     */\r
+    @SuppressWarnings("unused")\r
+    @Subscribe\r
+    public void pauseButtonClicked(MediaOverlayPlayPauseEvent event) {\r
+        if (isAdded()\r
+                && spineItem.href.equals(event.getHref())) {\r
+            mediaController.stateChanged(event);\r
+        }\r
+    }\r
+\r
+    /**\r
+     * [EVENT BUS FUNCTION]\r
+     * Function triggered from {@link FolioActivity#initAudioView()} when speed\r
+     * change buttons are clicked\r
+     *\r
+     * @param event of type {@link MediaOverlaySpeedEvent} contains selected speed\r
+     *              type HALF,ONE,ONE_HALF and TWO.\r
+     */\r
+    @SuppressWarnings("unused")\r
+    @Subscribe\r
+    public void speedChanged(MediaOverlaySpeedEvent event) {\r
+        mediaController.setSpeed(event.getSpeed());\r
+    }\r
+\r
+    /**\r
+     * [EVENT BUS FUNCTION]\r
+     * Function triggered from {@link FolioActivity#initAudioView()} when new\r
+     * style is selected on button click.\r
+     *\r
+     * @param event of type {@link MediaOverlaySpeedEvent} contains selected style\r
+     *              of type DEFAULT,UNDERLINE and BACKGROUND.\r
+     */\r
+    @SuppressWarnings("unused")\r
+    @Subscribe\r
+    public void styleChanged(MediaOverlayHighlightStyleEvent event) {\r
+        if (isAdded()) {\r
+            switch (event.getStyle()) {\r
+                case DEFAULT:\r
+                    highlightStyle =\r
+                            HighlightImpl.HighlightStyle.classForStyle(HighlightImpl.HighlightStyle.Normal);\r
+                    break;\r
+                case UNDERLINE:\r
+                    highlightStyle =\r
+                            HighlightImpl.HighlightStyle.classForStyle(HighlightImpl.HighlightStyle.DottetUnderline);\r
+                    break;\r
+                case BACKGROUND:\r
+                    highlightStyle =\r
+                            HighlightImpl.HighlightStyle.classForStyle(HighlightImpl.HighlightStyle.TextColor);\r
+                    break;\r
+            }\r
+            mWebview.loadUrl(String.format(getString(R.string.setmediaoverlaystyle), highlightStyle));\r
+        }\r
+    }\r
+\r
+    /**\r
+     * [EVENT BUS FUNCTION]\r
+     * Function triggered when any EBook configuration is changed.\r
+     *\r
+     * @param reloadDataEvent empty POJO.\r
+     */\r
+    @Subscribe\r
+    public void reload(ReloadDataEvent reloadDataEvent) {\r
+        if (isAdded()) {\r
+            mLastWebviewScrollpos = mWebview.getScrollY();\r
+            mIsPageReloaded = true;\r
+            setHtml(true);\r
+            updatePagesLeftTextBg();\r
+        }\r
+    }\r
+\r
+    /**\r
+     * [EVENT BUS FUNCTION]\r
+     * Function triggered from {@link FolioActivity#onActivityResult(int, int, Intent)} when any item in toc clicked.\r
+     *\r
+     * @param event of type {@link AnchorIdEvent} contains selected chapter href.\r
+     */\r
+    @Subscribe\r
+    public void jumpToAnchorPoint(AnchorIdEvent event) {\r
+        if (isAdded() && event != null && event.getHref() != null) {\r
+            String href = event.getHref();\r
+            if (href != null && href.indexOf('#') != -1 && spineItem.href.equals(href.substring(0, href.lastIndexOf('#')))) {\r
+                mAnchorId = href.substring(href.lastIndexOf('#') + 1);\r
+                if (mWebview.getContentHeight() > 0 && mAnchorId != null) {\r
+                    mWebview.loadUrl("javascript:document.getElementById(\"" + mAnchorId + "\").scrollIntoView()");\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+    @Override\r
+    public void onReceiveHtml(String html) {\r
+        if (isAdded()) {\r
+            mHtmlString = html;\r
+            setHtml(false);\r
+        }\r
+    }\r
+\r
+    private void setHtml(boolean reloaded) {\r
+        if (spineItem != null) {\r
+            String ref = spineItem.href;\r
+            if (!reloaded && spineItem.properties.contains("media-overlay")) {\r
+                mediaController.setSMILItems(SMILParser.parseSMIL(mHtmlString));\r
+                mediaController.setUpMediaPlayer(spineItem.mediaOverlay, spineItem.mediaOverlay.getAudioPath(spineItem.href), mBookTitle);\r
+            }\r
+            mConfig = AppUtil.getSavedConfig(getActivity());\r
+            String path = ref.substring(0, ref.lastIndexOf('/'));\r
+            mWebview.loadDataWithBaseURL(\r
+                    Constants.LOCALHOST + mBookTitle + "/" + path + "/",\r
+                    HtmlUtil.getHtmlContent(getActivity(), mHtmlString, mConfig),\r
+                    "text/html",\r
+                    "UTF-8",\r
+                    null);\r
+        }\r
+    }\r
+\r
+    @Override\r
+    public void onStop() {\r
+        super.onStop();\r
+        mediaController.stop();\r
+        //TODO save last media overlay item\r
+    }\r
+\r
+    private void initWebView() {\r
+        mWebview = (ObservableWebView) mRootView.findViewById(R.id.contentWebView);\r
+        mWebview.setSeekBarListener(FolioPageFragment.this);\r
+\r
+        if (getActivity() instanceof ObservableWebView.ToolBarListener)\r
+            mWebview.setToolBarListener((ObservableWebView.ToolBarListener) getActivity());\r
+\r
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\r
+            WebView.setWebContentsDebuggingEnabled(true);\r
+        }\r
+\r
+        setupScrollBar();\r
+        mWebview.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {\r
+            @Override\r
+            public void onLayoutChange(View view, int left, int top, int right, int bottom,\r
+                                       int oldLeft, int oldTop, int oldRight, int oldBottom) {\r
+                int height =\r
+                        (int) Math.floor(mWebview.getContentHeight() * mWebview.getScale());\r
+                int webViewHeight = mWebview.getMeasuredHeight();\r
+                mScrollSeekbar.setMaximum(height - webViewHeight);\r
+            }\r
+        });\r
+\r
+        mWebview.getSettings().setJavaScriptEnabled(true);\r
+        mWebview.setVerticalScrollBarEnabled(false);\r
+        mWebview.getSettings().setAllowFileAccess(true);\r
+\r
+        mWebview.setHorizontalScrollBarEnabled(false);\r
+\r
+        mWebview.addJavascriptInterface(this, "Highlight");\r
+        mWebview.setScrollListener(new ObservableWebView.ScrollListener() {\r
+            @Override\r
+            public void onScrollChange(int percent) {\r
+                if (mWebview.getScrollY() != 0) {\r
+                    mScrollY = mWebview.getScrollY();\r
+                    if (isAdded()) {\r
+                        ((FolioActivity) getActivity()).setLastWebViewPosition(mScrollY);\r
+                    }\r
+                }\r
+                mScrollSeekbar.setProgressAndThumb(percent);\r
+                updatePagesLeftText(percent);\r
+\r
+            }\r
+        });\r
+\r
+        mWebview.setWebViewClient(new WebViewClient() {\r
+            @Override\r
+            public void onPageFinished(WebView view, String url) {\r
+                if (isAdded()) {\r
+                    if (mAnchorId != null)\r
+                        view.loadUrl("javascript:document.getElementById(\"" + mAnchorId + "\").scrollIntoView()");\r
+                    view.loadUrl("javascript:alert(getReadingTime())");\r
+                    if (!hasMediaOverlay) {\r
+                        view.loadUrl("javascript:alert(wrappingSentencesWithinPTags())");\r
+                    }\r
+                    view.loadUrl(String.format(getString(R.string.setmediaoverlaystyle),\r
+                            HighlightImpl.HighlightStyle.classForStyle(\r
+                                    HighlightImpl.HighlightStyle.Normal)));\r
+                    if (isCurrentFragment()) {\r
+                        setWebViewPosition(AppUtil.getPreviousBookStateWebViewPosition(getActivity(), mBookTitle));\r
+                    } else if (mIsPageReloaded) {\r
+                        setWebViewPosition(mLastWebviewScrollpos);\r
+                        mIsPageReloaded = false;\r
+                    }\r
+                    String rangy = HighlightUtil.generateRangyString(getPageName());\r
+                    FolioPageFragment.this.rangy = rangy;\r
+                    if (!rangy.isEmpty()) {\r
+                        loadRangy(view, rangy);\r
+                    }\r
+\r
+                    scrollToHighlightId();\r
+\r
+\r
+                }\r
+            }\r
+\r
+            @Override\r
+            public boolean shouldOverrideUrlLoading(WebView view, String url) {\r
+                if (!url.isEmpty() && url.length() > 0) {\r
+                    if (Uri.parse(url).getScheme().startsWith("highlight")) {\r
+                        final Pattern pattern = Pattern.compile(getString(R.string.pattern));\r
+                        try {\r
+                            String htmlDecode = URLDecoder.decode(url, "UTF-8");\r
+                            Matcher matcher = pattern.matcher(htmlDecode.substring(12));\r
+                            if (matcher.matches()) {\r
+                                double left = Double.parseDouble(matcher.group(1));\r
+                                double top = Double.parseDouble(matcher.group(2));\r
+                                double width = Double.parseDouble(matcher.group(3));\r
+                                double height = Double.parseDouble(matcher.group(4));\r
+                                onHighlight((int) (UiUtil.convertDpToPixel((float) left,\r
+                                        getActivity())),\r
+                                        (int) (UiUtil.convertDpToPixel((float) top,\r
+                                                getActivity())),\r
+                                        (int) (UiUtil.convertDpToPixel((float) width,\r
+                                                getActivity())),\r
+                                        (int) (UiUtil.convertDpToPixel((float) height,\r
+                                                getActivity())));\r
+                            }\r
+                        } catch (UnsupportedEncodingException e) {\r
+                            Log.d(TAG, e.getMessage());\r
+                        }\r
+                    } else {\r
+                        if (url.contains("storage")) {\r
+                            mActivityCallback.setPagerToPosition(url);\r
+                        } else if (url.endsWith(".xhtml") || url.endsWith(".html") || url.contains(".xhtml#") || url.contains(".html#")) {\r
+                            mActivityCallback.goToChapter(url);\r
+                        } else {\r
+                            // Otherwise, give the default behavior (open in browser)\r
+                            Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));\r
+                            startActivity(intent);\r
+                        }\r
+                    }\r
+                }\r
+                return true;\r
+            }\r
+\r
+\r
+            // prevent favicon.ico to be loaded automatically\r
+            @Override\r
+            public WebResourceResponse shouldInterceptRequest(WebView view, String url) {\r
+                if(url.toLowerCase().contains("/favicon.ico")) {\r
+                    try {\r
+                        return new WebResourceResponse("image/png", null, null);\r
+                    } catch (Exception e) {\r
+                        Log.e(TAG, "shouldInterceptRequest failed", e);\r
+                    }\r
+                }\r
+                return null;\r
+            }\r
+\r
+            // prevent favicon.ico to be loaded automatically\r
+            @Override\r
+            @SuppressLint("NewApi")\r
+            public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {\r
+                if(!request.isForMainFrame() && request.getUrl().getPath().endsWith("/favicon.ico")) {\r
+                    try {\r
+                        return new WebResourceResponse("image/png", null, null);\r
+                    } catch (Exception e) {\r
+                        Log.e(TAG, "shouldInterceptRequest failed", e);\r
+                    }\r
+                }\r
+                return null;\r
+            }\r
+        });\r
+\r
+        mWebview.setWebChromeClient(new WebChromeClient() {\r
+            @Override\r
+            public void onProgressChanged(WebView view, int progress) {\r
+\r
+                if (view.getProgress() == 100) {\r
+                    mWebview.postDelayed(new Runnable() {\r
+                        @Override\r
+                        public void run() {\r
+                            Log.d("scroll y", "Scrolly" + mScrollY);\r
+                            mWebview.scrollTo(0, mScrollY);\r
+                        }\r
+                    }, 100);\r
+                }\r
+            }\r
+\r
+            @Override\r
+            public boolean onJsAlert(WebView view, String url, String message, JsResult result) {\r
+                if (FolioPageFragment.this.isVisible()) {\r
+                    String rangyPattern = "\\d+\\$\\d+\\$\\d+\\$\\w+\\$";\r
+                    Pattern pattern = Pattern.compile(rangyPattern);\r
+                    Matcher matcher = pattern.matcher(message);\r
+                    if (matcher.matches()) {\r
+                        HighlightImpl highlightImpl = HighLightTable.getHighlightForRangy(message);\r
+                        if (HighLightTable.deleteHighlight(message)) {\r
+                            String rangy = HighlightUtil.generateRangyString(getPageName());\r
+                            loadRangy(view, rangy);\r
+                           // mTextSelectionSupport.endSelectionMode();\r
+                            if (highlightImpl != null) {\r
+                                HighlightUtil.sendHighlightBroadcastEvent(\r
+                                        FolioPageFragment.this.getActivity().getApplicationContext(),\r
+                                        highlightImpl,\r
+                                        HighLight.HighLightAction.DELETE);\r
+                            }\r
+                        }\r
+                    } else if (TextUtils.isDigitsOnly(message)) {\r
+                        mTotalMinutes = Integer.parseInt(message);\r
+                    } else {\r
+                        pattern = Pattern.compile(getString(R.string.pattern));\r
+                        matcher = pattern.matcher(message);\r
+                        if (matcher.matches()) {\r
+                            double left = Double.parseDouble(matcher.group(1));\r
+                            double top = Double.parseDouble(matcher.group(2));\r
+                            double width = Double.parseDouble(matcher.group(3));\r
+                            double height = Double.parseDouble(matcher.group(4));\r
+                            showTextSelectionMenu((int) (UiUtil.convertDpToPixel((float) left,\r
+                                    getActivity())),\r
+                                    (int) (UiUtil.convertDpToPixel((float) top,\r
+                                            getActivity())),\r
+                                    (int) (UiUtil.convertDpToPixel((float) width,\r
+                                            getActivity())),\r
+                                    (int) (UiUtil.convertDpToPixel((float) height,\r
+                                            getActivity())));\r
+                        } else {\r
+                            // to handle TTS playback when highlight is deleted.\r
+                            Pattern p = Pattern.compile("[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}");\r
+                            if (!p.matcher(message).matches() && (!message.equals("undefined")) && isCurrentFragment()) {\r
+                                mediaController.speakAudio(message);\r
+                            }\r
+                        }\r
+                    }\r
+                    result.confirm();\r
+                }\r
+                return true;\r
+            }\r
+        });\r
+\r
+//        mTextSelectionSupport = TextSelectionSupport.support(getActivity(), mWebview);\r
+//        mTextSelectionSupport.setSelectionListener(new TextSelectionSupport.SelectionListener() {\r
+//            @Override\r
+//            public void startSelection() {\r
+//            }\r
+//\r
+//            @Override\r
+//            public void selectionChanged(String text) {\r
+//                mSelectedText = text;\r
+//                getActivity().runOnUiThread(new Runnable() {\r
+//                    @Override\r
+//                    public void run() {\r
+//                        mWebview.loadUrl("javascript:alert(getRectForSelectedText())");\r
+//                    }\r
+//                });\r
+//            }\r
+//\r
+//            @Override\r
+//            public void endSelection() {\r
+//\r
+//            }\r
+//        });\r
+\r
+        mWebview.getSettings().setDefaultTextEncodingName("utf-8");\r
+        mWebview.setOnLongClickListener(new View.OnLongClickListener() {\r
+            @Override\r
+            public boolean onLongClick(View v) {\r
+                return true;\r
+            }\r
+        });\r
+        mWebview.setLongClickable(false);\r
+        mWebview.setHapticFeedbackEnabled(false);\r
+        new HtmlTask(this).execute(getWebviewUrl());\r
+    }\r
+\r
+    private void loadRangy(WebView view, String rangy) {\r
+        view.loadUrl(String.format("javascript:if(typeof ssReader !== \"undefined\"){ssReader.setHighlights('%s');}", rangy));\r
+    }\r
+\r
+    private void setupScrollBar() {\r
+        UiUtil.setColorToImage(getActivity(), mConfig.getThemeColor(), mScrollSeekbar.getProgressDrawable());\r
+        Drawable thumbDrawable = ContextCompat.getDrawable(getActivity(), R.drawable.icons_sroll);\r
+        UiUtil.setColorToImage(getActivity(), mConfig.getThemeColor(), (thumbDrawable));\r
+        mScrollSeekbar.setThumb(thumbDrawable);\r
+    }\r
+\r
+    private void initSeekbar() {\r
+        mScrollSeekbar = (VerticalSeekbar) mRootView.findViewById(R.id.scrollSeekbar);\r
+        mScrollSeekbar.getProgressDrawable()\r
+                .setColorFilter(getResources()\r
+                                .getColor(R.color.app_green),\r
+                        PorterDuff.Mode.SRC_IN);\r
+    }\r
+\r
+    private void updatePagesLeftTextBg() {\r
+\r
+        if (mConfig.isNightMode()) {\r
+            mRootView.findViewById(R.id.indicatorLayout)\r
+                    .setBackgroundColor(getResources().getColor(R.color.dark_night));\r
+        } else {\r
+            mRootView.findViewById(R.id.indicatorLayout)\r
+                    .setBackgroundColor(Color.WHITE);\r
+        }\r
+    }\r
+\r
+    private void updatePagesLeftText(int scrollY) {\r
+        try {\r
+            int currentPage = (int) (Math.ceil((double) scrollY / mWebview.getWebViewHeight()) + 1);\r
+            int totalPages =\r
+                    (int) Math.ceil((double) mWebview.getContentHeightVal()\r
+                            / mWebview.getWebViewHeight());\r
+            int pagesRemaining = totalPages - currentPage;\r
+            String pagesRemainingStrFormat =\r
+                    pagesRemaining > 1 ?\r
+                            getString(R.string.pages_left) : getString(R.string.page_left);\r
+            String pagesRemainingStr = String.format(Locale.US,\r
+                    pagesRemainingStrFormat, pagesRemaining);\r
+\r
+            int minutesRemaining =\r
+                    (int) Math.ceil((double) (pagesRemaining * mTotalMinutes) / totalPages);\r
+            String minutesRemainingStr;\r
+            if (minutesRemaining > 1) {\r
+                minutesRemainingStr =\r
+                        String.format(Locale.US, getString(R.string.minutes_left),\r
+                                minutesRemaining);\r
+            } else if (minutesRemaining == 1) {\r
+                minutesRemainingStr =\r
+                        String.format(Locale.US, getString(R.string.minute_left),\r
+                                minutesRemaining);\r
+            } else {\r
+                minutesRemainingStr = getString(R.string.less_than_minute);\r
+            }\r
+\r
+            mMinutesLeftTextView.setText(minutesRemainingStr);\r
+            mPagesLeftTextView.setText(pagesRemainingStr);\r
+        } catch (java.lang.ArithmeticException exp) {\r
+            Log.d("divide error", exp.toString());\r
+        }\r
+    }\r
+\r
+    private void initAnimations() {\r
+        mFadeInAnimation = AnimationUtils.loadAnimation(getActivity(), R.anim.fadein);\r
+        mFadeInAnimation.setAnimationListener(new Animation.AnimationListener() {\r
+            @Override\r
+            public void onAnimationStart(Animation animation) {\r
+                mScrollSeekbar.setVisibility(View.VISIBLE);\r
+            }\r
+\r
+            @Override\r
+            public void onAnimationEnd(Animation animation) {\r
+                fadeOutSeekBarIfVisible();\r
+            }\r
+\r
+            @Override\r
+            public void onAnimationRepeat(Animation animation) {\r
+\r
+            }\r
+        });\r
+        mFadeOutAnimation = AnimationUtils.loadAnimation(getActivity(), R.anim.fadeout);\r
+        mFadeOutAnimation.setAnimationListener(new Animation.AnimationListener() {\r
+            @Override\r
+            public void onAnimationStart(Animation animation) {\r
+\r
+            }\r
+\r
+            @Override\r
+            public void onAnimationEnd(Animation animation) {\r
+                mScrollSeekbar.setVisibility(View.INVISIBLE);\r
+            }\r
+\r
+            @Override\r
+            public void onAnimationRepeat(Animation animation) {\r
+\r
+            }\r
+        });\r
+    }\r
+\r
+    public void fadeInSeekBarIfInvisible() {\r
+        if (mScrollSeekbar.getVisibility() == View.INVISIBLE ||\r
+                mScrollSeekbar.getVisibility() == View.GONE) {\r
+            mScrollSeekbar.startAnimation(mFadeInAnimation);\r
+        }\r
+    }\r
+\r
+    public void fadeOutSeekBarIfVisible() {\r
+        if (mScrollSeekbar.getVisibility() == View.VISIBLE) {\r
+            mScrollSeekbar.startAnimation(mFadeOutAnimation);\r
+        }\r
+    }\r
+\r
+    @Override\r
+    public void onDestroyView() {\r
+        mFadeInAnimation.setAnimationListener(null);\r
+        mFadeOutAnimation.setAnimationListener(null);\r
+\r
+        Activity activity = getActivity();\r
+        if (activity instanceof BusOwner)\r
+            ((BusOwner) activity).getBus().unregister(this);\r
+        super.onDestroyView();\r
+    }\r
+\r
+    @Override\r
+    public void onSaveInstanceState(Bundle outState) {\r
+        super.onSaveInstanceState(outState);\r
+        outState.putInt(KEY_FRAGMENT_FOLIO_POSITION, mPosition);\r
+        outState.putString(KEY_FRAGMENT_FOLIO_BOOK_TITLE, mBookTitle);\r
+        outState.putString(KEY_FRAGMENT_EPUB_FILE_NAME, mEpubFileName);\r
+        outState.putSerializable(SPINE_ITEM, spineItem);\r
+    }\r
+\r
+    public void highlight(HighlightImpl.HighlightStyle style, boolean isCreated) {\r
+        if (isCreated) {\r
+            mWebview.loadUrl(String.format("javascript:if(typeof ssReader !== \"undefined\"){ssReader.highlightSelection('%s');}", HighlightImpl.HighlightStyle.classForStyle(style)));\r
+        } else {\r
+            mWebview.loadUrl(String.format("javascript:alert(setHighlightStyle('%s'))", "highlight_" + HighlightImpl.HighlightStyle.classForStyle(style)));\r
+        }\r
+    }\r
+\r
+    public void highlightRemove() {\r
+        mWebview.loadUrl("javascript:alert(removeThisHighlight())");\r
+    }\r
+\r
+    public void showTextSelectionMenu(int x, int y, final int width, final int height) {\r
+        final ViewGroup root =\r
+                (ViewGroup) getActivity().getWindow()\r
+                        .getDecorView().findViewById(android.R.id.content);\r
+        final View view = new View(getActivity());\r
+        view.setLayoutParams(new ViewGroup.LayoutParams(width, height));\r
+        view.setBackgroundColor(Color.TRANSPARENT);\r
+\r
+        root.addView(view);\r
+\r
+        view.setX(x);\r
+        view.setY(y);\r
+        final QuickAction quickAction =\r
+                new QuickAction(getActivity(), QuickAction.HORIZONTAL);\r
+        quickAction.addActionItem(new ActionItem(ACTION_ID_COPY,\r
+                getString(R.string.copy)));\r
+        quickAction.addActionItem(new ActionItem(ACTION_ID_HIGHLIGHT,\r
+                getString(R.string.highlight)));\r
+        if (!mSelectedText.trim().contains(" ")) {\r
+            quickAction.addActionItem(new ActionItem(ACTION_ID_DEFINE,\r
+                    getString(R.string.define)));\r
+        }\r
+        quickAction.addActionItem(new ActionItem(ACTION_ID_SHARE,\r
+                getString(R.string.share)));\r
+        quickAction.setOnActionItemClickListener(new QuickAction.OnActionItemClickListener() {\r
+            @Override\r
+            public void onItemClick(QuickAction source, int pos, int actionId) {\r
+                quickAction.dismiss();\r
+                root.removeView(view);\r
+                onTextSelectionActionItemClicked(actionId, view, width, height);\r
+            }\r
+        });\r
+        quickAction.show(view, width, height);\r
+    }\r
+\r
+    private void onTextSelectionActionItemClicked(int actionId, View view, int width, int height) {\r
+        if (actionId == ACTION_ID_COPY) {\r
+            UiUtil.copyToClipboard(getActivity(), mSelectedText);\r
+            Toast.makeText(getActivity(), getString(R.string.copied), Toast.LENGTH_SHORT).show();\r
+            mTextSelectionSupport.endSelectionMode();\r
+        } else if (actionId == ACTION_ID_SHARE) {\r
+            UiUtil.share(getActivity(), mSelectedText);\r
+        } else if (actionId == ACTION_ID_DEFINE) {\r
+            showDictDialog(mSelectedText);\r
+            mTextSelectionSupport.endSelectionMode();\r
+        } else if (actionId == ACTION_ID_HIGHLIGHT) {\r
+            onHighlight(view, width, height, true);\r
+        }\r
+    }\r
+\r
+    private void showDictDialog(String mSelectedText) {\r
+        DictionaryFragment dictionaryFragment = new DictionaryFragment();\r
+        Bundle b = new Bundle();\r
+        b.putString(Constants.SELECTED_WORD, mSelectedText);\r
+        dictionaryFragment.setArguments(b);\r
+        dictionaryFragment.show(getFragmentManager(), DictionaryFragment.class.getName());\r
+    }\r
+\r
+    private void onHighlight(int x, int y, int width, int height) {\r
+        final View view = new View(getActivity());\r
+        view.setLayoutParams(new ViewGroup.LayoutParams(width, height));\r
+        view.setBackgroundColor(Color.TRANSPARENT);\r
+        view.setX(x);\r
+        view.setY(y);\r
+        onHighlight(view, width, height, false);\r
+    }\r
+\r
+    private void onHighlight(final View view, int width, int height, final boolean isCreated) {\r
+        ViewGroup root =\r
+                (ViewGroup) getActivity().getWindow().\r
+                        getDecorView().findViewById(android.R.id.content);\r
+        ViewGroup parent = (ViewGroup) view.getParent();\r
+        if (parent == null) {\r
+            root.addView(view);\r
+        } else {\r
+            final int index = parent.indexOfChild(view);\r
+            parent.removeView(view);\r
+            parent.addView(view, index);\r
+        }\r
+\r
+        final QuickAction quickAction = new QuickAction(getActivity(), QuickAction.HORIZONTAL);\r
+        quickAction.addActionItem(new ActionItem(ACTION_ID_HIGHLIGHT_COLOR,\r
+                getResources().getDrawable(R.drawable.colors_marker)));\r
+        quickAction.addActionItem(new ActionItem(ACTION_ID_DELETE,\r
+                getResources().getDrawable(R.drawable.ic_action_discard)));\r
+        quickAction.addActionItem(new ActionItem(ACTION_ID_SHARE,\r
+                getResources().getDrawable(R.drawable.ic_action_share)));\r
+        final ViewGroup finalRoot = root;\r
+        quickAction.setOnActionItemClickListener(new QuickAction.OnActionItemClickListener() {\r
+            @Override\r
+            public void onItemClick(QuickAction source, int pos, int actionId) {\r
+                quickAction.dismiss();\r
+                finalRoot.removeView(view);\r
+                onHighlightActionItemClicked(actionId, view, isCreated);\r
+            }\r
+        });\r
+        quickAction.show(view, width, height);\r
+    }\r
+\r
+    private void onHighlightActionItemClicked(int actionId, View view, boolean isCreated) {\r
+        if (actionId == ACTION_ID_HIGHLIGHT_COLOR) {\r
+            onHighlightColors(view, isCreated);\r
+        } else if (actionId == ACTION_ID_SHARE) {\r
+            UiUtil.share(getActivity(), mSelectedText);\r
+            mTextSelectionSupport.endSelectionMode();\r
+        } else if (actionId == ACTION_ID_DELETE) {\r
+            highlightRemove();\r
+        }\r
+    }\r
+\r
+    private void onHighlightColors(final View view, final boolean isCreated) {\r
+        ViewGroup root =\r
+                (ViewGroup) getActivity().getWindow()\r
+                        .getDecorView().findViewById(android.R.id.content);\r
+        ViewGroup parent = (ViewGroup) view.getParent();\r
+        if (parent == null) {\r
+            root.addView(view);\r
+        } else {\r
+            final int index = parent.indexOfChild(view);\r
+            parent.removeView(view);\r
+            parent.addView(view, index);\r
+        }\r
+\r
+        final QuickAction quickAction = new QuickAction(getActivity(), QuickAction.HORIZONTAL);\r
+        quickAction.addActionItem(new ActionItem(ACTION_ID_HIGHLIGHT_YELLOW,\r
+                getResources().getDrawable(R.drawable.ic_yellow_marker)));\r
+        quickAction.addActionItem(new ActionItem(ACTION_ID_HIGHLIGHT_GREEN,\r
+                getResources().getDrawable(R.drawable.ic_green_marker)));\r
+        quickAction.addActionItem(new ActionItem(ACTION_ID_HIGHLIGHT_BLUE,\r
+                getResources().getDrawable(R.drawable.ic_blue_marker)));\r
+        quickAction.addActionItem(new ActionItem(ACTION_ID_HIGHLIGHT_PINK,\r
+                getResources().getDrawable(R.drawable.ic_pink_marker)));\r
+        quickAction.addActionItem(new ActionItem(ACTION_ID_HIGHLIGHT_UNDERLINE,\r
+                getResources().getDrawable(R.drawable.ic_underline_marker)));\r
+        final ViewGroup finalRoot = root;\r
+        quickAction.setOnActionItemClickListener(new QuickAction.OnActionItemClickListener() {\r
+            @Override\r
+            public void onItemClick(QuickAction source, int pos, int actionId) {\r
+                quickAction.dismiss();\r
+                finalRoot.removeView(view);\r
+                onHighlightColorsActionItemClicked(actionId, view, isCreated);\r
+            }\r
+        });\r
+        quickAction.show(view);\r
+    }\r
+\r
+    private void onHighlightColorsActionItemClicked(int actionId, View view, boolean isCreated) {\r
+        if (actionId == ACTION_ID_HIGHLIGHT_YELLOW) {\r
+            highlight(HighlightImpl.HighlightStyle.Yellow, isCreated);\r
+        } else if (actionId == ACTION_ID_HIGHLIGHT_GREEN) {\r
+            highlight(HighlightImpl.HighlightStyle.Green, isCreated);\r
+        } else if (actionId == ACTION_ID_HIGHLIGHT_BLUE) {\r
+            highlight(HighlightImpl.HighlightStyle.Blue, isCreated);\r
+        } else if (actionId == ACTION_ID_HIGHLIGHT_PINK) {\r
+            highlight(HighlightImpl.HighlightStyle.Pink, isCreated);\r
+        } else if (actionId == ACTION_ID_HIGHLIGHT_UNDERLINE) {\r
+            highlight(HighlightImpl.HighlightStyle.Underline, isCreated);\r
+        }\r
+        mTextSelectionSupport.endSelectionMode();\r
+    }\r
+\r
+    @Override\r
+    public void resetCurrentIndex() {\r
+        if (isCurrentFragment()) {\r
+            mWebview.loadUrl("javascript:alert(rewindCurrentIndex())");\r
+        }\r
+    }\r
+\r
+    @SuppressWarnings("unused")\r
+    @JavascriptInterface\r
+    public void onReceiveHighlights(String html) {\r
+        if (html != null) {\r
+            rangy = HighlightUtil.createHighlightRangy(getActivity().getApplicationContext(),\r
+                    html,\r
+                    mBookId,\r
+                    getPageName(),\r
+                    mPosition,\r
+                    rangy);\r
+        }\r
+    }\r
+\r
+    private String getPageName() {\r
+        return mBookTitle + "$" + spineItem.href;\r
+    }\r
+\r
+    @SuppressWarnings("unused")\r
+    @Subscribe\r
+    public void setWebView(final WebViewPosition position) {\r
+        if (position.getHref().equals(spineItem.href) && isAdded()) {\r
+            highlightId = position.getHighlightId();\r
+\r
+            if (mWebview.getContentHeight() > 0) {\r
+                scrollToHighlightId();\r
+                //Webview.loadUrl(String.format(getString(R.string.goto_highlight), highlightId));\r
+            }\r
+        }\r
+    }\r
+\r
+    public void setWebViewPosition(final int position) {\r
+        mWebview.post(new Runnable() {\r
+            @Override\r
+            public void run() {\r
+                if (isAdded()) {\r
+                    mWebview.scrollTo(0, position);\r
+                }\r
+            }\r
+        });\r
+    }\r
+\r
+    @Override\r
+    public void highLightText(String fragmentId) {\r
+        mWebview.loadUrl(String.format(getString(R.string.audio_mark_id), fragmentId));\r
+    }\r
+\r
+    @Override\r
+    public void highLightTTS() {\r
+        mWebview.loadUrl("javascript:alert(getSentenceWithIndex('epub-media-overlay-playing'))");\r
+    }\r
+\r
+    @JavascriptInterface\r
+    public void getUpdatedHighlightId(String id, String style) {\r
+        if (id != null) {\r
+            HighlightImpl highlightImpl = HighLightTable.updateHighlightStyle(id, style);\r
+            if (highlightImpl != null) {\r
+                HighlightUtil.sendHighlightBroadcastEvent(\r
+                        getActivity().getApplicationContext(),\r
+                        highlightImpl,\r
+                        HighLight.HighLightAction.MODIFY);\r
+            }\r
+            final String rangyString = HighlightUtil.generateRangyString(getPageName());\r
+            getActivity().runOnUiThread(new Runnable() {\r
+                public void run() {\r
+                    loadRangy(mWebview, rangyString);\r
+                }\r
+            });\r
+\r
+        }\r
+    }\r
+\r
+    @Subscribe\r
+    public void resetCurrentIndex(RewindIndexEvent resetIndex) {\r
+        if (isCurrentFragment()) {\r
+            mWebview.loadUrl("javascript:alert(rewindCurrentIndex())");\r
+        }\r
+    }\r
+\r
+    @Override\r
+    public void onDestroy() {\r
+        super.onDestroy();\r
+        if (mWebview != null) mWebview.destroy();\r
+    }\r
+\r
+    private boolean isCurrentFragment() {\r
+        return isAdded() && ((FolioActivity) getActivity()).getmChapterPosition() == mPos;\r
+    }\r
+\r
+    public void setFragmentPos(int pos) {\r
+        mPos = pos;\r
+    }\r
+\r
+    @Override\r
+    public void onError() {\r
+    }\r
+\r
+    private void scrollToHighlightId() {\r
+        mWebview.loadUrl(String.format(getString(R.string.goto_highlight), highlightId));\r
+    }\r
+}\r