1 package com.folioreader.ui.folio.fragment;
\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
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
65 import org.readium.r2_streamer.model.publication.link.Link;
\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
74 * Created by mahavir on 4/2/16.
\r
76 @SuppressWarnings("PMD.AvoidDuplicateLiterals")
\r
77 public class FolioPageFragment extends Fragment implements HtmlTaskCallback, MediaControllerCallbacks, ObservableWebView.SeekBarListener {
\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
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
90 private static final int ACTION_ID_HIGHLIGHT_COLOR = 1005;
\r
91 private static final int ACTION_ID_DELETE = 1006;
\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
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
107 public interface FolioPageFragmentCallback {
\r
109 void setPagerToPosition(String href);
\r
111 void setLastWebViewPosition(int position);
\r
113 void goToChapter(String href);
\r
116 private View mRootView;
\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
124 private int mScrollY;
\r
125 private int mTotalMinutes;
\r
126 private String mSelectedText;
\r
127 private Animation mFadeInAnimation, mFadeOutAnimation;
\r
129 private Link spineItem;
\r
130 private int mPosition = -1;
\r
131 private String mBookTitle;
\r
132 private String mEpubFileName = null;
\r
134 private boolean mIsPageReloaded;
\r
135 private int mLastWebviewScrollpos;
\r
137 private String highlightStyle;
\r
139 private MediaController mediaController;
\r
140 private Config mConfig;
\r
141 private String mBookId;
\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
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
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
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
177 mediaController = new MediaController(getActivity(), MediaController.MediaType.TTS, this);
\r
178 mediaController.setTextToSpeech(getActivity());
\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
186 Activity activity = getActivity();
\r
188 mConfig = AppUtil.getSavedConfig(activity);
\r
190 if (activity instanceof FolioPageFragmentCallback)
\r
191 mActivityCallback = (FolioPageFragmentCallback) activity;
\r
193 if (activity instanceof BusOwner)
\r
194 ((BusOwner) activity).getBus().register(this);
\r
199 updatePagesLeftTextBg();
\r
205 private String getWebviewUrl() {
\r
206 return Constants.LOCALHOST + mBookTitle + "/" + spineItem.href;
\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
223 * [EVENT BUS FUNCTION]
\r
224 * Function triggered from {@link FolioActivity#initAudioView()} when pause/play
\r
225 * button is clicked
\r
227 * @param event of type {@link MediaOverlayPlayPauseEvent} contains if paused/played
\r
229 @SuppressWarnings("unused")
\r
231 public void pauseButtonClicked(MediaOverlayPlayPauseEvent event) {
\r
233 && spineItem.href.equals(event.getHref())) {
\r
234 mediaController.stateChanged(event);
\r
239 * [EVENT BUS FUNCTION]
\r
240 * Function triggered from {@link FolioActivity#initAudioView()} when speed
\r
241 * change buttons are clicked
\r
243 * @param event of type {@link MediaOverlaySpeedEvent} contains selected speed
\r
244 * type HALF,ONE,ONE_HALF and TWO.
\r
246 @SuppressWarnings("unused")
\r
248 public void speedChanged(MediaOverlaySpeedEvent event) {
\r
249 mediaController.setSpeed(event.getSpeed());
\r
253 * [EVENT BUS FUNCTION]
\r
254 * Function triggered from {@link FolioActivity#initAudioView()} when new
\r
255 * style is selected on button click.
\r
257 * @param event of type {@link MediaOverlaySpeedEvent} contains selected style
\r
258 * of type DEFAULT,UNDERLINE and BACKGROUND.
\r
260 @SuppressWarnings("unused")
\r
262 public void styleChanged(MediaOverlayHighlightStyleEvent event) {
\r
264 switch (event.getStyle()) {
\r
267 HighlightImpl.HighlightStyle.classForStyle(HighlightImpl.HighlightStyle.Normal);
\r
271 HighlightImpl.HighlightStyle.classForStyle(HighlightImpl.HighlightStyle.DottetUnderline);
\r
275 HighlightImpl.HighlightStyle.classForStyle(HighlightImpl.HighlightStyle.TextColor);
\r
278 mWebview.loadUrl(String.format(getString(R.string.setmediaoverlaystyle), highlightStyle));
\r
283 * [EVENT BUS FUNCTION]
\r
284 * Function triggered when any EBook configuration is changed.
\r
286 * @param reloadDataEvent empty POJO.
\r
289 public void reload(ReloadDataEvent reloadDataEvent) {
\r
291 mLastWebviewScrollpos = mWebview.getScrollY();
\r
292 mIsPageReloaded = true;
\r
294 updatePagesLeftTextBg();
\r
299 * [EVENT BUS FUNCTION]
\r
300 * Function triggered from {@link FolioActivity#onActivityResult(int, int, Intent)} when any item in toc clicked.
\r
302 * @param event of type {@link AnchorIdEvent} contains selected chapter href.
\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
318 public void onReceiveHtml(String html) {
\r
320 mHtmlString = html;
\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
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
344 public void onStop() {
\r
346 mediaController.stop();
\r
347 //TODO save last media overlay item
\r
350 private void initWebView() {
\r
351 mWebview = (ObservableWebView) mRootView.findViewById(R.id.contentWebView);
\r
352 mWebview.setSeekBarListener(FolioPageFragment.this);
\r
354 if (getActivity() instanceof ObservableWebView.ToolBarListener)
\r
355 mWebview.setToolBarListener((ObservableWebView.ToolBarListener) getActivity());
\r
357 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
\r
358 WebView.setWebContentsDebuggingEnabled(true);
\r
362 mWebview.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
\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
367 (int) Math.floor(mWebview.getContentHeight() * mWebview.getScale());
\r
368 int webViewHeight = mWebview.getMeasuredHeight();
\r
369 mScrollSeekbar.setMaximum(height - webViewHeight);
\r
373 mWebview.getSettings().setJavaScriptEnabled(true);
\r
374 mWebview.setVerticalScrollBarEnabled(false);
\r
375 mWebview.getSettings().setAllowFileAccess(true);
\r
377 mWebview.setHorizontalScrollBarEnabled(false);
\r
379 mWebview.addJavascriptInterface(this, "Highlight");
\r
380 mWebview.setScrollListener(new ObservableWebView.ScrollListener() {
\r
382 public void onScrollChange(int percent) {
\r
383 if (mWebview.getScrollY() != 0) {
\r
384 mScrollY = mWebview.getScrollY();
\r
386 ((FolioActivity) getActivity()).setLastWebViewPosition(mScrollY);
\r
389 mScrollSeekbar.setProgressAndThumb(percent);
\r
390 updatePagesLeftText(percent);
\r
395 mWebview.setWebViewClient(new WebViewClient() {
\r
397 public void onPageFinished(WebView view, String url) {
\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
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
414 String rangy = HighlightUtil.generateRangyString(getPageName());
\r
415 FolioPageFragment.this.rangy = rangy;
\r
416 if (!rangy.isEmpty()) {
\r
417 loadRangy(view, rangy);
\r
420 scrollToHighlightId();
\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
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
441 (int) (UiUtil.convertDpToPixel((float) top,
\r
443 (int) (UiUtil.convertDpToPixel((float) width,
\r
445 (int) (UiUtil.convertDpToPixel((float) height,
\r
448 } catch (UnsupportedEncodingException e) {
\r
449 Log.d(TAG, e.getMessage());
\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
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
467 // prevent favicon.ico to be loaded automatically
\r
469 public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
\r
470 if(url.toLowerCase().contains("/favicon.ico")) {
\r
472 return new WebResourceResponse("image/png", null, null);
\r
473 } catch (Exception e) {
\r
474 Log.e(TAG, "shouldInterceptRequest failed", e);
\r
480 // prevent favicon.ico to be loaded automatically
\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
486 return new WebResourceResponse("image/png", null, null);
\r
487 } catch (Exception e) {
\r
488 Log.e(TAG, "shouldInterceptRequest failed", e);
\r
495 mWebview.setWebChromeClient(new WebChromeClient() {
\r
497 public void onProgressChanged(WebView view, int progress) {
\r
499 if (view.getProgress() == 100) {
\r
500 mWebview.postDelayed(new Runnable() {
\r
502 public void run() {
\r
503 Log.d("scroll y", "Scrolly" + mScrollY);
\r
504 mWebview.scrollTo(0, mScrollY);
\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
526 HighLight.HighLightAction.DELETE);
\r
529 } else if (TextUtils.isDigitsOnly(message)) {
\r
530 mTotalMinutes = Integer.parseInt(message);
\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
541 (int) (UiUtil.convertDpToPixel((float) top,
\r
543 (int) (UiUtil.convertDpToPixel((float) width,
\r
545 (int) (UiUtil.convertDpToPixel((float) height,
\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
561 // mTextSelectionSupport = TextSelectionSupport.support(getActivity(), mWebview);
\r
562 // mTextSelectionSupport.setSelectionListener(new TextSelectionSupport.SelectionListener() {
\r
564 // public void startSelection() {
\r
568 // public void selectionChanged(String text) {
\r
569 // mSelectedText = text;
\r
570 // getActivity().runOnUiThread(new Runnable() {
\r
572 // public void run() {
\r
573 // mWebview.loadUrl("javascript:alert(getRectForSelectedText())");
\r
579 // public void endSelection() {
\r
584 mWebview.getSettings().setDefaultTextEncodingName("utf-8");
\r
585 mWebview.setOnLongClickListener(new View.OnLongClickListener() {
\r
587 public boolean onLongClick(View v) {
\r
591 mWebview.setLongClickable(false);
\r
592 mWebview.setHapticFeedbackEnabled(false);
\r
593 new HtmlTask(this).execute(getWebviewUrl());
\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
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
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
615 private void updatePagesLeftTextBg() {
\r
617 if (mConfig.isNightMode()) {
\r
618 mRootView.findViewById(R.id.indicatorLayout)
\r
619 .setBackgroundColor(getResources().getColor(R.color.dark_night));
\r
621 mRootView.findViewById(R.id.indicatorLayout)
\r
622 .setBackgroundColor(Color.WHITE);
\r
626 private void updatePagesLeftText(int scrollY) {
\r
628 int currentPage = (int) (Math.ceil((double) scrollY / mWebview.getWebViewHeight()) + 1);
\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
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
646 } else if (minutesRemaining == 1) {
\r
647 minutesRemainingStr =
\r
648 String.format(Locale.US, getString(R.string.minute_left),
\r
651 minutesRemainingStr = getString(R.string.less_than_minute);
\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
661 private void initAnimations() {
\r
662 mFadeInAnimation = AnimationUtils.loadAnimation(getActivity(), R.anim.fadein);
\r
663 mFadeInAnimation.setAnimationListener(new Animation.AnimationListener() {
\r
665 public void onAnimationStart(Animation animation) {
\r
666 mScrollSeekbar.setVisibility(View.VISIBLE);
\r
670 public void onAnimationEnd(Animation animation) {
\r
671 fadeOutSeekBarIfVisible();
\r
675 public void onAnimationRepeat(Animation animation) {
\r
679 mFadeOutAnimation = AnimationUtils.loadAnimation(getActivity(), R.anim.fadeout);
\r
680 mFadeOutAnimation.setAnimationListener(new Animation.AnimationListener() {
\r
682 public void onAnimationStart(Animation animation) {
\r
687 public void onAnimationEnd(Animation animation) {
\r
688 mScrollSeekbar.setVisibility(View.INVISIBLE);
\r
692 public void onAnimationRepeat(Animation animation) {
\r
698 public void fadeInSeekBarIfInvisible() {
\r
699 if (mScrollSeekbar.getVisibility() == View.INVISIBLE ||
\r
700 mScrollSeekbar.getVisibility() == View.GONE) {
\r
701 mScrollSeekbar.startAnimation(mFadeInAnimation);
\r
705 public void fadeOutSeekBarIfVisible() {
\r
706 if (mScrollSeekbar.getVisibility() == View.VISIBLE) {
\r
707 mScrollSeekbar.startAnimation(mFadeOutAnimation);
\r
712 public void onDestroyView() {
\r
713 mFadeInAnimation.setAnimationListener(null);
\r
714 mFadeOutAnimation.setAnimationListener(null);
\r
716 Activity activity = getActivity();
\r
717 if (activity instanceof BusOwner)
\r
718 ((BusOwner) activity).getBus().unregister(this);
\r
719 super.onDestroyView();
\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
731 public void highlight(HighlightImpl.HighlightStyle style, boolean isCreated) {
\r
733 mWebview.loadUrl(String.format("javascript:if(typeof ssReader !== \"undefined\"){ssReader.highlightSelection('%s');}", HighlightImpl.HighlightStyle.classForStyle(style)));
\r
735 mWebview.loadUrl(String.format("javascript:alert(setHighlightStyle('%s'))", "highlight_" + HighlightImpl.HighlightStyle.classForStyle(style)));
\r
739 public void highlightRemove() {
\r
740 mWebview.loadUrl("javascript:alert(removeThisHighlight())");
\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
751 root.addView(view);
\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
765 quickAction.addActionItem(new ActionItem(ACTION_ID_SHARE,
\r
766 getString(R.string.share)));
\r
767 quickAction.setOnActionItemClickListener(new QuickAction.OnActionItemClickListener() {
\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
775 quickAction.show(view, width, height);
\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
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
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
807 onHighlight(view, width, height, false);
\r
810 private void onHighlight(final View view, int width, int height, final boolean isCreated) {
\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
818 final int index = parent.indexOfChild(view);
\r
819 parent.removeView(view);
\r
820 parent.addView(view, index);
\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
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
839 quickAction.show(view, width, height);
\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
853 private void onHighlightColors(final View view, final boolean isCreated) {
\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
861 final int index = parent.indexOfChild(view);
\r
862 parent.removeView(view);
\r
863 parent.addView(view, index);
\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
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
886 quickAction.show(view);
\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
901 mTextSelectionSupport.endSelectionMode();
\r
905 public void resetCurrentIndex() {
\r
906 if (isCurrentFragment()) {
\r
907 mWebview.loadUrl("javascript:alert(rewindCurrentIndex())");
\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
924 private String getPageName() {
\r
925 return mBookTitle + "$" + spineItem.href;
\r
928 @SuppressWarnings("unused")
\r
930 public void setWebView(final WebViewPosition position) {
\r
931 if (position.getHref().equals(spineItem.href) && isAdded()) {
\r
932 highlightId = position.getHighlightId();
\r
934 if (mWebview.getContentHeight() > 0) {
\r
935 scrollToHighlightId();
\r
936 //Webview.loadUrl(String.format(getString(R.string.goto_highlight), highlightId));
\r
941 public void setWebViewPosition(final int position) {
\r
942 mWebview.post(new Runnable() {
\r
944 public void run() {
\r
946 mWebview.scrollTo(0, position);
\r
953 public void highLightText(String fragmentId) {
\r
954 mWebview.loadUrl(String.format(getString(R.string.audio_mark_id), fragmentId));
\r
958 public void highLightTTS() {
\r
959 mWebview.loadUrl("javascript:alert(getSentenceWithIndex('epub-media-overlay-playing'))");
\r
962 @JavascriptInterface
\r
963 public void getUpdatedHighlightId(String id, String style) {
\r
965 HighlightImpl highlightImpl = HighLightTable.updateHighlightStyle(id, style);
\r
966 if (highlightImpl != null) {
\r
967 HighlightUtil.sendHighlightBroadcastEvent(
\r
968 getActivity().getApplicationContext(),
\r
970 HighLight.HighLightAction.MODIFY);
\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
983 public void resetCurrentIndex(RewindIndexEvent resetIndex) {
\r
984 if (isCurrentFragment()) {
\r
985 mWebview.loadUrl("javascript:alert(rewindCurrentIndex())");
\r
990 public void onDestroy() {
\r
992 if (mWebview != null) mWebview.destroy();
\r
995 private boolean isCurrentFragment() {
\r
996 return isAdded() && ((FolioActivity) getActivity()).getmChapterPosition() == mPos;
\r
999 public void setFragmentPos(int pos) {
\r
1004 public void onError() {
\r
1007 private void scrollToHighlightId() {
\r
1008 mWebview.loadUrl(String.format(getString(R.string.goto_highlight), highlightId));
\r