mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2024-12-04 01:55:16 +01:00
commit
03167a1e9c
4
.github/CONTRIBUTING.md
vendored
4
.github/CONTRIBUTING.md
vendored
@ -42,10 +42,6 @@ You'll see *exactly* what is sent, be able to add **your comments**, and then se
|
||||
* Create PRs that cover only **one specific issue/solution/bug**. Do not create PRs that are huge monoliths and could have been split into multiple independent contributions.
|
||||
* NewPipe uses [NewPipeExtractor](https://github.com/TeamNewPipe/NewPipeExtractor) to fetch data from services. If you need to change something there, you must test your changes in NewPipe. Telling NewPipe to use your extractor version can be accomplished by editing the `app/build.gradle` file: the comments under the "NewPipe libraries" section of `dependencies` will help you out.
|
||||
|
||||
### Kotlin in NewPipe
|
||||
* NewPipe will remain mostly Java for time being
|
||||
* Contributions containing a simple conversion from Java to Kotlin should be avoided. Conversions to Kotlin should only be done if Kotlin actually brings improvements like bug fixes or better performance which are not, or only with much more effort, implementable in Java. The core team sees Java as an easier to learn and generally well adopted programming language.
|
||||
|
||||
### Creating a Pull Request (PR)
|
||||
|
||||
* Make changes on a **separate branch** with a meaningful name, not on the _master_ branch or the _dev_ branch. This is commonly known as *feature branch workflow*. You may then send your changes as a pull request (PR) on GitHub.
|
||||
|
1
.github/workflows/ci.yml
vendored
1
.github/workflows/ci.yml
vendored
@ -6,6 +6,7 @@ on:
|
||||
branches:
|
||||
- dev
|
||||
- master
|
||||
- refactor
|
||||
- release**
|
||||
paths-ignore:
|
||||
- 'README.md'
|
||||
|
@ -287,7 +287,7 @@ dependencies {
|
||||
implementation "com.jakewharton.rxbinding4:rxbinding:4.0.0"
|
||||
|
||||
// Date and time formatting
|
||||
implementation "org.ocpsoft.prettytime:prettytime:5.0.7.Final"
|
||||
implementation "org.ocpsoft.prettytime:prettytime:5.0.8.Final"
|
||||
|
||||
// Jetpack Compose
|
||||
implementation(platform('androidx.compose:compose-bom:2024.02.01'))
|
||||
|
@ -367,6 +367,7 @@
|
||||
<data android:host="tilvids.com" />
|
||||
<data android:host="video.lqdn.fr" />
|
||||
<data android:host="video.ploud.fr" />
|
||||
<data android:host="subscribeto.me" />
|
||||
|
||||
<data android:pathPrefix="/videos/" /> <!-- it contains playlists -->
|
||||
<data android:pathPrefix="/w/" /> <!-- short video URLs -->
|
||||
|
@ -22,6 +22,7 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
import androidx.core.view.MenuProvider;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
@ -99,6 +100,7 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo>
|
||||
private MenuItem menuRssButton;
|
||||
private MenuItem menuNotifyButton;
|
||||
private SubscriptionEntity channelSubscription;
|
||||
private MenuProvider menuProvider;
|
||||
|
||||
public static ChannelFragment getInstance(final int serviceId, final String url,
|
||||
final String name) {
|
||||
@ -121,7 +123,62 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo>
|
||||
@Override
|
||||
public void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
menuProvider = new MenuProvider() {
|
||||
@Override
|
||||
public void onCreateMenu(@NonNull final Menu menu,
|
||||
@NonNull final MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.menu_channel, menu);
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onCreateOptionsMenu() called with: "
|
||||
+ "menu = [" + menu + "], inflater = [" + inflater + "]");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepareMenu(@NonNull final Menu menu) {
|
||||
menuRssButton = menu.findItem(R.id.menu_item_rss);
|
||||
menuNotifyButton = menu.findItem(R.id.menu_item_notify);
|
||||
updateRssButton();
|
||||
updateNotifyButton(channelSubscription);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemSelected(@NonNull final MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_item_notify:
|
||||
final boolean value = !item.isChecked();
|
||||
item.setEnabled(false);
|
||||
setNotify(value);
|
||||
break;
|
||||
case R.id.action_settings:
|
||||
NavigationHelper.openSettings(requireContext());
|
||||
break;
|
||||
case R.id.menu_item_rss:
|
||||
if (currentInfo != null) {
|
||||
ShareUtils.openUrlInApp(requireContext(), currentInfo.getFeedUrl());
|
||||
}
|
||||
break;
|
||||
case R.id.menu_item_openInBrowser:
|
||||
if (currentInfo != null) {
|
||||
ShareUtils.openUrlInBrowser(requireContext(),
|
||||
currentInfo.getOriginalUrl());
|
||||
}
|
||||
break;
|
||||
case R.id.menu_item_share:
|
||||
if (currentInfo != null) {
|
||||
ShareUtils.shareText(requireContext(), name,
|
||||
currentInfo.getOriginalUrl(), currentInfo.getAvatars());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
activity.addMenuProvider(menuProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -183,67 +240,10 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo>
|
||||
}
|
||||
disposables.clear();
|
||||
binding = null;
|
||||
activity.removeMenuProvider(menuProvider);
|
||||
menuProvider = null;
|
||||
}
|
||||
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Menu
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(@NonNull final Menu menu,
|
||||
@NonNull final MenuInflater inflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
inflater.inflate(R.menu.menu_channel, menu);
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onCreateOptionsMenu() called with: "
|
||||
+ "menu = [" + menu + "], inflater = [" + inflater + "]");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepareOptionsMenu(@NonNull final Menu menu) {
|
||||
super.onPrepareOptionsMenu(menu);
|
||||
menuRssButton = menu.findItem(R.id.menu_item_rss);
|
||||
menuNotifyButton = menu.findItem(R.id.menu_item_notify);
|
||||
updateNotifyButton(channelSubscription);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_item_notify:
|
||||
final boolean value = !item.isChecked();
|
||||
item.setEnabled(false);
|
||||
setNotify(value);
|
||||
break;
|
||||
case R.id.action_settings:
|
||||
NavigationHelper.openSettings(requireContext());
|
||||
break;
|
||||
case R.id.menu_item_rss:
|
||||
if (currentInfo != null) {
|
||||
ShareUtils.openUrlInApp(requireContext(), currentInfo.getFeedUrl());
|
||||
}
|
||||
break;
|
||||
case R.id.menu_item_openInBrowser:
|
||||
if (currentInfo != null) {
|
||||
ShareUtils.openUrlInBrowser(requireContext(), currentInfo.getOriginalUrl());
|
||||
}
|
||||
break;
|
||||
case R.id.menu_item_share:
|
||||
if (currentInfo != null) {
|
||||
ShareUtils.shareText(requireContext(), name, currentInfo.getOriginalUrl(),
|
||||
currentInfo.getAvatars());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Channel Subscription
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
@ -408,6 +408,13 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo>
|
||||
animate(binding.channelSubscribeButton, true, 100, AnimationType.LIGHT_SCALE_AND_ALPHA);
|
||||
}
|
||||
|
||||
private void updateRssButton() {
|
||||
if (menuRssButton == null || currentInfo == null) {
|
||||
return;
|
||||
}
|
||||
menuRssButton.setVisible(!TextUtils.isEmpty(currentInfo.getFeedUrl()));
|
||||
}
|
||||
|
||||
private void updateNotifyButton(@Nullable final SubscriptionEntity subscription) {
|
||||
if (menuNotifyButton == null) {
|
||||
return;
|
||||
@ -610,9 +617,7 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo>
|
||||
binding.subChannelAvatarView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
if (menuRssButton != null) {
|
||||
menuRssButton.setVisible(!TextUtils.isEmpty(result.getFeedUrl()));
|
||||
}
|
||||
updateRssButton();
|
||||
|
||||
channelContentNotSupported = false;
|
||||
for (final Throwable throwable : result.getErrors()) {
|
||||
|
@ -352,6 +352,7 @@ public class PlaylistFragment extends BaseListInfoFragment<StreamInfoItem, Playl
|
||||
});
|
||||
ellipsizer.setContent(description);
|
||||
headerBinding.playlistDescriptionReadMore.setOnClickListener(v -> ellipsizer.toggle());
|
||||
headerBinding.playlistDescription.setOnClickListener(v -> ellipsizer.toggle());
|
||||
} else {
|
||||
headerBinding.playlistDescription.setVisibility(View.GONE);
|
||||
headerBinding.playlistDescriptionReadMore.setVisibility(View.GONE);
|
||||
|
@ -1,9 +1,13 @@
|
||||
package org.schabi.newpipe.info_list.holder;
|
||||
|
||||
import static org.schabi.newpipe.util.ServiceHelper.getServiceById;
|
||||
import static org.schabi.newpipe.util.text.TouchUtils.getOffsetForHorizontalLine;
|
||||
|
||||
import android.text.Spanned;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.text.style.URLSpan;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
@ -25,7 +29,6 @@ import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.external_communication.ShareUtils;
|
||||
import org.schabi.newpipe.util.image.ImageStrategy;
|
||||
import org.schabi.newpipe.util.image.PicassoHelper;
|
||||
import org.schabi.newpipe.util.text.CommentTextOnTouchListener;
|
||||
import org.schabi.newpipe.util.text.TextEllipsizer;
|
||||
|
||||
public class CommentInfoItemHolder extends InfoItemHolder {
|
||||
@ -128,7 +131,26 @@ public class CommentInfoItemHolder extends InfoItemHolder {
|
||||
textEllipsizer.ellipsize();
|
||||
|
||||
//noinspection ClickableViewAccessibility
|
||||
itemContentView.setOnTouchListener(CommentTextOnTouchListener.INSTANCE);
|
||||
itemContentView.setOnTouchListener((v, event) -> {
|
||||
final CharSequence text = itemContentView.getText();
|
||||
if (text instanceof Spanned buffer) {
|
||||
final int action = event.getAction();
|
||||
|
||||
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) {
|
||||
final int offset = getOffsetForHorizontalLine(itemContentView, event);
|
||||
final var links = buffer.getSpans(offset, offset, ClickableSpan.class);
|
||||
|
||||
if (links.length != 0) {
|
||||
if (action == MotionEvent.ACTION_UP) {
|
||||
links[0].onClick(itemContentView);
|
||||
}
|
||||
// we handle events that intersect links, so return true
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
itemView.setOnClickListener(view -> {
|
||||
textEllipsizer.toggle();
|
||||
|
@ -1,42 +0,0 @@
|
||||
package org.schabi.newpipe.util.text;
|
||||
|
||||
import static org.schabi.newpipe.util.text.TouchUtils.getOffsetForHorizontalLine;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class CommentTextOnTouchListener implements View.OnTouchListener {
|
||||
public static final CommentTextOnTouchListener INSTANCE = new CommentTextOnTouchListener();
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public boolean onTouch(final View v, final MotionEvent event) {
|
||||
if (!(v instanceof TextView)) {
|
||||
return false;
|
||||
}
|
||||
final TextView widget = (TextView) v;
|
||||
final CharSequence text = widget.getText();
|
||||
if (text instanceof Spanned) {
|
||||
final Spanned buffer = (Spanned) text;
|
||||
final int action = event.getAction();
|
||||
|
||||
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) {
|
||||
final int offset = getOffsetForHorizontalLine(widget, event);
|
||||
final ClickableSpan[] links = buffer.getSpans(offset, offset, ClickableSpan.class);
|
||||
|
||||
if (links.length != 0) {
|
||||
if (action == MotionEvent.ACTION_UP) {
|
||||
links[0].onClick(widget);
|
||||
}
|
||||
// we handle events that intersect links, so return true
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user