Added Android code
[wl-app.git] / Android / folioreader / src / main / java / com / folioreader / ui / folio / fragment / FolioPageFragment.java
1 package com.folioreader.ui.folio.fragment;\r
2 \r
3 import android.app.Activity;\r
4 import android.annotation.SuppressLint;\r
5 import android.content.Intent;\r
6 import android.content.res.Configuration;\r
7 import android.graphics.Color;\r
8 import android.graphics.PorterDuff;\r
9 import android.graphics.drawable.Drawable;\r
10 import android.net.Uri;\r
11 import android.os.Build;\r
12 import android.os.Bundle;\r
13 import android.support.v4.app.Fragment;\r
14 import android.support.v4.content.ContextCompat;\r
15 import android.text.TextUtils;\r
16 import android.util.Log;\r
17 import android.view.LayoutInflater;\r
18 import android.view.View;\r
19 import android.view.ViewGroup;\r
20 import android.view.animation.Animation;\r
21 import android.view.animation.AnimationUtils;\r
22 import android.webkit.JavascriptInterface;\r
23 import android.webkit.JsResult;\r
24 import android.webkit.WebChromeClient;\r
25 import android.webkit.WebResourceRequest;\r
26 import android.webkit.WebResourceResponse;\r
27 import android.webkit.WebView;\r
28 import android.webkit.WebViewClient;\r
29 import android.widget.TextView;\r
30 import android.widget.Toast;\r
31 \r
32 import com.bossturban.webviewmarker.TextSelectionSupport;\r
33 import com.folioreader.Config;\r
34 import com.folioreader.Constants;\r
35 import com.folioreader.R;\r
36 import com.folioreader.model.HighLight;\r
37 import com.folioreader.model.HighlightImpl;\r
38 import com.folioreader.model.event.AnchorIdEvent;\r
39 import com.folioreader.model.event.BusOwner;\r
40 import com.folioreader.model.event.MediaOverlayHighlightStyleEvent;\r
41 import com.folioreader.model.event.MediaOverlayPlayPauseEvent;\r
42 import com.folioreader.model.event.MediaOverlaySpeedEvent;\r
43 import com.folioreader.model.event.ReloadDataEvent;\r
44 import com.folioreader.model.event.RewindIndexEvent;\r
45 import com.folioreader.model.event.WebViewPosition;\r
46 import com.folioreader.model.quickaction.ActionItem;\r
47 import com.folioreader.model.quickaction.QuickAction;\r
48 import com.folioreader.model.sqlite.HighLightTable;\r
49 import com.folioreader.ui.base.HtmlTask;\r
50 import com.folioreader.ui.base.HtmlTaskCallback;\r
51 import com.folioreader.ui.base.HtmlUtil;\r
52 import com.folioreader.ui.folio.activity.FolioActivity;\r
53 import com.folioreader.ui.folio.mediaoverlay.MediaController;\r
54 import com.folioreader.ui.folio.mediaoverlay.MediaControllerCallbacks;\r
55 import com.folioreader.util.AppUtil;\r
56 import com.folioreader.util.FolioReader;\r
57 import com.folioreader.util.HighlightUtil;\r
58 import com.folioreader.util.SMILParser;\r
59 import com.folioreader.util.UiUtil;\r
60 import com.folioreader.view.ObservableWebView;\r
61 import com.folioreader.view.VerticalSeekbar;\r
62 import com.squareup.otto.Bus;\r
63 import com.squareup.otto.Subscribe;\r
64 \r
65 import org.readium.r2_streamer.model.publication.link.Link;\r
66 \r
67 import java.io.UnsupportedEncodingException;\r
68 import java.net.URLDecoder;\r
69 import java.util.Locale;\r
70 import java.util.regex.Matcher;\r
71 import java.util.regex.Pattern;\r
72 \r
73 /**\r
74  * Created by mahavir on 4/2/16.\r
75  */\r
76 @SuppressWarnings("PMD.AvoidDuplicateLiterals")\r
77 public class FolioPageFragment extends Fragment implements HtmlTaskCallback, MediaControllerCallbacks, ObservableWebView.SeekBarListener {\r
78 \r
79     public static final String KEY_FRAGMENT_FOLIO_POSITION = "com.folioreader.ui.folio.fragment.FolioPageFragment.POSITION";\r
80     public static final String KEY_FRAGMENT_FOLIO_BOOK_TITLE = "com.folioreader.ui.folio.fragment.FolioPageFragment.BOOK_TITLE";\r
81     public static final String KEY_FRAGMENT_EPUB_FILE_NAME = "com.folioreader.ui.folio.fragment.FolioPageFragment.EPUB_FILE_NAME";\r
82     private static final String KEY_IS_SMIL_AVAILABLE = "com.folioreader.ui.folio.fragment.FolioPageFragment.IS_SMIL_AVAILABLE";\r
83     public static final String TAG = FolioPageFragment.class.getSimpleName();\r
84 \r
85     private static final int ACTION_ID_COPY = 1001;\r
86     private static final int ACTION_ID_SHARE = 1002;\r
87     private static final int ACTION_ID_HIGHLIGHT = 1003;\r
88     private static final int ACTION_ID_DEFINE = 1004;\r
89 \r
90     private static final int ACTION_ID_HIGHLIGHT_COLOR = 1005;\r
91     private static final int ACTION_ID_DELETE = 1006;\r
92 \r
93     private static final int ACTION_ID_HIGHLIGHT_YELLOW = 1007;\r
94     private static final int ACTION_ID_HIGHLIGHT_GREEN = 1008;\r
95     private static final int ACTION_ID_HIGHLIGHT_BLUE = 1009;\r
96     private static final int ACTION_ID_HIGHLIGHT_PINK = 1010;\r
97     private static final int ACTION_ID_HIGHLIGHT_UNDERLINE = 1011;\r
98     private static final String KEY_TEXT_ELEMENTS = "text_elements";\r
99     private static final String SPINE_ITEM = "spine_item";\r
100 \r
101     private String mHtmlString = null;\r
102     private boolean hasMediaOverlay = false;\r
103     private String mAnchorId;\r
104     private String rangy = "";\r
105     private String highlightId;\r
106 \r
107     public interface FolioPageFragmentCallback {\r
108 \r
109         void setPagerToPosition(String href);\r
110 \r
111         void setLastWebViewPosition(int position);\r
112 \r
113         void goToChapter(String href);\r
114     }\r
115 \r
116     private View mRootView;\r
117 \r
118     private VerticalSeekbar mScrollSeekbar;\r
119     private ObservableWebView mWebview;\r
120     private TextSelectionSupport mTextSelectionSupport;\r
121     private TextView mPagesLeftTextView, mMinutesLeftTextView;\r
122     private FolioPageFragmentCallback mActivityCallback;\r
123 \r
124     private int mScrollY;\r
125     private int mTotalMinutes;\r
126     private String mSelectedText;\r
127     private Animation mFadeInAnimation, mFadeOutAnimation;\r
128 \r
129     private Link spineItem;\r
130     private int mPosition = -1;\r
131     private String mBookTitle;\r
132     private String mEpubFileName = null;\r
133     private int mPos;\r
134     private boolean mIsPageReloaded;\r
135     private int mLastWebviewScrollpos;\r
136 \r
137     private String highlightStyle;\r
138 \r
139     private MediaController mediaController;\r
140     private Config mConfig;\r
141     private String mBookId;\r
142 \r
143     public static FolioPageFragment newInstance(int position, String bookTitle, Link spineRef, String bookId) {\r
144         FolioPageFragment fragment = new FolioPageFragment();\r
145         Bundle args = new Bundle();\r
146         args.putInt(KEY_FRAGMENT_FOLIO_POSITION, position);\r
147         args.putString(KEY_FRAGMENT_FOLIO_BOOK_TITLE, bookTitle);\r
148         args.putString(FolioReader.INTENT_BOOK_ID, bookId);\r
149         args.putSerializable(SPINE_ITEM, spineRef);\r
150         fragment.setArguments(args);\r
151         return fragment;\r
152     }\r
153 \r
154     @Override\r
155     public View onCreateView(LayoutInflater inflater,\r
156                              ViewGroup container, Bundle savedInstanceState) {\r
157         if ((savedInstanceState != null)\r
158                 && savedInstanceState.containsKey(KEY_FRAGMENT_FOLIO_POSITION)\r
159                 && savedInstanceState.containsKey(KEY_FRAGMENT_FOLIO_BOOK_TITLE)) {\r
160             mPosition = savedInstanceState.getInt(KEY_FRAGMENT_FOLIO_POSITION);\r
161             mBookTitle = savedInstanceState.getString(KEY_FRAGMENT_FOLIO_BOOK_TITLE);\r
162             mEpubFileName = savedInstanceState.getString(KEY_FRAGMENT_EPUB_FILE_NAME);\r
163             mBookId = getArguments().getString(FolioReader.INTENT_BOOK_ID);\r
164             spineItem = (Link) savedInstanceState.getSerializable(SPINE_ITEM);\r
165         } else {\r
166             mPosition = getArguments().getInt(KEY_FRAGMENT_FOLIO_POSITION);\r
167             mBookTitle = getArguments().getString(KEY_FRAGMENT_FOLIO_BOOK_TITLE);\r
168             mEpubFileName = getArguments().getString(KEY_FRAGMENT_EPUB_FILE_NAME);\r
169             spineItem = (Link) getArguments().getSerializable(SPINE_ITEM);\r
170             mBookId = getArguments().getString(FolioReader.INTENT_BOOK_ID);\r
171         }\r
172         if (spineItem != null) {\r
173             if (spineItem.properties.contains("media-overlay")) {\r
174                 mediaController = new MediaController(getActivity(), MediaController.MediaType.SMIL, this);\r
175                 hasMediaOverlay = true;\r
176             } else {\r
177                 mediaController = new MediaController(getActivity(), MediaController.MediaType.TTS, this);\r
178                 mediaController.setTextToSpeech(getActivity());\r
179             }\r
180         }\r
181         highlightStyle = HighlightImpl.HighlightStyle.classForStyle(HighlightImpl.HighlightStyle.Normal);\r
182         mRootView = View.inflate(getActivity(), R.layout.folio_page_fragment, null);\r
183         mPagesLeftTextView = (TextView) mRootView.findViewById(R.id.pagesLeft);\r
184         mMinutesLeftTextView = (TextView) mRootView.findViewById(R.id.minutesLeft);\r
185 \r
186         Activity activity = getActivity();\r
187 \r
188         mConfig = AppUtil.getSavedConfig(activity);\r
189 \r
190         if (activity instanceof FolioPageFragmentCallback)\r
191             mActivityCallback = (FolioPageFragmentCallback) activity;\r
192 \r
193         if (activity instanceof BusOwner)\r
194             ((BusOwner) activity).getBus().register(this);\r
195 \r
196         initSeekbar();\r
197         initAnimations();\r
198         initWebView();\r
199         updatePagesLeftTextBg();\r
200 \r
201         return mRootView;\r
202     }\r
203 \r
204 \r
205     private String getWebviewUrl() {\r
206         return Constants.LOCALHOST + mBookTitle + "/" + spineItem.href;\r
207     }\r
208 \r
209     @Override\r
210     public void onConfigurationChanged(Configuration newConfig) {\r
211         super.onConfigurationChanged(newConfig);\r
212         float positionTopView = mWebview.getTop();\r
213         float contentHeight = mWebview.getContentHeight();\r
214         float currentScrollPosition = mScrollY;\r
215         float percentWebview = (currentScrollPosition - positionTopView) / contentHeight;\r
216         float webviewsize = mWebview.getContentHeight() - mWebview.getTop();\r
217         float positionInWV = webviewsize * percentWebview;\r
218         int positionY = Math.round(mWebview.getTop() + positionInWV);\r
219         mScrollY = positionY;\r
220     }\r
221 \r
222     /**\r
223      * [EVENT BUS FUNCTION]\r
224      * Function triggered from {@link FolioActivity#initAudioView()} when pause/play\r
225      * button is clicked\r
226      *\r
227      * @param event of type {@link MediaOverlayPlayPauseEvent} contains if paused/played\r
228      */\r
229     @SuppressWarnings("unused")\r
230     @Subscribe\r
231     public void pauseButtonClicked(MediaOverlayPlayPauseEvent event) {\r
232         if (isAdded()\r
233                 && spineItem.href.equals(event.getHref())) {\r
234             mediaController.stateChanged(event);\r
235         }\r
236     }\r
237 \r
238     /**\r
239      * [EVENT BUS FUNCTION]\r
240      * Function triggered from {@link FolioActivity#initAudioView()} when speed\r
241      * change buttons are clicked\r
242      *\r
243      * @param event of type {@link MediaOverlaySpeedEvent} contains selected speed\r
244      *              type HALF,ONE,ONE_HALF and TWO.\r
245      */\r
246     @SuppressWarnings("unused")\r
247     @Subscribe\r
248     public void speedChanged(MediaOverlaySpeedEvent event) {\r
249         mediaController.setSpeed(event.getSpeed());\r
250     }\r
251 \r
252     /**\r
253      * [EVENT BUS FUNCTION]\r
254      * Function triggered from {@link FolioActivity#initAudioView()} when new\r
255      * style is selected on button click.\r
256      *\r
257      * @param event of type {@link MediaOverlaySpeedEvent} contains selected style\r
258      *              of type DEFAULT,UNDERLINE and BACKGROUND.\r
259      */\r
260     @SuppressWarnings("unused")\r
261     @Subscribe\r
262     public void styleChanged(MediaOverlayHighlightStyleEvent event) {\r
263         if (isAdded()) {\r
264             switch (event.getStyle()) {\r
265                 case DEFAULT:\r
266                     highlightStyle =\r
267                             HighlightImpl.HighlightStyle.classForStyle(HighlightImpl.HighlightStyle.Normal);\r
268                     break;\r
269                 case UNDERLINE:\r
270                     highlightStyle =\r
271                             HighlightImpl.HighlightStyle.classForStyle(HighlightImpl.HighlightStyle.DottetUnderline);\r
272                     break;\r
273                 case BACKGROUND:\r
274                     highlightStyle =\r
275                             HighlightImpl.HighlightStyle.classForStyle(HighlightImpl.HighlightStyle.TextColor);\r
276                     break;\r
277             }\r
278             mWebview.loadUrl(String.format(getString(R.string.setmediaoverlaystyle), highlightStyle));\r
279         }\r
280     }\r
281 \r
282     /**\r
283      * [EVENT BUS FUNCTION]\r
284      * Function triggered when any EBook configuration is changed.\r
285      *\r
286      * @param reloadDataEvent empty POJO.\r
287      */\r
288     @Subscribe\r
289     public void reload(ReloadDataEvent reloadDataEvent) {\r
290         if (isAdded()) {\r
291             mLastWebviewScrollpos = mWebview.getScrollY();\r
292             mIsPageReloaded = true;\r
293             setHtml(true);\r
294             updatePagesLeftTextBg();\r
295         }\r
296     }\r
297 \r
298     /**\r
299      * [EVENT BUS FUNCTION]\r
300      * Function triggered from {@link FolioActivity#onActivityResult(int, int, Intent)} when any item in toc clicked.\r
301      *\r
302      * @param event of type {@link AnchorIdEvent} contains selected chapter href.\r
303      */\r
304     @Subscribe\r
305     public void jumpToAnchorPoint(AnchorIdEvent event) {\r
306         if (isAdded() && event != null && event.getHref() != null) {\r
307             String href = event.getHref();\r
308             if (href != null && href.indexOf('#') != -1 && spineItem.href.equals(href.substring(0, href.lastIndexOf('#')))) {\r
309                 mAnchorId = href.substring(href.lastIndexOf('#') + 1);\r
310                 if (mWebview.getContentHeight() > 0 && mAnchorId != null) {\r
311                     mWebview.loadUrl("javascript:document.getElementById(\"" + mAnchorId + "\").scrollIntoView()");\r
312                 }\r
313             }\r
314         }\r
315     }\r
316 \r
317     @Override\r
318     public void onReceiveHtml(String html) {\r
319         if (isAdded()) {\r
320             mHtmlString = html;\r
321             setHtml(false);\r
322         }\r
323     }\r
324 \r
325     private void setHtml(boolean reloaded) {\r
326         if (spineItem != null) {\r
327             String ref = spineItem.href;\r
328             if (!reloaded && spineItem.properties.contains("media-overlay")) {\r
329                 mediaController.setSMILItems(SMILParser.parseSMIL(mHtmlString));\r
330                 mediaController.setUpMediaPlayer(spineItem.mediaOverlay, spineItem.mediaOverlay.getAudioPath(spineItem.href), mBookTitle);\r
331             }\r
332             mConfig = AppUtil.getSavedConfig(getActivity());\r
333             String path = ref.substring(0, ref.lastIndexOf('/'));\r
334             mWebview.loadDataWithBaseURL(\r
335                     Constants.LOCALHOST + mBookTitle + "/" + path + "/",\r
336                     HtmlUtil.getHtmlContent(getActivity(), mHtmlString, mConfig),\r
337                     "text/html",\r
338                     "UTF-8",\r
339                     null);\r
340         }\r
341     }\r
342 \r
343     @Override\r
344     public void onStop() {\r
345         super.onStop();\r
346         mediaController.stop();\r
347         //TODO save last media overlay item\r
348     }\r
349 \r
350     private void initWebView() {\r
351         mWebview = (ObservableWebView) mRootView.findViewById(R.id.contentWebView);\r
352         mWebview.setSeekBarListener(FolioPageFragment.this);\r
353 \r
354         if (getActivity() instanceof ObservableWebView.ToolBarListener)\r
355             mWebview.setToolBarListener((ObservableWebView.ToolBarListener) getActivity());\r
356 \r
357         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\r
358             WebView.setWebContentsDebuggingEnabled(true);\r
359         }\r
360 \r
361         setupScrollBar();\r
362         mWebview.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {\r
363             @Override\r
364             public void onLayoutChange(View view, int left, int top, int right, int bottom,\r
365                                        int oldLeft, int oldTop, int oldRight, int oldBottom) {\r
366                 int height =\r
367                         (int) Math.floor(mWebview.getContentHeight() * mWebview.getScale());\r
368                 int webViewHeight = mWebview.getMeasuredHeight();\r
369                 mScrollSeekbar.setMaximum(height - webViewHeight);\r
370             }\r
371         });\r
372 \r
373         mWebview.getSettings().setJavaScriptEnabled(true);\r
374         mWebview.setVerticalScrollBarEnabled(false);\r
375         mWebview.getSettings().setAllowFileAccess(true);\r
376 \r
377         mWebview.setHorizontalScrollBarEnabled(false);\r
378 \r
379         mWebview.addJavascriptInterface(this, "Highlight");\r
380         mWebview.setScrollListener(new ObservableWebView.ScrollListener() {\r
381             @Override\r
382             public void onScrollChange(int percent) {\r
383                 if (mWebview.getScrollY() != 0) {\r
384                     mScrollY = mWebview.getScrollY();\r
385                     if (isAdded()) {\r
386                         ((FolioActivity) getActivity()).setLastWebViewPosition(mScrollY);\r
387                     }\r
388                 }\r
389                 mScrollSeekbar.setProgressAndThumb(percent);\r
390                 updatePagesLeftText(percent);\r
391 \r
392             }\r
393         });\r
394 \r
395         mWebview.setWebViewClient(new WebViewClient() {\r
396             @Override\r
397             public void onPageFinished(WebView view, String url) {\r
398                 if (isAdded()) {\r
399                     if (mAnchorId != null)\r
400                         view.loadUrl("javascript:document.getElementById(\"" + mAnchorId + "\").scrollIntoView()");\r
401                     view.loadUrl("javascript:alert(getReadingTime())");\r
402                     if (!hasMediaOverlay) {\r
403                         view.loadUrl("javascript:alert(wrappingSentencesWithinPTags())");\r
404                     }\r
405                     view.loadUrl(String.format(getString(R.string.setmediaoverlaystyle),\r
406                             HighlightImpl.HighlightStyle.classForStyle(\r
407                                     HighlightImpl.HighlightStyle.Normal)));\r
408                     if (isCurrentFragment()) {\r
409                         setWebViewPosition(AppUtil.getPreviousBookStateWebViewPosition(getActivity(), mBookTitle));\r
410                     } else if (mIsPageReloaded) {\r
411                         setWebViewPosition(mLastWebviewScrollpos);\r
412                         mIsPageReloaded = false;\r
413                     }\r
414                     String rangy = HighlightUtil.generateRangyString(getPageName());\r
415                     FolioPageFragment.this.rangy = rangy;\r
416                     if (!rangy.isEmpty()) {\r
417                         loadRangy(view, rangy);\r
418                     }\r
419 \r
420                     scrollToHighlightId();\r
421 \r
422 \r
423                 }\r
424             }\r
425 \r
426             @Override\r
427             public boolean shouldOverrideUrlLoading(WebView view, String url) {\r
428                 if (!url.isEmpty() && url.length() > 0) {\r
429                     if (Uri.parse(url).getScheme().startsWith("highlight")) {\r
430                         final Pattern pattern = Pattern.compile(getString(R.string.pattern));\r
431                         try {\r
432                             String htmlDecode = URLDecoder.decode(url, "UTF-8");\r
433                             Matcher matcher = pattern.matcher(htmlDecode.substring(12));\r
434                             if (matcher.matches()) {\r
435                                 double left = Double.parseDouble(matcher.group(1));\r
436                                 double top = Double.parseDouble(matcher.group(2));\r
437                                 double width = Double.parseDouble(matcher.group(3));\r
438                                 double height = Double.parseDouble(matcher.group(4));\r
439                                 onHighlight((int) (UiUtil.convertDpToPixel((float) left,\r
440                                         getActivity())),\r
441                                         (int) (UiUtil.convertDpToPixel((float) top,\r
442                                                 getActivity())),\r
443                                         (int) (UiUtil.convertDpToPixel((float) width,\r
444                                                 getActivity())),\r
445                                         (int) (UiUtil.convertDpToPixel((float) height,\r
446                                                 getActivity())));\r
447                             }\r
448                         } catch (UnsupportedEncodingException e) {\r
449                             Log.d(TAG, e.getMessage());\r
450                         }\r
451                     } else {\r
452                         if (url.contains("storage")) {\r
453                             mActivityCallback.setPagerToPosition(url);\r
454                         } else if (url.endsWith(".xhtml") || url.endsWith(".html") || url.contains(".xhtml#") || url.contains(".html#")) {\r
455                             mActivityCallback.goToChapter(url);\r
456                         } else {\r
457                             // Otherwise, give the default behavior (open in browser)\r
458                             Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));\r
459                             startActivity(intent);\r
460                         }\r
461                     }\r
462                 }\r
463                 return true;\r
464             }\r
465 \r
466 \r
467             // prevent favicon.ico to be loaded automatically\r
468             @Override\r
469             public WebResourceResponse shouldInterceptRequest(WebView view, String url) {\r
470                 if(url.toLowerCase().contains("/favicon.ico")) {\r
471                     try {\r
472                         return new WebResourceResponse("image/png", null, null);\r
473                     } catch (Exception e) {\r
474                         Log.e(TAG, "shouldInterceptRequest failed", e);\r
475                     }\r
476                 }\r
477                 return null;\r
478             }\r
479 \r
480             // prevent favicon.ico to be loaded automatically\r
481             @Override\r
482             @SuppressLint("NewApi")\r
483             public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {\r
484                 if(!request.isForMainFrame() && request.getUrl().getPath().endsWith("/favicon.ico")) {\r
485                     try {\r
486                         return new WebResourceResponse("image/png", null, null);\r
487                     } catch (Exception e) {\r
488                         Log.e(TAG, "shouldInterceptRequest failed", e);\r
489                     }\r
490                 }\r
491                 return null;\r
492             }\r
493         });\r
494 \r
495         mWebview.setWebChromeClient(new WebChromeClient() {\r
496             @Override\r
497             public void onProgressChanged(WebView view, int progress) {\r
498 \r
499                 if (view.getProgress() == 100) {\r
500                     mWebview.postDelayed(new Runnable() {\r
501                         @Override\r
502                         public void run() {\r
503                             Log.d("scroll y", "Scrolly" + mScrollY);\r
504                             mWebview.scrollTo(0, mScrollY);\r
505                         }\r
506                     }, 100);\r
507                 }\r
508             }\r
509 \r
510             @Override\r
511             public boolean onJsAlert(WebView view, String url, String message, JsResult result) {\r
512                 if (FolioPageFragment.this.isVisible()) {\r
513                     String rangyPattern = "\\d+\\$\\d+\\$\\d+\\$\\w+\\$";\r
514                     Pattern pattern = Pattern.compile(rangyPattern);\r
515                     Matcher matcher = pattern.matcher(message);\r
516                     if (matcher.matches()) {\r
517                         HighlightImpl highlightImpl = HighLightTable.getHighlightForRangy(message);\r
518                         if (HighLightTable.deleteHighlight(message)) {\r
519                             String rangy = HighlightUtil.generateRangyString(getPageName());\r
520                             loadRangy(view, rangy);\r
521                            // mTextSelectionSupport.endSelectionMode();\r
522                             if (highlightImpl != null) {\r
523                                 HighlightUtil.sendHighlightBroadcastEvent(\r
524                                         FolioPageFragment.this.getActivity().getApplicationContext(),\r
525                                         highlightImpl,\r
526                                         HighLight.HighLightAction.DELETE);\r
527                             }\r
528                         }\r
529                     } else if (TextUtils.isDigitsOnly(message)) {\r
530                         mTotalMinutes = Integer.parseInt(message);\r
531                     } else {\r
532                         pattern = Pattern.compile(getString(R.string.pattern));\r
533                         matcher = pattern.matcher(message);\r
534                         if (matcher.matches()) {\r
535                             double left = Double.parseDouble(matcher.group(1));\r
536                             double top = Double.parseDouble(matcher.group(2));\r
537                             double width = Double.parseDouble(matcher.group(3));\r
538                             double height = Double.parseDouble(matcher.group(4));\r
539                             showTextSelectionMenu((int) (UiUtil.convertDpToPixel((float) left,\r
540                                     getActivity())),\r
541                                     (int) (UiUtil.convertDpToPixel((float) top,\r
542                                             getActivity())),\r
543                                     (int) (UiUtil.convertDpToPixel((float) width,\r
544                                             getActivity())),\r
545                                     (int) (UiUtil.convertDpToPixel((float) height,\r
546                                             getActivity())));\r
547                         } else {\r
548                             // to handle TTS playback when highlight is deleted.\r
549                             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
550                             if (!p.matcher(message).matches() && (!message.equals("undefined")) && isCurrentFragment()) {\r
551                                 mediaController.speakAudio(message);\r
552                             }\r
553                         }\r
554                     }\r
555                     result.confirm();\r
556                 }\r
557                 return true;\r
558             }\r
559         });\r
560 \r
561 //        mTextSelectionSupport = TextSelectionSupport.support(getActivity(), mWebview);\r
562 //        mTextSelectionSupport.setSelectionListener(new TextSelectionSupport.SelectionListener() {\r
563 //            @Override\r
564 //            public void startSelection() {\r
565 //            }\r
566 //\r
567 //            @Override\r
568 //            public void selectionChanged(String text) {\r
569 //                mSelectedText = text;\r
570 //                getActivity().runOnUiThread(new Runnable() {\r
571 //                    @Override\r
572 //                    public void run() {\r
573 //                        mWebview.loadUrl("javascript:alert(getRectForSelectedText())");\r
574 //                    }\r
575 //                });\r
576 //            }\r
577 //\r
578 //            @Override\r
579 //            public void endSelection() {\r
580 //\r
581 //            }\r
582 //        });\r
583 \r
584         mWebview.getSettings().setDefaultTextEncodingName("utf-8");\r
585         mWebview.setOnLongClickListener(new View.OnLongClickListener() {\r
586             @Override\r
587             public boolean onLongClick(View v) {\r
588                 return true;\r
589             }\r
590         });\r
591         mWebview.setLongClickable(false);\r
592         mWebview.setHapticFeedbackEnabled(false);\r
593         new HtmlTask(this).execute(getWebviewUrl());\r
594     }\r
595 \r
596     private void loadRangy(WebView view, String rangy) {\r
597         view.loadUrl(String.format("javascript:if(typeof ssReader !== \"undefined\"){ssReader.setHighlights('%s');}", rangy));\r
598     }\r
599 \r
600     private void setupScrollBar() {\r
601         UiUtil.setColorToImage(getActivity(), mConfig.getThemeColor(), mScrollSeekbar.getProgressDrawable());\r
602         Drawable thumbDrawable = ContextCompat.getDrawable(getActivity(), R.drawable.icons_sroll);\r
603         UiUtil.setColorToImage(getActivity(), mConfig.getThemeColor(), (thumbDrawable));\r
604         mScrollSeekbar.setThumb(thumbDrawable);\r
605     }\r
606 \r
607     private void initSeekbar() {\r
608         mScrollSeekbar = (VerticalSeekbar) mRootView.findViewById(R.id.scrollSeekbar);\r
609         mScrollSeekbar.getProgressDrawable()\r
610                 .setColorFilter(getResources()\r
611                                 .getColor(R.color.app_green),\r
612                         PorterDuff.Mode.SRC_IN);\r
613     }\r
614 \r
615     private void updatePagesLeftTextBg() {\r
616 \r
617         if (mConfig.isNightMode()) {\r
618             mRootView.findViewById(R.id.indicatorLayout)\r
619                     .setBackgroundColor(getResources().getColor(R.color.dark_night));\r
620         } else {\r
621             mRootView.findViewById(R.id.indicatorLayout)\r
622                     .setBackgroundColor(Color.WHITE);\r
623         }\r
624     }\r
625 \r
626     private void updatePagesLeftText(int scrollY) {\r
627         try {\r
628             int currentPage = (int) (Math.ceil((double) scrollY / mWebview.getWebViewHeight()) + 1);\r
629             int totalPages =\r
630                     (int) Math.ceil((double) mWebview.getContentHeightVal()\r
631                             / mWebview.getWebViewHeight());\r
632             int pagesRemaining = totalPages - currentPage;\r
633             String pagesRemainingStrFormat =\r
634                     pagesRemaining > 1 ?\r
635                             getString(R.string.pages_left) : getString(R.string.page_left);\r
636             String pagesRemainingStr = String.format(Locale.US,\r
637                     pagesRemainingStrFormat, pagesRemaining);\r
638 \r
639             int minutesRemaining =\r
640                     (int) Math.ceil((double) (pagesRemaining * mTotalMinutes) / totalPages);\r
641             String minutesRemainingStr;\r
642             if (minutesRemaining > 1) {\r
643                 minutesRemainingStr =\r
644                         String.format(Locale.US, getString(R.string.minutes_left),\r
645                                 minutesRemaining);\r
646             } else if (minutesRemaining == 1) {\r
647                 minutesRemainingStr =\r
648                         String.format(Locale.US, getString(R.string.minute_left),\r
649                                 minutesRemaining);\r
650             } else {\r
651                 minutesRemainingStr = getString(R.string.less_than_minute);\r
652             }\r
653 \r
654             mMinutesLeftTextView.setText(minutesRemainingStr);\r
655             mPagesLeftTextView.setText(pagesRemainingStr);\r
656         } catch (java.lang.ArithmeticException exp) {\r
657             Log.d("divide error", exp.toString());\r
658         }\r
659     }\r
660 \r
661     private void initAnimations() {\r
662         mFadeInAnimation = AnimationUtils.loadAnimation(getActivity(), R.anim.fadein);\r
663         mFadeInAnimation.setAnimationListener(new Animation.AnimationListener() {\r
664             @Override\r
665             public void onAnimationStart(Animation animation) {\r
666                 mScrollSeekbar.setVisibility(View.VISIBLE);\r
667             }\r
668 \r
669             @Override\r
670             public void onAnimationEnd(Animation animation) {\r
671                 fadeOutSeekBarIfVisible();\r
672             }\r
673 \r
674             @Override\r
675             public void onAnimationRepeat(Animation animation) {\r
676 \r
677             }\r
678         });\r
679         mFadeOutAnimation = AnimationUtils.loadAnimation(getActivity(), R.anim.fadeout);\r
680         mFadeOutAnimation.setAnimationListener(new Animation.AnimationListener() {\r
681             @Override\r
682             public void onAnimationStart(Animation animation) {\r
683 \r
684             }\r
685 \r
686             @Override\r
687             public void onAnimationEnd(Animation animation) {\r
688                 mScrollSeekbar.setVisibility(View.INVISIBLE);\r
689             }\r
690 \r
691             @Override\r
692             public void onAnimationRepeat(Animation animation) {\r
693 \r
694             }\r
695         });\r
696     }\r
697 \r
698     public void fadeInSeekBarIfInvisible() {\r
699         if (mScrollSeekbar.getVisibility() == View.INVISIBLE ||\r
700                 mScrollSeekbar.getVisibility() == View.GONE) {\r
701             mScrollSeekbar.startAnimation(mFadeInAnimation);\r
702         }\r
703     }\r
704 \r
705     public void fadeOutSeekBarIfVisible() {\r
706         if (mScrollSeekbar.getVisibility() == View.VISIBLE) {\r
707             mScrollSeekbar.startAnimation(mFadeOutAnimation);\r
708         }\r
709     }\r
710 \r
711     @Override\r
712     public void onDestroyView() {\r
713         mFadeInAnimation.setAnimationListener(null);\r
714         mFadeOutAnimation.setAnimationListener(null);\r
715 \r
716         Activity activity = getActivity();\r
717         if (activity instanceof BusOwner)\r
718             ((BusOwner) activity).getBus().unregister(this);\r
719         super.onDestroyView();\r
720     }\r
721 \r
722     @Override\r
723     public void onSaveInstanceState(Bundle outState) {\r
724         super.onSaveInstanceState(outState);\r
725         outState.putInt(KEY_FRAGMENT_FOLIO_POSITION, mPosition);\r
726         outState.putString(KEY_FRAGMENT_FOLIO_BOOK_TITLE, mBookTitle);\r
727         outState.putString(KEY_FRAGMENT_EPUB_FILE_NAME, mEpubFileName);\r
728         outState.putSerializable(SPINE_ITEM, spineItem);\r
729     }\r
730 \r
731     public void highlight(HighlightImpl.HighlightStyle style, boolean isCreated) {\r
732         if (isCreated) {\r
733             mWebview.loadUrl(String.format("javascript:if(typeof ssReader !== \"undefined\"){ssReader.highlightSelection('%s');}", HighlightImpl.HighlightStyle.classForStyle(style)));\r
734         } else {\r
735             mWebview.loadUrl(String.format("javascript:alert(setHighlightStyle('%s'))", "highlight_" + HighlightImpl.HighlightStyle.classForStyle(style)));\r
736         }\r
737     }\r
738 \r
739     public void highlightRemove() {\r
740         mWebview.loadUrl("javascript:alert(removeThisHighlight())");\r
741     }\r
742 \r
743     public void showTextSelectionMenu(int x, int y, final int width, final int height) {\r
744         final ViewGroup root =\r
745                 (ViewGroup) getActivity().getWindow()\r
746                         .getDecorView().findViewById(android.R.id.content);\r
747         final View view = new View(getActivity());\r
748         view.setLayoutParams(new ViewGroup.LayoutParams(width, height));\r
749         view.setBackgroundColor(Color.TRANSPARENT);\r
750 \r
751         root.addView(view);\r
752 \r
753         view.setX(x);\r
754         view.setY(y);\r
755         final QuickAction quickAction =\r
756                 new QuickAction(getActivity(), QuickAction.HORIZONTAL);\r
757         quickAction.addActionItem(new ActionItem(ACTION_ID_COPY,\r
758                 getString(R.string.copy)));\r
759         quickAction.addActionItem(new ActionItem(ACTION_ID_HIGHLIGHT,\r
760                 getString(R.string.highlight)));\r
761         if (!mSelectedText.trim().contains(" ")) {\r
762             quickAction.addActionItem(new ActionItem(ACTION_ID_DEFINE,\r
763                     getString(R.string.define)));\r
764         }\r
765         quickAction.addActionItem(new ActionItem(ACTION_ID_SHARE,\r
766                 getString(R.string.share)));\r
767         quickAction.setOnActionItemClickListener(new QuickAction.OnActionItemClickListener() {\r
768             @Override\r
769             public void onItemClick(QuickAction source, int pos, int actionId) {\r
770                 quickAction.dismiss();\r
771                 root.removeView(view);\r
772                 onTextSelectionActionItemClicked(actionId, view, width, height);\r
773             }\r
774         });\r
775         quickAction.show(view, width, height);\r
776     }\r
777 \r
778     private void onTextSelectionActionItemClicked(int actionId, View view, int width, int height) {\r
779         if (actionId == ACTION_ID_COPY) {\r
780             UiUtil.copyToClipboard(getActivity(), mSelectedText);\r
781             Toast.makeText(getActivity(), getString(R.string.copied), Toast.LENGTH_SHORT).show();\r
782             mTextSelectionSupport.endSelectionMode();\r
783         } else if (actionId == ACTION_ID_SHARE) {\r
784             UiUtil.share(getActivity(), mSelectedText);\r
785         } else if (actionId == ACTION_ID_DEFINE) {\r
786             showDictDialog(mSelectedText);\r
787             mTextSelectionSupport.endSelectionMode();\r
788         } else if (actionId == ACTION_ID_HIGHLIGHT) {\r
789             onHighlight(view, width, height, true);\r
790         }\r
791     }\r
792 \r
793     private void showDictDialog(String mSelectedText) {\r
794         DictionaryFragment dictionaryFragment = new DictionaryFragment();\r
795         Bundle b = new Bundle();\r
796         b.putString(Constants.SELECTED_WORD, mSelectedText);\r
797         dictionaryFragment.setArguments(b);\r
798         dictionaryFragment.show(getFragmentManager(), DictionaryFragment.class.getName());\r
799     }\r
800 \r
801     private void onHighlight(int x, int y, int width, int height) {\r
802         final View view = new View(getActivity());\r
803         view.setLayoutParams(new ViewGroup.LayoutParams(width, height));\r
804         view.setBackgroundColor(Color.TRANSPARENT);\r
805         view.setX(x);\r
806         view.setY(y);\r
807         onHighlight(view, width, height, false);\r
808     }\r
809 \r
810     private void onHighlight(final View view, int width, int height, final boolean isCreated) {\r
811         ViewGroup root =\r
812                 (ViewGroup) getActivity().getWindow().\r
813                         getDecorView().findViewById(android.R.id.content);\r
814         ViewGroup parent = (ViewGroup) view.getParent();\r
815         if (parent == null) {\r
816             root.addView(view);\r
817         } else {\r
818             final int index = parent.indexOfChild(view);\r
819             parent.removeView(view);\r
820             parent.addView(view, index);\r
821         }\r
822 \r
823         final QuickAction quickAction = new QuickAction(getActivity(), QuickAction.HORIZONTAL);\r
824         quickAction.addActionItem(new ActionItem(ACTION_ID_HIGHLIGHT_COLOR,\r
825                 getResources().getDrawable(R.drawable.colors_marker)));\r
826         quickAction.addActionItem(new ActionItem(ACTION_ID_DELETE,\r
827                 getResources().getDrawable(R.drawable.ic_action_discard)));\r
828         quickAction.addActionItem(new ActionItem(ACTION_ID_SHARE,\r
829                 getResources().getDrawable(R.drawable.ic_action_share)));\r
830         final ViewGroup finalRoot = root;\r
831         quickAction.setOnActionItemClickListener(new QuickAction.OnActionItemClickListener() {\r
832             @Override\r
833             public void onItemClick(QuickAction source, int pos, int actionId) {\r
834                 quickAction.dismiss();\r
835                 finalRoot.removeView(view);\r
836                 onHighlightActionItemClicked(actionId, view, isCreated);\r
837             }\r
838         });\r
839         quickAction.show(view, width, height);\r
840     }\r
841 \r
842     private void onHighlightActionItemClicked(int actionId, View view, boolean isCreated) {\r
843         if (actionId == ACTION_ID_HIGHLIGHT_COLOR) {\r
844             onHighlightColors(view, isCreated);\r
845         } else if (actionId == ACTION_ID_SHARE) {\r
846             UiUtil.share(getActivity(), mSelectedText);\r
847             mTextSelectionSupport.endSelectionMode();\r
848         } else if (actionId == ACTION_ID_DELETE) {\r
849             highlightRemove();\r
850         }\r
851     }\r
852 \r
853     private void onHighlightColors(final View view, final boolean isCreated) {\r
854         ViewGroup root =\r
855                 (ViewGroup) getActivity().getWindow()\r
856                         .getDecorView().findViewById(android.R.id.content);\r
857         ViewGroup parent = (ViewGroup) view.getParent();\r
858         if (parent == null) {\r
859             root.addView(view);\r
860         } else {\r
861             final int index = parent.indexOfChild(view);\r
862             parent.removeView(view);\r
863             parent.addView(view, index);\r
864         }\r
865 \r
866         final QuickAction quickAction = new QuickAction(getActivity(), QuickAction.HORIZONTAL);\r
867         quickAction.addActionItem(new ActionItem(ACTION_ID_HIGHLIGHT_YELLOW,\r
868                 getResources().getDrawable(R.drawable.ic_yellow_marker)));\r
869         quickAction.addActionItem(new ActionItem(ACTION_ID_HIGHLIGHT_GREEN,\r
870                 getResources().getDrawable(R.drawable.ic_green_marker)));\r
871         quickAction.addActionItem(new ActionItem(ACTION_ID_HIGHLIGHT_BLUE,\r
872                 getResources().getDrawable(R.drawable.ic_blue_marker)));\r
873         quickAction.addActionItem(new ActionItem(ACTION_ID_HIGHLIGHT_PINK,\r
874                 getResources().getDrawable(R.drawable.ic_pink_marker)));\r
875         quickAction.addActionItem(new ActionItem(ACTION_ID_HIGHLIGHT_UNDERLINE,\r
876                 getResources().getDrawable(R.drawable.ic_underline_marker)));\r
877         final ViewGroup finalRoot = root;\r
878         quickAction.setOnActionItemClickListener(new QuickAction.OnActionItemClickListener() {\r
879             @Override\r
880             public void onItemClick(QuickAction source, int pos, int actionId) {\r
881                 quickAction.dismiss();\r
882                 finalRoot.removeView(view);\r
883                 onHighlightColorsActionItemClicked(actionId, view, isCreated);\r
884             }\r
885         });\r
886         quickAction.show(view);\r
887     }\r
888 \r
889     private void onHighlightColorsActionItemClicked(int actionId, View view, boolean isCreated) {\r
890         if (actionId == ACTION_ID_HIGHLIGHT_YELLOW) {\r
891             highlight(HighlightImpl.HighlightStyle.Yellow, isCreated);\r
892         } else if (actionId == ACTION_ID_HIGHLIGHT_GREEN) {\r
893             highlight(HighlightImpl.HighlightStyle.Green, isCreated);\r
894         } else if (actionId == ACTION_ID_HIGHLIGHT_BLUE) {\r
895             highlight(HighlightImpl.HighlightStyle.Blue, isCreated);\r
896         } else if (actionId == ACTION_ID_HIGHLIGHT_PINK) {\r
897             highlight(HighlightImpl.HighlightStyle.Pink, isCreated);\r
898         } else if (actionId == ACTION_ID_HIGHLIGHT_UNDERLINE) {\r
899             highlight(HighlightImpl.HighlightStyle.Underline, isCreated);\r
900         }\r
901         mTextSelectionSupport.endSelectionMode();\r
902     }\r
903 \r
904     @Override\r
905     public void resetCurrentIndex() {\r
906         if (isCurrentFragment()) {\r
907             mWebview.loadUrl("javascript:alert(rewindCurrentIndex())");\r
908         }\r
909     }\r
910 \r
911     @SuppressWarnings("unused")\r
912     @JavascriptInterface\r
913     public void onReceiveHighlights(String html) {\r
914         if (html != null) {\r
915             rangy = HighlightUtil.createHighlightRangy(getActivity().getApplicationContext(),\r
916                     html,\r
917                     mBookId,\r
918                     getPageName(),\r
919                     mPosition,\r
920                     rangy);\r
921         }\r
922     }\r
923 \r
924     private String getPageName() {\r
925         return mBookTitle + "$" + spineItem.href;\r
926     }\r
927 \r
928     @SuppressWarnings("unused")\r
929     @Subscribe\r
930     public void setWebView(final WebViewPosition position) {\r
931         if (position.getHref().equals(spineItem.href) && isAdded()) {\r
932             highlightId = position.getHighlightId();\r
933 \r
934             if (mWebview.getContentHeight() > 0) {\r
935                 scrollToHighlightId();\r
936                 //Webview.loadUrl(String.format(getString(R.string.goto_highlight), highlightId));\r
937             }\r
938         }\r
939     }\r
940 \r
941     public void setWebViewPosition(final int position) {\r
942         mWebview.post(new Runnable() {\r
943             @Override\r
944             public void run() {\r
945                 if (isAdded()) {\r
946                     mWebview.scrollTo(0, position);\r
947                 }\r
948             }\r
949         });\r
950     }\r
951 \r
952     @Override\r
953     public void highLightText(String fragmentId) {\r
954         mWebview.loadUrl(String.format(getString(R.string.audio_mark_id), fragmentId));\r
955     }\r
956 \r
957     @Override\r
958     public void highLightTTS() {\r
959         mWebview.loadUrl("javascript:alert(getSentenceWithIndex('epub-media-overlay-playing'))");\r
960     }\r
961 \r
962     @JavascriptInterface\r
963     public void getUpdatedHighlightId(String id, String style) {\r
964         if (id != null) {\r
965             HighlightImpl highlightImpl = HighLightTable.updateHighlightStyle(id, style);\r
966             if (highlightImpl != null) {\r
967                 HighlightUtil.sendHighlightBroadcastEvent(\r
968                         getActivity().getApplicationContext(),\r
969                         highlightImpl,\r
970                         HighLight.HighLightAction.MODIFY);\r
971             }\r
972             final String rangyString = HighlightUtil.generateRangyString(getPageName());\r
973             getActivity().runOnUiThread(new Runnable() {\r
974                 public void run() {\r
975                     loadRangy(mWebview, rangyString);\r
976                 }\r
977             });\r
978 \r
979         }\r
980     }\r
981 \r
982     @Subscribe\r
983     public void resetCurrentIndex(RewindIndexEvent resetIndex) {\r
984         if (isCurrentFragment()) {\r
985             mWebview.loadUrl("javascript:alert(rewindCurrentIndex())");\r
986         }\r
987     }\r
988 \r
989     @Override\r
990     public void onDestroy() {\r
991         super.onDestroy();\r
992         if (mWebview != null) mWebview.destroy();\r
993     }\r
994 \r
995     private boolean isCurrentFragment() {\r
996         return isAdded() && ((FolioActivity) getActivity()).getmChapterPosition() == mPos;\r
997     }\r
998 \r
999     public void setFragmentPos(int pos) {\r
1000         mPos = pos;\r
1001     }\r
1002 \r
1003     @Override\r
1004     public void onError() {\r
1005     }\r
1006 \r
1007     private void scrollToHighlightId() {\r
1008         mWebview.loadUrl(String.format(getString(R.string.goto_highlight), highlightId));\r
1009     }\r
1010 }\r