+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