diff --git a/app/build.gradle b/app/build.gradle index f89f1d004..da735302c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,8 +8,8 @@ android { applicationId "org.schabi.newpipe" minSdkVersion 19 targetSdkVersion 28 - versionCode 720 - versionName "0.16.0" + versionCode 740 + versionName "0.16.2" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true @@ -57,7 +57,7 @@ dependencies { exclude module: 'support-annotations' }) - implementation 'com.github.TeamNewPipe:NewPipeExtractor:aa4f03a' + implementation 'com.github.TeamNewPipe:NewPipeExtractor:2ac713e' testImplementation 'junit:junit:4.12' testImplementation 'org.mockito:mockito-core:2.23.0' diff --git a/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java index acee1f111..4546483d2 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java @@ -230,21 +230,4 @@ public abstract class BaseStateFragment extends BaseFragment implements ViewC ErrorActivity.reportError(getContext(), exception, MainActivity.class, rootView, ErrorActivity.ErrorInfo.make(userAction, serviceName, request, errorId)); } - - /*////////////////////////////////////////////////////////////////////////// - // Utils - //////////////////////////////////////////////////////////////////////////*/ - - protected void openUrlInBrowser(String url) { - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); - startActivity(Intent.createChooser(intent, activity.getString(R.string.share_dialog_title))); - } - - protected void shareUrl(String subject, String url) { - Intent intent = new Intent(Intent.ACTION_SEND); - intent.setType("text/plain"); - intent.putExtra(Intent.EXTRA_SUBJECT, subject); - intent.putExtra(Intent.EXTRA_TEXT, url); - startActivity(Intent.createChooser(intent, getString(R.string.share_dialog_title))); - } } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/TabAdaptor.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/TabAdaptor.java index 27cc3ec8a..8314f9539 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/TabAdaptor.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/TabAdaptor.java @@ -1,5 +1,6 @@ package org.schabi.newpipe.fragments.detail; +import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; @@ -61,6 +62,18 @@ public class TabAdaptor extends FragmentPagerAdapter { else return POSITION_NONE; } + public int getItemPositionByTitle(String title) { + return mFragmentTitleList.indexOf(title); + } + + @Nullable + public String getItemTitle(int position) { + if (position < 0 || position >= mFragmentTitleList.size()) { + return null; + } + return mFragmentTitleList.get(position); + } + public void notifyDataSetUpdate(){ notifyDataSetChanged(); } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index 8c87c1875..bbd1a315d 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -86,6 +86,7 @@ import org.schabi.newpipe.util.ListHelper; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.PermissionHelper; +import org.schabi.newpipe.util.ShareUtils; import org.schabi.newpipe.util.StreamItemAdapter; import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper; @@ -122,6 +123,7 @@ public class VideoDetailFragment private boolean autoPlayEnabled; private boolean showRelatedStreams; private boolean showComments; + private String selectedTabTag; @State protected int serviceId = Constants.NO_SERVICE_ID; @@ -213,6 +215,9 @@ public class VideoDetailFragment showComments = PreferenceManager.getDefaultSharedPreferences(activity) .getBoolean(getString(R.string.show_comments_key), true); + selectedTabTag = PreferenceManager.getDefaultSharedPreferences(activity) + .getString(getString(R.string.stream_info_selected_tab_key), COMMENTS_TAB_TAG); + PreferenceManager.getDefaultSharedPreferences(activity) .registerOnSharedPreferenceChangeListener(this); } @@ -226,6 +231,10 @@ public class VideoDetailFragment public void onPause() { super.onPause(); if (currentWorker != null) currentWorker.dispose(); + PreferenceManager.getDefaultSharedPreferences(getContext()) + .edit() + .putString(getString(R.string.stream_info_selected_tab_key), pageAdapter.getItemTitle(viewPager.getCurrentItem())) + .apply(); } @Override @@ -539,7 +548,7 @@ public class VideoDetailFragment } break; case 3: - shareUrl(item.getName(), item.getUrl()); + ShareUtils.shareUrl(this.getContext(), item.getName(), item.getUrl()); break; default: break; @@ -628,13 +637,13 @@ public class VideoDetailFragment switch (id) { case R.id.menu_item_share: { if (currentInfo != null) { - shareUrl(currentInfo.getName(), currentInfo.getOriginalUrl()); + ShareUtils.shareUrl(this.getContext(), currentInfo.getName(), currentInfo.getOriginalUrl()); } return true; } case R.id.menu_item_openInBrowser: { if (currentInfo != null) { - openUrlInBrowser(currentInfo.getOriginalUrl()); + ShareUtils.openUrlInBrowser(this.getContext(), currentInfo.getOriginalUrl()); } return true; } @@ -815,6 +824,9 @@ public class VideoDetailFragment } private void initTabs() { + if (pageAdapter.getCount() != 0) { + selectedTabTag = pageAdapter.getItemTitle(viewPager.getCurrentItem()); + } pageAdapter.clearAllItems(); if(shouldShowComments()){ @@ -835,6 +847,8 @@ public class VideoDetailFragment if(pageAdapter.getCount() < 2){ tabLayout.setVisibility(View.GONE); }else{ + int position = pageAdapter.getItemPositionByTitle(selectedTabTag); + if(position != -1) viewPager.setCurrentItem(position); tabLayout.setVisibility(View.VISIBLE); } } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java index 788d285b3..dbc3dd8a2 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java @@ -34,6 +34,7 @@ import org.schabi.newpipe.player.playqueue.SinglePlayQueue; import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.OnClickGesture; +import org.schabi.newpipe.util.ShareUtils; import org.schabi.newpipe.util.StateSaver; import java.util.Collections; @@ -280,7 +281,7 @@ public abstract class BaseListFragment extends BaseStateFragment implem } break; case 4: - shareUrl(item.getName(), item.getUrl()); + ShareUtils.shareUrl(this.getContext(), item.getName(), item.getUrl()); break; default: break; diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java index b9489ffa7..71865b04d 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java @@ -46,6 +46,7 @@ import org.schabi.newpipe.util.ExtractorHelper; import org.schabi.newpipe.util.ImageDisplayConstants; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; +import org.schabi.newpipe.util.ShareUtils; import java.util.ArrayList; import java.util.Collections; @@ -190,7 +191,7 @@ public class ChannelFragment extends BaseListInfoFragment { } break; case 6: - shareUrl(item.getName(), item.getUrl()); + ShareUtils.shareUrl(this.getContext(), item.getName(), item.getUrl()); break; default: break; @@ -233,10 +234,10 @@ public class ChannelFragment extends BaseListInfoFragment { openRssFeed(); break; case R.id.menu_item_openInBrowser: - openUrlInBrowser(currentInfo.getOriginalUrl()); + ShareUtils.openUrlInBrowser(this.getContext(), currentInfo.getOriginalUrl()); break; case R.id.menu_item_share: - shareUrl(name, currentInfo.getOriginalUrl()); + ShareUtils.shareUrl(this.getContext(), name, currentInfo.getOriginalUrl()); break; default: return super.onOptionsItemSelected(item); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java index d552b4e66..2a775fe8f 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java @@ -40,6 +40,7 @@ import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.util.ExtractorHelper; import org.schabi.newpipe.util.ImageDisplayConstants; import org.schabi.newpipe.util.NavigationHelper; +import org.schabi.newpipe.util.ShareUtils; import org.schabi.newpipe.util.ThemeHelper; import java.util.ArrayList; @@ -168,7 +169,7 @@ public class PlaylistFragment extends BaseListInfoFragment { NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index)); break; case 5: - shareUrl(item.getName(), item.getUrl()); + ShareUtils.shareUrl(this.getContext(), item.getName(), item.getUrl()); break; default: break; @@ -230,10 +231,10 @@ public class PlaylistFragment extends BaseListInfoFragment { public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_item_openInBrowser: - openUrlInBrowser(url); + ShareUtils.openUrlInBrowser(this.getContext(), url); break; case R.id.menu_item_share: - shareUrl(name, url); + ShareUtils.shareUrl(this.getContext(), name, url); break; case R.id.menu_item_bookmark: onBookmarkClicked(); diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java index 94fb78875..ce8412b20 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java @@ -46,7 +46,7 @@ public class CommentsMiniInfoItemHolder extends InfoItemHolder { if(hours != null) timestamp += (Integer.parseInt(hours.replace(":", ""))*3600); if(minutes != null) timestamp += (Integer.parseInt(minutes.replace(":", ""))*60); if(seconds != null) timestamp += (Integer.parseInt(seconds)); - return streamUrl + url.replace(match.group(0), "&t=" + String.valueOf(timestamp)); + return streamUrl + url.replace(match.group(0), "#timestamp=" + String.valueOf(timestamp)); } }; @@ -93,15 +93,14 @@ public class CommentsMiniInfoItemHolder extends InfoItemHolder { streamUrl = item.getUrl(); - itemContentView.setMaxLines(commentDefaultLines); + itemContentView.setLines(commentDefaultLines); commentText = item.getCommentText(); itemContentView.setText(commentText); - linkify(); itemContentView.setOnTouchListener(CommentTextOnTouchListener.INSTANCE); - if(itemContentView.getLineCount() == 0){ + if (itemContentView.getLineCount() == 0) { itemContentView.post(() -> ellipsize()); - }else{ + } else { ellipsize(); } @@ -121,15 +120,17 @@ public class CommentsMiniInfoItemHolder extends InfoItemHolder { private void ellipsize() { if (itemContentView.getLineCount() > commentDefaultLines){ int endOfLastLine = itemContentView.getLayout().getLineEnd(commentDefaultLines - 1); - String newVal = itemContentView.getText().subSequence(0, endOfLastLine - 3) + "..."; + int end = itemContentView.getText().toString().lastIndexOf(' ', endOfLastLine -2); + if(end == -1) end = Math.max(endOfLastLine -2, 0); + String newVal = itemContentView.getText().subSequence(0, end) + " …"; itemContentView.setText(newVal); - linkify(); } + linkify(); } private void toggleEllipsize() { if (itemContentView.getText().toString().equals(commentText)) { - ellipsize(); + if (itemContentView.getLineCount() > commentDefaultLines) ellipsize(); } else { expand(); } diff --git a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java index 5436913dc..5a62a3969 100644 --- a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java @@ -34,6 +34,7 @@ import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.settings.SettingsActivity; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.OnClickGesture; +import org.schabi.newpipe.util.ShareUtils; import org.schabi.newpipe.util.ThemeHelper; import java.util.ArrayList; @@ -394,7 +395,7 @@ public class StatisticsPlaylistFragment deleteEntry(index); break; case 6: - shareUrl(item.toStreamInfoItem().getName(), item.toStreamInfoItem().getUrl()); + ShareUtils.shareUrl(this.getContext(), item.toStreamInfoItem().getName(), item.toStreamInfoItem().getUrl()); break; default: break; diff --git a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java index f400061e1..dc101fade 100644 --- a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java @@ -34,6 +34,7 @@ import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.OnClickGesture; +import org.schabi.newpipe.util.ShareUtils; import java.util.ArrayList; import java.util.Collections; @@ -555,7 +556,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment= Build.VERSION_CODES.LOLLIPOP) { @ColorInt final int systenUiColor = @@ -393,6 +395,7 @@ public final class MainVideoPlayer extends AppCompatActivity private ImageButton playPauseButton; private ImageButton playPreviousButton; private ImageButton playNextButton; + private Button closeButton; private RelativeLayout queueLayout; private ImageButton itemsListCloseButton; @@ -402,6 +405,7 @@ public final class MainVideoPlayer extends AppCompatActivity private boolean queueVisible; private ImageButton moreOptionsButton; + private ImageButton shareButton; private ImageButton toggleOrientationButton; private ImageButton switchPopupButton; private ImageButton switchBackgroundButton; @@ -433,9 +437,11 @@ public final class MainVideoPlayer extends AppCompatActivity this.playPauseButton = rootView.findViewById(R.id.playPauseButton); this.playPreviousButton = rootView.findViewById(R.id.playPreviousButton); this.playNextButton = rootView.findViewById(R.id.playNextButton); + this.closeButton = rootView.findViewById(R.id.closeButton); this.moreOptionsButton = rootView.findViewById(R.id.moreOptionsButton); this.secondaryControls = rootView.findViewById(R.id.secondaryControls); + this.shareButton = rootView.findViewById(R.id.share); this.toggleOrientationButton = rootView.findViewById(R.id.toggleOrientation); this.switchBackgroundButton = rootView.findViewById(R.id.switchBackground); this.switchPopupButton = rootView.findViewById(R.id.switchPopup); @@ -479,8 +485,10 @@ public final class MainVideoPlayer extends AppCompatActivity playPauseButton.setOnClickListener(this); playPreviousButton.setOnClickListener(this); playNextButton.setOnClickListener(this); + closeButton.setOnClickListener(this); moreOptionsButton.setOnClickListener(this); + shareButton.setOnClickListener(this); toggleOrientationButton.setOnClickListener(this); switchBackgroundButton.setOnClickListener(this); switchPopupButton.setOnClickListener(this); @@ -631,6 +639,9 @@ public final class MainVideoPlayer extends AppCompatActivity } else if (v.getId() == moreOptionsButton.getId()) { onMoreOptionsClicked(); + } else if (v.getId() == shareButton.getId()) { + onShareClicked(); + } else if (v.getId() == toggleOrientationButton.getId()) { onScreenRotationClicked(); @@ -640,6 +651,9 @@ public final class MainVideoPlayer extends AppCompatActivity } else if (v.getId() == switchBackgroundButton.getId()) { onPlayBackgroundButtonClicked(); + } else if (v.getId() == closeButton.getId()) { + onPlaybackShutdown(); + return; } if (getCurrentState() != STATE_COMPLETED) { @@ -684,6 +698,13 @@ public final class MainVideoPlayer extends AppCompatActivity showControls(DEFAULT_CONTROLS_DURATION); } + private void onShareClicked() { + // share video at the current time (youtube.com/watch?v=ID&t=SECONDS) + ShareUtils.shareUrl(MainVideoPlayer.this, + playerImpl.getVideoTitle(), + playerImpl.getVideoUrl() + "&t=" + String.valueOf(playerImpl.getPlaybackSeekBar().getProgress()/1000)); + } + private void onScreenRotationClicked() { if (DEBUG) Log.d(TAG, "onScreenRotationClicked() called"); toggleOrientation(); @@ -766,6 +787,7 @@ public final class MainVideoPlayer extends AppCompatActivity super.onBlocked(); playPauseButton.setImageResource(R.drawable.ic_pause_white); animatePlayButtons(false, 100); + animateView(closeButton, false, DEFAULT_CONTROLS_DURATION); getRootView().setKeepScreenOn(true); } @@ -781,6 +803,7 @@ public final class MainVideoPlayer extends AppCompatActivity animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 80, 0, () -> { playPauseButton.setImageResource(R.drawable.ic_pause_white); animatePlayButtons(true, 200); + animateView(closeButton, false, DEFAULT_CONTROLS_DURATION); }); getRootView().setKeepScreenOn(true); @@ -792,6 +815,7 @@ public final class MainVideoPlayer extends AppCompatActivity animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 80, 0, () -> { playPauseButton.setImageResource(R.drawable.ic_play_arrow_white); animatePlayButtons(true, 200); + animateView(closeButton, false, DEFAULT_CONTROLS_DURATION); }); showSystemUi(); @@ -811,8 +835,8 @@ public final class MainVideoPlayer extends AppCompatActivity animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 0, 0, () -> { playPauseButton.setImageResource(R.drawable.ic_replay_white); animatePlayButtons(true, DEFAULT_CONTROLS_DURATION); + animateView(closeButton, true, DEFAULT_CONTROLS_DURATION); }); - getRootView().setKeepScreenOn(false); super.onCompleted(); } @@ -847,8 +871,8 @@ public final class MainVideoPlayer extends AppCompatActivity if (DEBUG) Log.d(TAG, "hideControls() called with: delay = [" + delay + "]"); getControlsVisibilityHandler().removeCallbacksAndMessages(null); getControlsVisibilityHandler().postDelayed(() -> - animateView(getControlsRoot(), false, duration, 0, - MainVideoPlayer.this::hideSystemUi), + animateView(getControlsRoot(), false, duration, 0, + MainVideoPlayer.this::hideSystemUi), /*delayMillis=*/delay ); } @@ -1052,9 +1076,9 @@ public final class MainVideoPlayer extends AppCompatActivity final int resId = currentProgressPercent <= 0 ? R.drawable.ic_volume_off_white_72dp - : currentProgressPercent < 0.25 ? R.drawable.ic_volume_mute_white_72dp - : currentProgressPercent < 0.75 ? R.drawable.ic_volume_down_white_72dp - : R.drawable.ic_volume_up_white_72dp; + : currentProgressPercent < 0.25 ? R.drawable.ic_volume_mute_white_72dp + : currentProgressPercent < 0.75 ? R.drawable.ic_volume_down_white_72dp + : R.drawable.ic_volume_up_white_72dp; playerImpl.getVolumeImageView().setImageDrawable( AppCompatResources.getDrawable(getApplicationContext(), resId) @@ -1078,8 +1102,8 @@ public final class MainVideoPlayer extends AppCompatActivity final int resId = currentProgressPercent < 0.25 ? R.drawable.ic_brightness_low_white_72dp - : currentProgressPercent < 0.75 ? R.drawable.ic_brightness_medium_white_72dp - : R.drawable.ic_brightness_high_white_72dp; + : currentProgressPercent < 0.75 ? R.drawable.ic_brightness_medium_white_72dp + : R.drawable.ic_brightness_high_white_72dp; playerImpl.getBrightnessImageView().setImageDrawable( AppCompatResources.getDrawable(getApplicationContext(), resId) diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java index fb60ac473..4dbbc571d 100644 --- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java @@ -297,7 +297,6 @@ public abstract class VideoPlayer extends BasePlayer return true; }); - // Add all available captions for (int i = 0; i < availableLanguages.size(); i++) { final String captionLanguage = availableLanguages.get(i); MenuItem captionItem = captionPopupMenu.getMenu().add(captionPopupMenuGroupId, @@ -305,9 +304,9 @@ public abstract class VideoPlayer extends BasePlayer captionItem.setOnMenuItemClickListener(menuItem -> { final int textRendererIndex = getRendererIndex(C.TRACK_TYPE_TEXT); if (textRendererIndex != RENDERER_UNAVAILABLE) { + trackSelector.setPreferredTextLanguage(captionLanguage); trackSelector.setParameters(trackSelector.buildUponParameters() - .setRendererDisabled(textRendererIndex, false) - .setPreferredTextLanguage(captionLanguage)); + .setRendererDisabled(textRendererIndex, false)); } return true; }); @@ -315,7 +314,6 @@ public abstract class VideoPlayer extends BasePlayer captionPopupMenu.setOnDismissListener(this); } - private void updateStreamRelatedViews() { if (getCurrentMetadata() == null) return; diff --git a/app/src/main/java/org/schabi/newpipe/player/playback/CustomTrackSelector.java b/app/src/main/java/org/schabi/newpipe/player/playback/CustomTrackSelector.java new file mode 100644 index 000000000..063d6b93e --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/player/playback/CustomTrackSelector.java @@ -0,0 +1,115 @@ +package org.schabi.newpipe.player.playback; + +import android.support.annotation.NonNull; +import android.text.TextUtils; +import android.util.Pair; + +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.source.TrackGroup; +import com.google.android.exoplayer2.source.TrackGroupArray; +import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; +import com.google.android.exoplayer2.trackselection.FixedTrackSelection; +import com.google.android.exoplayer2.trackselection.TrackSelection; +import com.google.android.exoplayer2.util.Assertions; + +/** + * This class allows irregular text language labels for use when selecting text captions and + * is mostly a copy-paste from {@link DefaultTrackSelector}. + * + * This is a hack and should be removed once ExoPlayer fixes language normalization to accept + * a broader set of languages. + * */ +public class CustomTrackSelector extends DefaultTrackSelector { + private static final int WITHIN_RENDERER_CAPABILITIES_BONUS = 1000; + + private String preferredTextLanguage; + + public CustomTrackSelector(TrackSelection.Factory adaptiveTrackSelectionFactory) { + super(adaptiveTrackSelectionFactory); + } + + public String getPreferredTextLanguage() { + return preferredTextLanguage; + } + + public void setPreferredTextLanguage(@NonNull final String label) { + Assertions.checkNotNull(label); + if (!label.equals(preferredTextLanguage)) { + preferredTextLanguage = label; + invalidate(); + } + } + + /** @see DefaultTrackSelector#formatHasLanguage(Format, String)*/ + protected static boolean formatHasLanguage(Format format, String language) { + return language != null && TextUtils.equals(language, format.language); + } + + /** @see DefaultTrackSelector#formatHasNoLanguage(Format)*/ + protected static boolean formatHasNoLanguage(Format format) { + return TextUtils.isEmpty(format.language) || formatHasLanguage(format, C.LANGUAGE_UNDETERMINED); + } + + /** @see DefaultTrackSelector#selectTextTrack(TrackGroupArray, int[][], Parameters) */ + @Override + protected Pair selectTextTrack(TrackGroupArray groups, int[][] formatSupport, + Parameters params) { + TrackGroup selectedGroup = null; + int selectedTrackIndex = 0; + int selectedTrackScore = 0; + for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) { + TrackGroup trackGroup = groups.get(groupIndex); + int[] trackFormatSupport = formatSupport[groupIndex]; + for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) { + if (isSupported(trackFormatSupport[trackIndex], + params.exceedRendererCapabilitiesIfNecessary)) { + Format format = trackGroup.getFormat(trackIndex); + int maskedSelectionFlags = + format.selectionFlags & ~params.disabledTextTrackSelectionFlags; + boolean isDefault = (maskedSelectionFlags & C.SELECTION_FLAG_DEFAULT) != 0; + boolean isForced = (maskedSelectionFlags & C.SELECTION_FLAG_FORCED) != 0; + int trackScore; + boolean preferredLanguageFound = formatHasLanguage(format, preferredTextLanguage); + if (preferredLanguageFound + || (params.selectUndeterminedTextLanguage && formatHasNoLanguage(format))) { + if (isDefault) { + trackScore = 8; + } else if (!isForced) { + // Prefer non-forced to forced if a preferred text language has been specified. Where + // both are provided the non-forced track will usually contain the forced subtitles as + // a subset. + trackScore = 6; + } else { + trackScore = 4; + } + trackScore += preferredLanguageFound ? 1 : 0; + } else if (isDefault) { + trackScore = 3; + } else if (isForced) { + if (formatHasLanguage(format, params.preferredAudioLanguage)) { + trackScore = 2; + } else { + trackScore = 1; + } + } else { + // Track should not be selected. + continue; + } + if (isSupported(trackFormatSupport[trackIndex], false)) { + trackScore += WITHIN_RENDERER_CAPABILITIES_BONUS; + } + if (trackScore > selectedTrackScore) { + selectedGroup = trackGroup; + selectedTrackIndex = trackIndex; + selectedTrackScore = trackScore; + } + } + } + } + return selectedGroup == null + ? null + : Pair.create( + new FixedTrackSelection(selectedGroup, selectedTrackIndex), selectedTrackScore); + } +} \ No newline at end of file diff --git a/app/src/main/java/org/schabi/newpipe/player/playqueue/SinglePlayQueue.java b/app/src/main/java/org/schabi/newpipe/player/playqueue/SinglePlayQueue.java index 5993481e2..40d1a11e7 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playqueue/SinglePlayQueue.java +++ b/app/src/main/java/org/schabi/newpipe/player/playqueue/SinglePlayQueue.java @@ -16,6 +16,11 @@ public final class SinglePlayQueue extends PlayQueue { super(0, Collections.singletonList(new PlayQueueItem(info))); } + public SinglePlayQueue(final StreamInfo info, final long startPosition) { + super(0, Collections.singletonList(new PlayQueueItem(info))); + getItem().setRecoveryPosition(startPosition); + } + public SinglePlayQueue(final List items, final int index) { super(index, playQueueItemsOf(items)); } diff --git a/app/src/main/java/org/schabi/newpipe/util/CommentTextOnTouchListener.java b/app/src/main/java/org/schabi/newpipe/util/CommentTextOnTouchListener.java index 570b5f8b2..e1ecc662d 100644 --- a/app/src/main/java/org/schabi/newpipe/util/CommentTextOnTouchListener.java +++ b/app/src/main/java/org/schabi/newpipe/util/CommentTextOnTouchListener.java @@ -31,7 +31,7 @@ public class CommentTextOnTouchListener implements View.OnTouchListener { public static final CommentTextOnTouchListener INSTANCE = new CommentTextOnTouchListener(); - private static final Pattern timestampPattern = Pattern.compile(".*&t=(\\d+)"); + private static final Pattern timestampPattern = Pattern.compile("(.*)#timestamp=(\\d+)"); @Override public boolean onTouch(View v, MotionEvent event) { @@ -86,6 +86,12 @@ public class CommentTextOnTouchListener implements View.OnTouchListener { private boolean handleUrl(Context context, URLSpan urlSpan) { String url = urlSpan.getURL(); + int seconds = -1; + Matcher matcher = timestampPattern.matcher(url); + if(matcher.matches()){ + url = matcher.group(1); + seconds = Integer.parseInt(matcher.group(2)); + } StreamingService service; StreamingService.LinkType linkType; try { @@ -97,9 +103,7 @@ public class CommentTextOnTouchListener implements View.OnTouchListener { if(linkType == StreamingService.LinkType.NONE){ return false; } - Matcher matcher = timestampPattern.matcher(url); - if(linkType == StreamingService.LinkType.STREAM && matcher.matches()){ - int seconds = Integer.parseInt(matcher.group(1)); + if(linkType == StreamingService.LinkType.STREAM && seconds != -1){ return playOnPopup(context, url, service, seconds); }else{ NavigationHelper.openRouterActivity(context, url); @@ -119,9 +123,8 @@ public class CommentTextOnTouchListener implements View.OnTouchListener { single.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(info -> { - PlayQueue playQueue = new SinglePlayQueue((StreamInfo) info); - ((StreamInfo) info).setStartPosition(seconds); - NavigationHelper.enqueueOnPopupPlayer(context, playQueue, true); + PlayQueue playQueue = new SinglePlayQueue((StreamInfo) info, seconds*1000); + NavigationHelper.playOnPopupPlayer(context, playQueue); }); return true; } diff --git a/app/src/main/java/org/schabi/newpipe/util/ShareUtils.java b/app/src/main/java/org/schabi/newpipe/util/ShareUtils.java new file mode 100644 index 000000000..c5c78a726 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/util/ShareUtils.java @@ -0,0 +1,22 @@ +package org.schabi.newpipe.util; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; + +import org.schabi.newpipe.R; + +public class ShareUtils { + public static void openUrlInBrowser(Context context, String url) { + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + context.startActivity(Intent.createChooser(intent, context.getString(R.string.share_dialog_title))); + } + + public static void shareUrl(Context context, String subject, String url) { + Intent intent = new Intent(Intent.ACTION_SEND); + intent.setType("text/plain"); + intent.putExtra(Intent.EXTRA_SUBJECT, subject); + intent.putExtra(Intent.EXTRA_TEXT, url); + context.startActivity(Intent.createChooser(intent, context.getString(R.string.share_dialog_title))); + } +} diff --git a/app/src/main/res/layout-large-land/activity_main_player.xml b/app/src/main/res/layout-large-land/activity_main_player.xml index 5f484267b..8428d489a 100644 --- a/app/src/main/res/layout-large-land/activity_main_player.xml +++ b/app/src/main/res/layout-large-land/activity_main_player.xml @@ -249,7 +249,7 @@ android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_marginLeft="2dp" - android:padding="5dp" + android:padding="5dp" android:clickable="true" android:focusable="true" android:scaleType="fitXY" @@ -305,7 +305,7 @@ tools:text="English" /> + + +