From 6b5d269185d5c4c0ed6e352c42790f7f1c35b06b Mon Sep 17 00:00:00 2001 From: daniel oeh Date: Sun, 29 Jun 2014 03:33:22 +0200 Subject: [PATCH] Integrated timecode highlighting into Audioplayer --- .../activity/AudioplayerActivity.java | 10 +- .../fragment/ItemDescriptionFragment.java | 107 ++++++++++-------- src/de/danoeh/antennapod/util/Converter.java | 14 +-- .../util/playback/PlaybackController.java | 6 + .../antennapod/util/playback/Timeline.java | 19 +++- .../util/playback/TimelineTest.java | 1 + 6 files changed, 94 insertions(+), 63 deletions(-) diff --git a/src/de/danoeh/antennapod/activity/AudioplayerActivity.java b/src/de/danoeh/antennapod/activity/AudioplayerActivity.java index 090c3f1f5..abd383152 100644 --- a/src/de/danoeh/antennapod/activity/AudioplayerActivity.java +++ b/src/de/danoeh/antennapod/activity/AudioplayerActivity.java @@ -33,11 +33,12 @@ import de.danoeh.antennapod.service.playback.PlaybackService; import de.danoeh.antennapod.storage.DBReader; import de.danoeh.antennapod.util.playback.ExternalMedia; import de.danoeh.antennapod.util.playback.Playable; +import de.danoeh.antennapod.util.playback.PlaybackController; /** * Activity for playing audio files. */ -public class AudioplayerActivity extends MediaplayerActivity { +public class AudioplayerActivity extends MediaplayerActivity implements ItemDescriptionFragment.ItemDescriptionFragmentCallback { private static final int POS_COVER = 0; private static final int POS_DESCR = 1; private static final int POS_CHAPTERS = 2; @@ -293,7 +294,7 @@ public class AudioplayerActivity extends MediaplayerActivity { case POS_DESCR: if (descriptionFragment == null) { descriptionFragment = ItemDescriptionFragment - .newInstance(media, true); + .newInstance(media, true, true); } currentlyShownFragment = descriptionFragment; break; @@ -603,6 +604,11 @@ public class AudioplayerActivity extends MediaplayerActivity { clearStatusMsg(); } + @Override + public PlaybackController getPlaybackController() { + return controller; + } + public interface AudioplayerContentFragment { public void onDataSetChanged(Playable media); } diff --git a/src/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java b/src/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java index bf6974982..e2d7f32a2 100644 --- a/src/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java +++ b/src/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java @@ -2,8 +2,11 @@ package de.danoeh.antennapod.fragment; import android.annotation.SuppressLint; import android.app.Activity; -import android.content.*; -import android.content.res.TypedArray; +import android.content.ActivityNotFoundException; +import android.content.ClipData; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; import android.net.Uri; import android.os.AsyncTask; import android.os.Build; @@ -12,12 +15,18 @@ import android.support.v4.app.Fragment; import android.support.v7.app.ActionBarActivity; import android.util.Log; import android.util.TypedValue; -import android.view.*; +import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; import android.webkit.WebSettings.LayoutAlgorithm; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.Toast; + import de.danoeh.antennapod.BuildConfig; import de.danoeh.antennapod.R; import de.danoeh.antennapod.feed.FeedItem; @@ -26,11 +35,12 @@ import de.danoeh.antennapod.storage.DBReader; import de.danoeh.antennapod.util.ShareUtils; import de.danoeh.antennapod.util.ShownotesProvider; import de.danoeh.antennapod.util.playback.Playable; -import org.apache.commons.lang3.StringEscapeUtils; +import de.danoeh.antennapod.util.playback.PlaybackController; +import de.danoeh.antennapod.util.playback.Timeline; -import java.util.concurrent.Callable; - -/** Displays the description of a Playable object in a Webview. */ +/** + * Displays the description of a Playable object in a Webview. + */ public class ItemDescriptionFragment extends Fragment { private static final String TAG = "ItemDescriptionFragment"; @@ -43,6 +53,7 @@ public class ItemDescriptionFragment extends Fragment { private static final String ARG_FEEDITEM_ID = "arg.feeditem"; private static final String ARG_SAVE_STATE = "arg.saveState"; + private static final String ARG_HIGHLIGHT_TIMECODES = "arg.highlightTimecodes"; private WebView webvDescription; @@ -63,21 +74,29 @@ public class ItemDescriptionFragment extends Fragment { */ private boolean saveState; + /** + * True if Fragment should highlight timecodes (e.g. time codes in the HH:MM:SS format). + */ + private boolean highlightTimecodes; + public static ItemDescriptionFragment newInstance(Playable media, - boolean saveState) { + boolean saveState, + boolean highlightTimecodes) { ItemDescriptionFragment f = new ItemDescriptionFragment(); Bundle args = new Bundle(); args.putParcelable(ARG_PLAYABLE, media); args.putBoolean(ARG_SAVE_STATE, saveState); + args.putBoolean(ARG_HIGHLIGHT_TIMECODES, highlightTimecodes); f.setArguments(args); return f; } - public static ItemDescriptionFragment newInstance(FeedItem item, boolean saveState) { + public static ItemDescriptionFragment newInstance(FeedItem item, boolean saveState, boolean highlightTimecodes) { ItemDescriptionFragment f = new ItemDescriptionFragment(); Bundle args = new Bundle(); args.putLong(ARG_FEEDITEM_ID, item.getId()); args.putBoolean(ARG_SAVE_STATE, saveState); + args.putBoolean(ARG_HIGHLIGHT_TIMECODES, highlightTimecodes); f.setArguments(args); return f; } @@ -106,12 +125,22 @@ public class ItemDescriptionFragment extends Fragment { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); - try { - startActivity(intent); - } catch (ActivityNotFoundException e) { - e.printStackTrace(); - return false; + if (Timeline.isTimecodeLink(url)) { + int time = Timeline.getTimecodeLinkTime(url); + if (getActivity() != null && getActivity() instanceof ItemDescriptionFragmentCallback) { + PlaybackController pc = ((ItemDescriptionFragmentCallback) getActivity()).getPlaybackController(); + if (pc != null) { + pc.seekTo(time); + } + } + } else { + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + try { + startActivity(intent); + } catch (ActivityNotFoundException e) { + e.printStackTrace(); + return true; + } } return true; } @@ -178,6 +207,7 @@ public class ItemDescriptionFragment extends Fragment { Log.d(TAG, "Creating fragment"); Bundle args = getArguments(); saveState = args.getBoolean(ARG_SAVE_STATE, false); + highlightTimecodes = args.getBoolean(ARG_HIGHLIGHT_TIMECODES, false); } @@ -229,21 +259,6 @@ public class ItemDescriptionFragment extends Fragment { } } - /** - * Return the CSS style of the Webview. - * - * @param textColor the default color to use for the text in the webview. This - * value is inserted directly into the CSS String. - */ - private String applyWebviewStyle(String textColor, String data) { - final String WEBVIEW_STYLE = "%s"; - final int pageMargin = (int) TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, 8, getResources() - .getDisplayMetrics()); - return String.format(WEBVIEW_STYLE, textColor, "100%", pageMargin, - pageMargin, pageMargin, pageMargin, data); - } - private View.OnLongClickListener webViewLongClickListener = new View.OnLongClickListener() { @Override @@ -254,10 +269,13 @@ public class ItemDescriptionFragment extends Fragment { if (BuildConfig.DEBUG) Log.d(TAG, "Link of webview was long-pressed. Extra: " - + r.getExtra()); + + r.getExtra() + ); selectedURL = r.getExtra(); - webvDescription.showContextMenu(); - return true; + if (!Timeline.isTimecodeLink(selectedURL)) { + webvDescription.showContextMenu(); + return true; + } } selectedURL = null; return false; @@ -364,22 +382,10 @@ public class ItemDescriptionFragment extends Fragment { if (BuildConfig.DEBUG) Log.d(TAG, "Loading Webview"); try { - Callable shownotesLoadTask = shownotesProvider.loadShownotes(); - final String shownotes = shownotesLoadTask.call(); - - data = StringEscapeUtils.unescapeHtml4(shownotes); Activity activity = getActivity(); if (activity != null) { - TypedArray res = activity - .getTheme() - .obtainStyledAttributes( - new int[]{android.R.attr.textColorPrimary}); - int colorResource = res.getColor(0, 0); - String colorString = String.format("#%06X", - 0xFFFFFF & colorResource); - Log.i(TAG, "text color: " + colorString); - res.recycle(); - data = applyWebviewStyle(colorString, data); + Timeline timeline = new Timeline(activity, shownotesProvider); + data = timeline.processShownotes(highlightTimecodes); } else { cancel(true); } @@ -409,7 +415,8 @@ public class ItemDescriptionFragment extends Fragment { if (BuildConfig.DEBUG) Log.d(TAG, "Saving scroll position: " - + webvDescription.getScrollY()); + + webvDescription.getScrollY() + ); editor.putInt(PREF_SCROLL_Y, webvDescription.getScrollY()); editor.putString(PREF_PLAYABLE_ID, media.getIdentifier() .toString()); @@ -447,4 +454,8 @@ public class ItemDescriptionFragment extends Fragment { } return false; } + + public interface ItemDescriptionFragmentCallback { + public PlaybackController getPlaybackController(); + } } diff --git a/src/de/danoeh/antennapod/util/Converter.java b/src/de/danoeh/antennapod/util/Converter.java index bc3d9edd3..f4c2b2f66 100644 --- a/src/de/danoeh/antennapod/util/Converter.java +++ b/src/de/danoeh/antennapod/util/Converter.java @@ -80,24 +80,24 @@ public final class Converter { } /** Converts long duration string (HH:MM:SS) to milliseconds. */ - public static long durationStringLongToMs(String input) { + public static int durationStringLongToMs(String input) { String[] parts = input.split(":"); if (parts.length != 3) { return 0; } - return Long.valueOf(parts[0]) * 3600 * 1000 + - Long.valueOf(parts[1]) * 60 * 1000 + - Long.valueOf(parts[2]) * 1000; + return Integer.valueOf(parts[0]) * 3600 * 1000 + + Integer.valueOf(parts[1]) * 60 * 1000 + + Integer.valueOf(parts[2]) * 1000; } /** Converts short duration string (HH:MM) to milliseconds. */ - public static long durationStringShortToMs(String input) { + public static int durationStringShortToMs(String input) { String[] parts = input.split(":"); if (parts.length != 2) { return 0; } - return Long.valueOf(parts[0]) * 3600 * 1000 + - Long.valueOf(parts[1]) * 1000 * 60; + return Integer.valueOf(parts[0]) * 3600 * 1000 + + Integer.valueOf(parts[1]) * 1000 * 60; } } diff --git a/src/de/danoeh/antennapod/util/playback/PlaybackController.java b/src/de/danoeh/antennapod/util/playback/PlaybackController.java index 5783b5bc5..a979ddc1c 100644 --- a/src/de/danoeh/antennapod/util/playback/PlaybackController.java +++ b/src/de/danoeh/antennapod/util/playback/PlaybackController.java @@ -680,6 +680,12 @@ public abstract class PlaybackController { } } + public void seekTo(int time) { + if (playbackService != null) { + playbackService.seekTo(time); + } + } + public void setVideoSurface(SurfaceHolder holder) { if (playbackService != null) { playbackService.setVideoSurface(holder); diff --git a/src/de/danoeh/antennapod/util/playback/Timeline.java b/src/de/danoeh/antennapod/util/playback/Timeline.java index 6e00f725c..33ffb054c 100644 --- a/src/de/danoeh/antennapod/util/playback/Timeline.java +++ b/src/de/danoeh/antennapod/util/playback/Timeline.java @@ -27,7 +27,7 @@ import de.danoeh.antennapod.util.ShownotesProvider; public class Timeline { private static final String TAG = "Timeline"; - private static final String WEBVIEW_STYLE = "