handling timestamp links in comments

This commit is contained in:
Ritvik Saraf 2019-03-02 05:12:06 +05:30
parent c0004e988a
commit 67d2b9131e
4 changed files with 114 additions and 13 deletions

View file

@ -36,7 +36,6 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo; import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.VideoStream; import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.player.helper.PlayerHelper;
import org.schabi.newpipe.player.playqueue.ChannelPlayQueue; import org.schabi.newpipe.player.playqueue.ChannelPlayQueue;
import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue; import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue;
@ -81,10 +80,13 @@ public class RouterActivity extends AppCompatActivity {
protected int selectedPreviously = -1; protected int selectedPreviously = -1;
protected String currentUrl; protected String currentUrl;
protected boolean internalRoute = false;
protected final CompositeDisposable disposables = new CompositeDisposable(); protected final CompositeDisposable disposables = new CompositeDisposable();
private boolean selectionIsDownload = false; private boolean selectionIsDownload = false;
public static final String internalRouteKey = "internalRoute";
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -99,6 +101,8 @@ public class RouterActivity extends AppCompatActivity {
} }
} }
internalRoute = getIntent().getBooleanExtra(internalRouteKey, false);
setTheme(ThemeHelper.isLightThemeSelected(this) setTheme(ThemeHelper.isLightThemeSelected(this)
? R.style.RouterActivityThemeLight : R.style.RouterActivityThemeDark); ? R.style.RouterActivityThemeLight : R.style.RouterActivityThemeDark);
} }
@ -383,8 +387,10 @@ public class RouterActivity extends AppCompatActivity {
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(intent -> { .subscribe(intent -> {
if(!internalRoute){
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
}
startActivity(intent); startActivity(intent);
finish(); finish();

View file

@ -15,6 +15,9 @@ import org.schabi.newpipe.util.CommentTextOnTouchListener;
import org.schabi.newpipe.util.ImageDisplayConstants; import org.schabi.newpipe.util.ImageDisplayConstants;
import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.NavigationHelper;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import de.hdodenhof.circleimageview.CircleImageView; import de.hdodenhof.circleimageview.CircleImageView;
public class CommentsMiniInfoItemHolder extends InfoItemHolder { public class CommentsMiniInfoItemHolder extends InfoItemHolder {
@ -28,7 +31,23 @@ public class CommentsMiniInfoItemHolder extends InfoItemHolder {
private static final int commentExpandedLines = 1000; private static final int commentExpandedLines = 1000;
private String commentText; private String commentText;
private boolean containsLinks = false; private String streamUrl;
private static final Pattern pattern = Pattern.compile("(\\d+:)?(\\d+)?:(\\d+)");
private final Linkify.TransformFilter timestampLink = new Linkify.TransformFilter() {
@Override
public String transformUrl(Matcher match, String url) {
int timestamp = 0;
String hours = match.group(1);
String minutes = match.group(2);
String seconds = match.group(3);
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));
}
};
CommentsMiniInfoItemHolder(InfoItemBuilder infoItemBuilder, int layoutId, ViewGroup parent) { CommentsMiniInfoItemHolder(InfoItemBuilder infoItemBuilder, int layoutId, ViewGroup parent) {
super(infoItemBuilder, layoutId, parent); super(infoItemBuilder, layoutId, parent);
@ -70,10 +89,12 @@ public class CommentsMiniInfoItemHolder extends InfoItemHolder {
} }
}); });
streamUrl = item.getUrl();
itemContentView.setMaxLines(commentDefaultLines); itemContentView.setMaxLines(commentDefaultLines);
commentText = item.getCommentText(); commentText = item.getCommentText();
itemContentView.setText(commentText); itemContentView.setText(commentText);
containsLinks = linkify(); linkify();
itemContentView.setOnTouchListener(CommentTextOnTouchListener.INSTANCE); itemContentView.setOnTouchListener(CommentTextOnTouchListener.INSTANCE);
if(itemContentView.getLineCount() == 0){ if(itemContentView.getLineCount() == 0){
@ -100,7 +121,7 @@ public class CommentsMiniInfoItemHolder extends InfoItemHolder {
int endOfLastLine = itemContentView.getLayout().getLineEnd(commentDefaultLines - 1); int endOfLastLine = itemContentView.getLayout().getLineEnd(commentDefaultLines - 1);
String newVal = itemContentView.getText().subSequence(0, endOfLastLine - 3) + "..."; String newVal = itemContentView.getText().subSequence(0, endOfLastLine - 3) + "...";
itemContentView.setText(newVal); itemContentView.setText(newVal);
if(containsLinks) linkify(); linkify();
} }
} }
@ -115,12 +136,12 @@ public class CommentsMiniInfoItemHolder extends InfoItemHolder {
private void expand() { private void expand() {
itemContentView.setMaxLines(commentExpandedLines); itemContentView.setMaxLines(commentExpandedLines);
itemContentView.setText(commentText); itemContentView.setText(commentText);
if(containsLinks) linkify(); linkify();
} }
private boolean linkify(){ private void linkify(){
boolean res = Linkify.addLinks(itemContentView, Linkify.WEB_URLS); Linkify.addLinks(itemContentView, Linkify.WEB_URLS);
Linkify.addLinks(itemContentView, pattern, null, null, timestampLink);
itemContentView.setMovementMethod(null); itemContentView.setMovementMethod(null);
return res;
} }
} }

View file

@ -1,18 +1,38 @@
package org.schabi.newpipe.util; package org.schabi.newpipe.util;
import android.content.Context;
import android.text.Layout; import android.text.Layout;
import android.text.Selection; import android.text.Selection;
import android.text.Spannable; import android.text.Spannable;
import android.text.Spanned; import android.text.Spanned;
import android.text.style.ClickableSpan; import android.text.style.ClickableSpan;
import android.text.style.URLSpan;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
public class CommentTextOnTouchListener implements View.OnTouchListener { public class CommentTextOnTouchListener implements View.OnTouchListener {
public static final CommentTextOnTouchListener INSTANCE = new CommentTextOnTouchListener(); public static final CommentTextOnTouchListener INSTANCE = new CommentTextOnTouchListener();
private static final Pattern timestampPattern = Pattern.compile(".*&t=(\\d+)");
@Override @Override
public boolean onTouch(View v, MotionEvent event) { public boolean onTouch(View v, MotionEvent event) {
if(!(v instanceof TextView)){ if(!(v instanceof TextView)){
@ -45,7 +65,11 @@ public class CommentTextOnTouchListener implements View.OnTouchListener {
if (link.length != 0) { if (link.length != 0) {
if (action == MotionEvent.ACTION_UP) { if (action == MotionEvent.ACTION_UP) {
link[0].onClick(widget); boolean handled = false;
if(link[0] instanceof URLSpan){
handled = handleUrl(v.getContext(), (URLSpan) link[0]);
}
if(!handled) link[0].onClick(widget);
} else if (action == MotionEvent.ACTION_DOWN) { } else if (action == MotionEvent.ACTION_DOWN) {
Selection.setSelection(buffer, Selection.setSelection(buffer,
buffer.getSpanStart(link[0]), buffer.getSpanStart(link[0]),
@ -59,4 +83,46 @@ public class CommentTextOnTouchListener implements View.OnTouchListener {
return false; return false;
} }
private boolean handleUrl(Context context, URLSpan urlSpan) {
String url = urlSpan.getURL();
StreamingService service;
StreamingService.LinkType linkType;
try {
service = NewPipe.getServiceByUrl(url);
linkType = service.getLinkTypeByUrl(url);
} catch (ExtractionException e) {
return false;
}
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));
return playOnPopup(context, url, service, seconds);
}else{
NavigationHelper.openRouterActivity(context, url);
return true;
}
}
private boolean playOnPopup(Context context, String url, StreamingService service, int seconds) {
LinkHandlerFactory factory = service.getStreamLHFactory();
String cleanUrl = null;
try {
cleanUrl = factory.getUrl(factory.getId(url));
} catch (ParsingException e) {
return false;
}
Single single = ExtractorHelper.getStreamInfo(service.getServiceId(), cleanUrl, false);
single.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(info -> {
PlayQueue playQueue = new SinglePlayQueue((StreamInfo) info);
((StreamInfo) info).setStartPosition(seconds);
NavigationHelper.enqueueOnPopupPlayer(context, playQueue, true);
});
return true;
}
} }

View file

@ -21,6 +21,7 @@ import com.nostra13.universalimageloader.core.ImageLoader;
import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.RouterActivity;
import org.schabi.newpipe.about.AboutActivity; import org.schabi.newpipe.about.AboutActivity;
import org.schabi.newpipe.download.DownloadActivity; import org.schabi.newpipe.download.DownloadActivity;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
@ -34,11 +35,11 @@ import org.schabi.newpipe.fragments.MainFragment;
import org.schabi.newpipe.fragments.detail.VideoDetailFragment; import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
import org.schabi.newpipe.fragments.list.channel.ChannelFragment; import org.schabi.newpipe.fragments.list.channel.ChannelFragment;
import org.schabi.newpipe.fragments.list.comments.CommentsFragment; import org.schabi.newpipe.fragments.list.comments.CommentsFragment;
import org.schabi.newpipe.local.bookmark.BookmarkFragment;
import org.schabi.newpipe.local.feed.FeedFragment;
import org.schabi.newpipe.fragments.list.kiosk.KioskFragment; import org.schabi.newpipe.fragments.list.kiosk.KioskFragment;
import org.schabi.newpipe.fragments.list.playlist.PlaylistFragment; import org.schabi.newpipe.fragments.list.playlist.PlaylistFragment;
import org.schabi.newpipe.fragments.list.search.SearchFragment; import org.schabi.newpipe.fragments.list.search.SearchFragment;
import org.schabi.newpipe.local.bookmark.BookmarkFragment;
import org.schabi.newpipe.local.feed.FeedFragment;
import org.schabi.newpipe.local.history.StatisticsPlaylistFragment; import org.schabi.newpipe.local.history.StatisticsPlaylistFragment;
import org.schabi.newpipe.local.playlist.LocalPlaylistFragment; import org.schabi.newpipe.local.playlist.LocalPlaylistFragment;
import org.schabi.newpipe.local.subscription.SubscriptionFragment; import org.schabi.newpipe.local.subscription.SubscriptionFragment;
@ -422,6 +423,13 @@ public class NavigationHelper {
context.startActivity(mIntent); context.startActivity(mIntent);
} }
public static void openRouterActivity(Context context, String url) {
Intent mIntent = new Intent(context, RouterActivity.class);
mIntent.setData(Uri.parse(url));
mIntent.putExtra(RouterActivity.internalRouteKey, true);
context.startActivity(mIntent);
}
public static void openAbout(Context context) { public static void openAbout(Context context) {
Intent intent = new Intent(context, AboutActivity.class); Intent intent = new Intent(context, AboutActivity.class);
context.startActivity(intent); context.startActivity(intent);