1
mirror of https://github.com/TeamNewPipe/NewPipe synced 2025-09-18 12:40:51 +02:00

Compare commits

...

243 Commits
v0.27.7 ... dev

Author SHA1 Message Date
Tobi
eed09f8a1d Merge pull request #12550 from whistlingwoods/fix-downloads-lost-progress
Try to recover pending download missions when possible
2025-09-16 23:26:56 -07:00
Stypox
fd3f030d0b Merge pull request #12616 from Isira-Seneviratne/Bump-AGP 2025-09-10 09:18:32 +02:00
Profpatsch
45c22c0db8 Merge pull request #12615 from Isira-Seneviratne/Player-intent-refactor
Refactor player intent logic
2025-09-09 10:24:42 +02:00
Isira Seneviratne
2b7c72eb69 Update AGP to 8.13.0 2025-09-08 08:08:07 +05:30
Isira Seneviratne
89c4eb5237 Refactor player intent logic 2025-09-08 07:56:13 +05:30
Stypox
803aba4935 Merge pull request #12254 from TeamNewPipe/timestamp-keep-current-player 2025-09-06 17:51:36 +02:00
Profpatsch
1723bf0e8a Player/handleIntent: keep current player when clicking timestamp
This was always a bit weird, that clicking a timestamp would
unconditionally switch to the popup player.

With the new enum, it’s trivial to change it to always stay at the
selected player now ;)
2025-09-06 17:40:18 +02:00
whistlingwoods
21e24c9e34 Apply review suggestions 2025-09-06 19:14:15 +05:30
Profpatsch
eb277fe14b Player/handleIntent: call handleIntentPost unconditionally
We always need to handleIntentPost otherwise the VideoDetailFragment
is not setup correctly.
2025-09-06 15:31:14 +02:00
Profpatsch
d77771a60c Player/handleIntent: fix enqueue if player not running
In 063dcd41e5 I falsely claimed that the
fallthrough case is always degenerate, but it kinda somehow still
worked because if you long-click on e.g. the popup button, it would
call enqueue, but if nothing was running yet it would fallthrough to
the very last case and start the player with the video.

So let’s return to that and add a TODO for further refactoring in the
future.
2025-09-06 15:09:11 +02:00
Profpatsch
01f9a3de33 Fix Checkstyle & remove unused fields 2025-09-06 15:09:11 +02:00
Profpatsch
150649aea9 Player/handleIntent: Don’t delete queue when clicking on timestamp
Fixes https://github.com/TeamNewPipe/NewPipe/issues/11013

We finally are at the point where we can have good logic around
clicking on timestamps.

This is pretty straightforward:

1) if we are already playing the stream (usual case), we skip to the
   correct second directly
2) If we don’t have a queue yet, create a trivial one with the stream
3) If we have a queue, we insert the video as next item and start
  playing it.

The skipping logic in 1) is similar to the one further down in the old
optimization block, but will always correctly fire for timestamps now.
I copied it because it’s not quite the same code, and moving into a
separate method at this stage would complicate the code too much.
2025-09-06 15:09:11 +02:00
Profpatsch
3803d49489 Player/handleIntent: separate out the timestamp request into enum
Instead of implicitely reconstructing whether the intent was
intended (lol) to be a timestamp change, we create a new kind of
intent that *only* sets the data we need to switch to a new timestamp.

This means that the logic of what to do (opening a popup player) gets
moved from `InternalUrlsHandler.playOnPopup` to the
`Player.handleIntent` method, we only pass that we want to jump to a
new timestamp. Thus, the stream is now loaded *after* sending the
intent instead of before sending.

This is somewhat messy right now and still does not fix the issue of
queue deletion, but from now on the queue logic should get more
straightforward to implement.

In the end, everything should be a giant switch. Thus we don’t
fall-through anymore, but run the post-setup code manually by calling
`handeIntentPost` and then returning.
2025-09-06 15:06:53 +02:00
Profpatsch
25a4a9a253 Player/handleIntent: move prefs parameters into initPlayback
They are just read from the player preferences and don’t influence the
branching, no need to read them in the intent parsing logic.
2025-09-06 15:04:06 +02:00
Profpatsch
d534946550 Player: inline repeat mode cycling 2025-09-06 15:04:06 +02:00
Profpatsch
8fb3e90fe1 Player: remove unused REPEAT_MODE intent key 2025-09-06 15:04:06 +02:00
Profpatsch
5750ef6aa8 Player/handleIntent: start converting intent data to enum
The goal here is to convert all player intents to use a single enum
with extra data for each case. The queue ones are pretty easy, they
don’t carry any extra data. We fall through for everything else for
now.
2025-09-06 15:04:06 +02:00
Profpatsch
ab7d1377e5 Player/handleIntent: always early return on ENQUEUE an ENQUEUE_NEXT
We can do this, because:

1. if `playQueue` is not null, we return early
2. if `playQueue` is null and we need to enqueue:
  - the only “proper” case that could be triggered is
    the `RESUME_PLAYBACK` case, which is never `true` for the queuing
    intents, see the comment in `NavigationHelper.enqueueOnPlayer`
  - the generic `else` case is degenerate, because it would crash on
  `playQueue` being `null`.

This makes some sense, because there is no way to trigger the
enqueueing logic via the UI currently if there is no video playing
yet, in which case `playQueue` is not `null`.

So we need to transform this whole if desaster into a big switch.
2025-09-06 15:04:06 +02:00
Profpatsch
fd24c08529 Player/handleIntent: de morgan samePlayQueue
Okay, so this is the … only? branch in this if-chain that will
conditionally fire if `playQueue` *is* `null`, sometimes.

This is why the unconditional `initPlayback` in `else` is not passed a
`null` in many cases … because `RESUME_PLAYBACK` is `true` and
`playQueue` is `null`.

It’s gonna be hard to figure out which parts of that are intentional,
I say.
2025-09-06 15:04:06 +02:00
Profpatsch
e14ec3a4f9 NavigationHelper: inline trivial getPlayerIntent use 2025-09-06 15:04:06 +02:00
Profpatsch
b592403a66 NavigationHelper: push out resumePlayback one layer 2025-09-06 15:04:06 +02:00
Profpatsch
90e1ac56ef NavigationHelper: inline getPlayerEnqueueIntent
Funnily enough, I’m pretty sure that whole comment will be not
necessary, because we never check `resumePlayback` on handling the
intent anyway.
2025-09-06 15:04:06 +02:00
Profpatsch
32eb3afe16 Player/handleIntent: a few comments 2025-09-06 15:04:06 +02:00
Profpatsch
35c7f2f5d1 Player: Remove unused IS_MUTED intent key
The only use of the key was removed in commit
2a2c82e73b
but the handling logic stayed around. So let’s get rid of it.
2025-09-05 16:57:27 +02:00
Stypox
8afb00d2f0 Merge pull request #12603 from Stypox/better-error-panel 2025-09-05 13:31:39 +02:00
Stypox
f27ec53c08 Even more centralized error handling in ErrorInfo 2025-09-05 12:39:16 +02:00
Stypox
a3ddd616f9 Merge pull request #12578 from Stypox/better-error-messages 2025-09-04 13:18:40 +02:00
Stypox
79980e2078 Address PR reviews 2025-09-04 13:17:45 +02:00
Isira Seneviratne
b204fad9d5 Merge pull request #12471 from Isira-Seneviratne/Fix-notifications
Fix foreground service issues
2025-09-01 05:05:47 +05:30
Isira Seneviratne
08f51abefb Added comments 2025-08-31 22:25:12 +05:30
Stypox
204df4c45a Fix test 2025-08-30 14:58:08 +02:00
Stypox
989c0cfd28 Fix REPORT in snackbar not opening ErrorActivity if MainActivity not shown
Bug caused by https://github.com/TeamNewPipe/NewPipe/pull/11789
2025-08-30 14:39:23 +02:00
Stypox
a369deeef4 Allow ErrorInfo messages with formatArgs
- ErrorInfo.getMessage() now returns an ErrorMessage instance that can be formatted into a string using a context (this allows the construction of an ErrorInfo to remain independent of a Context)
- now the service ID is used in ErrorInfo.getMessage() to customize some messages based on the currently selected service
- player HTTP invalid statuses are now included in the message
- building a custom error message for AccountTerminatedException was moved from ErrorPanelHelper to ErrorInfo
2025-08-30 14:36:27 +02:00
Stypox
1bde2dcd9f Fix ordering of error messages conditions 2025-08-28 17:06:10 +02:00
Stypox
29a3ca83b5 Show better information about player errors 2025-08-28 17:06:09 +02:00
Stypox
38064be702 Add more specific error messages and deduplicate their handling 2025-08-28 17:05:52 +02:00
Tobi
d17eae9bad Merge pull request #12253 from Profpatsch/popup-overlay-alert-dialog
Overlay Permission: display explanatory dialog for Android > R
2025-08-27 02:50:45 -07:00
TobiGr
74562db965 Use androidx compat alert dialog 2025-08-27 11:45:31 +02:00
Profpatsch
386d5197d8 Permission: display explanatory dialog for Android > R
On Android > R, ACTION_MANAGE_OVERLAY_PERMISSION always brings the
user to the app selection screen.

https://developer.android.com/about/versions/11/privacy/permissions#manage_overlay

This is highly confusing behaviour from the system, so let’s add an
instruction before navigating to the settings menu.
2025-08-27 11:38:25 +02:00
Tobi
ccd76dea1f Merge pull request #12544 from Stypox/download-options
Add option to delete a download without also deleting file
2025-08-27 02:31:14 -07:00
whistlingwoods
9282cce6a8 fix: unfinished downloads disappear from the downloads list after app gets killed
Author: InfinityLoop1308
Adapted for NewPipe from a fork's this commit 1cf059ce5e
2025-08-22 01:14:24 +05:30
Stypox
7644066c5a Add option to delete a download without also deleting file 2025-08-16 16:50:01 +02:00
Stypox
9bc8139b8c Merge pull request #12483 from TeamNewPipe/ignore-picasso-update 2025-08-11 17:48:30 +02:00
Tobi
ff3526b28d Merge pull request #12460 from Isira-Seneviratne/Short-count-refactor
Fix short count formatting for Android versions below 7.0
2025-08-01 10:56:41 -07:00
TobiGr
d6c0dc32d1 Correctly ignore new version check for picasso 2025-08-01 10:50:54 +02:00
Stypox
124ab56c5f Merge branch 'master' into dev 2025-07-31 23:52:01 +02:00
Stypox
95a0e0ca39 Merge pull request #12435 from TeamNewPipe/release-0.28.0 2025-07-31 23:51:10 +02:00
Stypox
4d97a7653d Merge pull request #12450 from TeamNewPipe/yt-trending-migration 2025-07-31 23:48:53 +02:00
Hosted Weblate
5aefa4aff2 Translated using Weblate (Tigrinya)
Currently translated at 12.7% (95 of 748 strings)

Co-authored-by: fool <thing-sauna-cussed@duck.com>
2025-07-31 23:43:24 +02:00
Stypox
b846746119 Update NewPipeExtractor to v0.24.8 2025-07-31 23:43:19 +02:00
Stypox
b7b836e941 Update the names of YT kiosks 2025-07-31 23:43:19 +02:00
Stypox
d96c0aebb1 Show tabs above kiosks in drawer 2025-07-31 23:43:19 +02:00
Stypox
8400a9ae8e Remove DEBUG statements and don't replace yt trending with live
You can use this command to test instead:

adb shell run-as org.schabi.newpipe.debug.pr12450 'sed -i '"'"'s#<int name="last_used_preferences_version" value="8" />#<int name="last_used_preferences_version" value="6" />#'"'"' shared_prefs/org.schabi.newpipe.debug.pr12450_preferences.xml' && adb shell run-as org.schabi.newpipe.debug.pr12450 'sed -i '"'"'s#\]}</string>#,{\&quot;tab_id\&quot;:5,\&quot;service_id\&quot;:0,\&quot;kiosk_id\&quot;:\&quot;Trending\&quot;},{\&quot;tab_id\&quot;:5,\&quot;service_id\&quot;:1,\&quot;kiosk_id\&quot;:\&quot;Top 50\&quot;}]}</string>#'"'"' shared_prefs/org.schabi.newpipe.debug.pr12450_preferences.xml'
2025-07-31 23:43:19 +02:00
Stypox
7cecd11f72 [YouTube] Add icons and strings for new trending pages 2025-07-31 23:43:19 +02:00
TobiGr
ed93603815 WIP: Add SettingsMigration to change YouTube trending kiosk tab 2025-07-31 23:43:19 +02:00
Stypox
56f79fac13 Merge branch 'release-0.28.0' into dev 2025-07-30 11:42:06 +02:00
Stypox
86efde5996 Merge pull request #12476 from TeamNewPipe/weblate 2025-07-29 20:23:08 +02:00
Stypox
ca9fc14c2a Fix name of nepali language (there was a leftover N) 2025-07-29 20:19:31 +02:00
tobigr
7130adb4ec Clean strings 2025-07-29 20:19:31 +02:00
tobigr
e08d2d8726 Add new locals to the in-app language chooser 2025-07-29 20:19:31 +02:00
Isira Seneviratne
ef29c318b0 Remove NewApi suppression 2025-07-29 06:18:27 +05:30
Hosted Weblate
6516fb96fd Translated using Weblate (Romanian)
Currently translated at 100.0% (748 of 748 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (86 of 86 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (86 of 86 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (748 of 748 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (748 of 748 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (748 of 748 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (748 of 748 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (748 of 748 strings)

Translated using Weblate (French)

Currently translated at 100.0% (748 of 748 strings)

Translated using Weblate (Icelandic)

Currently translated at 99.4% (745 of 749 strings)

Translated using Weblate (Estonian)

Currently translated at 18.6% (16 of 86 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (86 of 86 strings)

Translated using Weblate (Portuguese (Portugal))

Currently translated at 99.5% (746 of 749 strings)

Translated using Weblate (Belarusian)

Currently translated at 99.7% (747 of 749 strings)

Translated using Weblate (Estonian)

Currently translated at 100.0% (749 of 749 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (749 of 749 strings)

Translated using Weblate (Hebrew)

Currently translated at 99.7% (747 of 749 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (749 of 749 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (749 of 749 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (749 of 749 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (749 of 749 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (749 of 749 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (749 of 749 strings)

Translated using Weblate (Slovak)

Currently translated at 99.7% (747 of 749 strings)

Translated using Weblate (Greek)

Currently translated at 100.0% (749 of 749 strings)

Translated using Weblate (Portuguese)

Currently translated at 99.5% (746 of 749 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (749 of 749 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (749 of 749 strings)

Translated using Weblate (German)

Currently translated at 100.0% (749 of 749 strings)

Translated using Weblate (German)

Currently translated at 100.0% (749 of 749 strings)

Translated using Weblate (Tamazight (Central Atlas))

Currently translated at 19.2% (144 of 749 strings)

Translated using Weblate (Macedonian)

Currently translated at 79.3% (594 of 749 strings)

Translated using Weblate (Slovenian)

Currently translated at 54.6% (409 of 749 strings)

Translated using Weblate (Tigrinya)

Currently translated at 12.7% (95 of 747 strings)

Translated using Weblate (Tigrinya)

Currently translated at 3.4% (3 of 86 strings)

Translated using Weblate (Icelandic)

Currently translated at 99.4% (743 of 747 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 90.6% (78 of 86 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (86 of 86 strings)

Translated using Weblate (French)

Currently translated at 100.0% (86 of 86 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (86 of 86 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (86 of 86 strings)

Translated using Weblate (Polish)

Currently translated at 58.1% (50 of 86 strings)

Translated using Weblate (Hindi)

Currently translated at 100.0% (86 of 86 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (86 of 86 strings)

Translated using Weblate (Punjabi)

Currently translated at 100.0% (86 of 86 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (86 of 86 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (86 of 86 strings)

Translated using Weblate (German)

Currently translated at 100.0% (86 of 86 strings)

Translated using Weblate (Belarusian)

Currently translated at 100.0% (747 of 747 strings)

Translated using Weblate (Punjabi)

Currently translated at 100.0% (747 of 747 strings)

Translated using Weblate (Hindi)

Currently translated at 100.0% (747 of 747 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (747 of 747 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (747 of 747 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (747 of 747 strings)

Translated using Weblate (German)

Currently translated at 100.0% (747 of 747 strings)

Co-authored-by: 439JBYL80IGQTF25UXNR0X1BG <439JBYL80IGQTF25UXNR0X1BG@users.noreply.hosted.weblate.org>
Co-authored-by: Agnieszka C <aga_04@o2.pl>
Co-authored-by: Drugi Sapog <dindrugi@users.noreply.hosted.weblate.org>
Co-authored-by: Dual Natan <dvapatinatan@gmail.com>
Co-authored-by: Emin Tufan Çetin <etcetin@gmail.com>
Co-authored-by: Fjuro <fjuro@alius.cz>
Co-authored-by: Ghost of Sparta <makesocialfoss32@keemail.me>
Co-authored-by: Hakim Oubouali <hakim.oubouali.skr@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Igor Rückert <igorruckert@yahoo.com.br>
Co-authored-by: Igor Sorocean <sorocean.igor@gmail.com>
Co-authored-by: Jeff Huang <s8321414@gmail.com>
Co-authored-by: Marian Hanzel <marulinko@gmail.com>
Co-authored-by: Matej U <mateju@src.gnome.org>
Co-authored-by: Michael Moroni <michaelmoroni@disroot.org>
Co-authored-by: Mickaël Binos <mickaelbinos@outlook.com>
Co-authored-by: Milan <mobrcian@hotmail.com>
Co-authored-by: NTFSynergy <ntfsynergy@gmail.com>
Co-authored-by: Priit Jõerüüt <jrthwlate@users.noreply.hosted.weblate.org>
Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Stypox <stypox@pm.me>
Co-authored-by: Sveinn í Felli <sv1@fellsnet.is>
Co-authored-by: Trunars <trunars@abv.bg>
Co-authored-by: Vasilis K <skyhirules@gmail.com>
Co-authored-by: VfBFan <drop0815@posteo.de>
Co-authored-by: Yaron Shahrabani <sh.yaron@gmail.com>
Co-authored-by: Yassin Amir <y6b5@proton.me>
Co-authored-by: erti <erti@users.noreply.hosted.weblate.org>
Co-authored-by: ikanakova <ikanakova@users.noreply.hosted.weblate.org>
Co-authored-by: nautilusx <translate@disroot.org>
Co-authored-by: ssantos <ssantos@web.de>
Co-authored-by: whistlingwoods <72640314+whistlingwoods@users.noreply.github.com>
Co-authored-by: zmni <zmni@outlook.com>
Co-authored-by: Максим Горпиніч <gorpinicmaksim5@gmail.com>
Co-authored-by: 大王叫我来巡山 <hamburger2048@users.noreply.hosted.weblate.org>
Co-authored-by: 赖诚俊 <cosmic.universe.glitch@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/ar/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/cs/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/de/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/et/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/fr/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/hi/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/hu/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/it/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/pa/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/pl/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/ru/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/sk/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/ti/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/uk/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/zh_Hans/
Translation: NewPipe/Metadata
2025-07-28 23:05:05 +02:00
Stypox
e9922fe162 Merge pull request #12470 from litetex/cleanup-PlayerHelper-localization 2025-07-28 15:30:07 +02:00
Stypox
eea2b7417e Fix player formatters resetting too early on language change
formatters() is called again by the player before the user has a chance to click on the language in the language chooser.

So the correct solution would probably be to attach to https://developer.android.com/reference/android/content/Intent#ACTION_LOCALE_CHANGED, but let's keep it simple. I added `PlayerHelper.resetFormat();` in `ContentSettingsFragment.onDestroy()` and it works. It will mean the player formatters will be reset every time the user exits content settings, but whatever.
2025-07-28 15:29:06 +02:00
litetex
893a1cb699 Encapsulate Formatters in PlayerHelper
and reset them when the language is changed/changing.
This way they will be re-initialized on the next call.

Also Remove a bunch of outdated/non-thread safe code (STRING_FORMATTER)
2025-07-28 15:11:27 +02:00
litetex
ebd5e1a318 Remove unused method 2025-07-28 15:11:27 +02:00
litetex
70841db92f Cleanup `Localization` formatting 2025-07-28 15:11:27 +02:00
litetex
859555e129 Use regions 2025-07-28 15:11:27 +02:00
Stypox
c1cef19b33 Merge pull request #12455 from TobiGr/nextPage-nullable 2025-07-28 14:52:08 +02:00
Stypox
9ba30887f9 Improve null checking further in SearchFragment.handleNextItems 2025-07-28 14:43:46 +02:00
Stypox
0ef38e3a4d Merge pull request #12472 from TeamNewPipe/user-agent-140 2025-07-28 14:03:42 +02:00
Isira Seneviratne
9f11db8e06 Improve scale display 2025-07-28 09:02:52 +05:30
Isira Seneviratne
fece0741e5 Suppress NewApi 2025-07-27 15:47:06 +05:30
TobiGr
a9ce2e9605 Update USER_AGENT to Firefox 140 ESR 2025-07-27 09:39:53 +02:00
Isira Seneviratne
b9b47fc520 Update manifest, startForeground call 2025-07-27 11:58:01 +05:30
Isira Seneviratne
59db955493 Fix new streams notification issue 2025-07-27 11:31:23 +05:30
Isira Seneviratne
22a709d53b Merge pull request #12388 from mikooomich/sdk35
Target SDK 35
2025-07-24 08:18:32 +05:30
Michael Zh
329d76c857 Bump emulator target 33 -> 35 2025-07-23 22:30:34 -04:00
Isira Seneviratne
9f526e8e8f Fix short count formatting for Android versions below 7.0 2025-07-24 07:56:44 +05:30
Michael Zh
50caba6606 Fix compile
Co-Authored-By: Isira Seneviratne <31027858+Isira-Seneviratne@users.noreply.github.com>
2025-07-23 18:49:28 -04:00
Michael Zh
26443f9f14 WIP: Fix compile 2025-07-23 18:45:30 -04:00
Michael Zh
366129eee2 Fix error toast crash 2025-07-23 18:45:30 -04:00
Michael Zh
4c8d44b6ba Bump compileSdk to 36 and targetSdk to 35
* Sdk 36 requires edge to edge, so use 35 so we can opt out for now
2025-07-23 18:45:30 -04:00
Michael Zh
14cd562ebd Update manifest for sdk34 FGS changes 2025-07-23 18:45:30 -04:00
Michael Zh
04ef608f7a Specify RECEIVER_EXPORTED/RECEIVER_NOT_EXPORTED for sdk34 2025-07-23 18:45:30 -04:00
Stypox
71fcc5ebce Release v0.28.0 (1005) 2025-07-23 14:22:07 +02:00
TobiGr
30e33d59e8 Use correct fix for nextPage being null while creating error report in SearchFragment.handleNextItems() 2025-07-22 16:12:02 +02:00
Kouki Badr
a4bd82be8a fix: handle nullable nextPage behavior when searching albums #12401 (#12408)
* fix: handle nullable nextPage behavior when searching albums #12401

* feat: add nullable annotation to newPage attribute in SearchFragment

* Updated more usages of InfoItemsPage#getNextPage. Nullability is already handled in these areas so no other changes needed

---------

Co-authored-by: Siddhesh Naik <siddheshnaik20@protonmail.com>
2025-07-22 08:58:56 +05:30
litetex
45589dbf26 Merge pull request #12444 from Isira-Seneviratne/Per-app-language
Enable per-app language preferences for Android < 13
2025-07-20 22:20:12 +02:00
litetex
99ae3fdd4e Removed no longer needed translation key 2025-07-20 22:05:05 +02:00
litetex
f48e73eb2a Cleaned up some code related to app language
* Use build constants when possible
* Inline variables
* Don't use var for normal-sized types (that way it's easier to review)
* Split code into methods
2025-07-20 21:52:07 +02:00
Isira Seneviratne
99003bab07 Clean up imports 2025-07-20 16:43:37 +05:30
Isira Seneviratne
9e14f93186 Properly handle when system language is selected 2025-07-20 16:27:07 +05:30
Isira Seneviratne
abd9aade87 Update AppCompat 2025-07-20 05:24:56 +05:30
Isira Seneviratne
b8f9c125cd Add link for future reference 2025-07-20 05:03:20 +05:30
Isira Seneviratne
893a227ab1 Enable per-app language preferences for Android < 13 2025-07-20 04:50:49 +05:30
Stypox
0db859e225 Merge pull request #12438 from TeamNewPipe/soundcloud/top_50 2025-07-19 20:53:44 +02:00
Stypox
e61f98bd47 Merge pull request #12434 from TeamNewPipe/fix-new-badge-links-on-readme 2025-07-19 20:44:03 +02:00
Stypox
991d9ea3df Fix state not saved 2025-07-19 20:39:55 +02:00
Stypox
f94892166d Improve comment 2025-07-19 20:34:09 +02:00
Stypox
9697112db6 Show error panel in EmptyFragment 2025-07-19 19:41:13 +02:00
litetex
f64dba0107 Fix new badge links on Readme being rendered incorrectly
For all non default Readmes
2025-07-19 22:45:32 +05:30
litetex
9bf01e1241 Fix new badge links on Readme being rendered incorrectly 2025-07-19 22:45:32 +05:30
Stypox
474efbebc1 Merge pull request #12437 from TeamNewPipe/localization-main-page-content 2025-07-19 19:04:34 +02:00
tobigr
fe58ec85ed Fix error detection when loading main page tabs
Do not crash if something unexpected happens.
2025-07-19 13:37:54 +02:00
tobigr
941f85781b Display dialog informing the user about the removal of the Top 50 kiosk 2025-07-19 13:37:54 +02:00
tobigr
7e0ee4eb7a Update Extractor and add migration to remove SoundCloud Top 50 kiosk 2025-07-18 18:59:28 +02:00
tobigr
4a41214df4 Do not capitalize "page" for main page content options 2025-07-18 10:28:59 +02:00
Stypox
938265d127 Update NewPipeExtractor 2025-07-17 23:57:03 +02:00
Stypox
ba4e7a3c7f Add changelog for v0.28.0 (1005) 2025-07-17 10:18:10 +02:00
Hosted Weblate
58b5ccb66f Translated using Weblate (Czech)
Currently translated at 100.0% (85 of 85 strings)

Translated using Weblate (Estonian)

Currently translated at 100.0% (747 of 747 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (747 of 747 strings)

Translated using Weblate (Hebrew)

Currently translated at 100.0% (747 of 747 strings)

Translated using Weblate (Hebrew)

Currently translated at 100.0% (747 of 747 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (745 of 745 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (745 of 745 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (747 of 747 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (747 of 747 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (745 of 745 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (745 of 745 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (747 of 747 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (747 of 747 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (747 of 747 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (747 of 747 strings)

Translated using Weblate (Serbian)

Currently translated at 99.7% (745 of 747 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (747 of 747 strings)

Translated using Weblate (French)

Currently translated at 100.0% (747 of 747 strings)

Translated using Weblate (Tigrinya)

Currently translated at 12.6% (94 of 744 strings)

Translated using Weblate (Tigrinya)

Currently translated at 12.6% (94 of 744 strings)

Translated using Weblate (Tigrinya)

Currently translated at 12.6% (94 of 744 strings)

Translated using Weblate (Tigrinya)

Currently translated at 12.6% (94 of 744 strings)

Translated using Weblate (Tigrinya)

Currently translated at 12.6% (94 of 744 strings)

Translated using Weblate (Tigrinya)

Currently translated at 12.6% (94 of 744 strings)

Translated using Weblate (Tigrinya)

Currently translated at 12.6% (94 of 744 strings)

Translated using Weblate (Tigrinya)

Currently translated at 12.6% (94 of 744 strings)

Translated using Weblate (Tigrinya)

Currently translated at 12.6% (94 of 744 strings)

Translated using Weblate (Tigrinya)

Currently translated at 12.6% (94 of 744 strings)

Translated using Weblate (Tigrinya)

Currently translated at 12.6% (94 of 744 strings)

Translated using Weblate (Tigrinya)

Currently translated at 12.6% (94 of 744 strings)

Translated using Weblate (Tigrinya)

Currently translated at 12.6% (94 of 744 strings)

Translated using Weblate (Tigrinya)

Currently translated at 12.6% (94 of 744 strings)

Translated using Weblate (Tigrinya)

Currently translated at 12.6% (94 of 744 strings)

Translated using Weblate (Tigrinya)

Currently translated at 12.6% (94 of 744 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 8.2% (7 of 85 strings)

Translated using Weblate (Serbian)

Currently translated at 16.4% (14 of 85 strings)

Translated using Weblate (Portuguese (Portugal))

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Serbian)

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Azerbaijani (Southern))

Currently translated at 1.1% (1 of 85 strings)

Added translation using Weblate (Azerbaijani (Southern))

Translated using Weblate (Azerbaijani)

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Serbian)

Currently translated at 16.4% (14 of 85 strings)

Translated using Weblate (Greek)

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Tamil)

Currently translated at 100.0% (85 of 85 strings)

Translated using Weblate (Danish)

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Tamil)

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (85 of 85 strings)

Translated using Weblate (Romanian)

Currently translated at 99.7% (742 of 744 strings)

Translated using Weblate (Serbian)

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (85 of 85 strings)

Translated using Weblate (Belarusian)

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Hebrew)

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (French)

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Estonian)

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (German)

Currently translated at 100.0% (744 of 744 strings)

Translated using Weblate (Persian)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Portuguese (Portugal))

Currently translated at 97.6% (83 of 85 strings)

Translated using Weblate (Hebrew)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Hebrew)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Hebrew)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Hebrew)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Hebrew)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Hebrew)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Hebrew)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (85 of 85 strings)

Translated using Weblate (Kabyle)

Currently translated at 29.0% (215 of 741 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (741 of 741 strings)

Added translation using Weblate (Luri (Bakhtiari))

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Persian)

Currently translated at 94.3% (699 of 741 strings)

Translated using Weblate (Estonian)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Serbian)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Persian)

Currently translated at 94.1% (698 of 741 strings)

Translated using Weblate (Belarusian)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Punjabi)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Belarusian)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Finnish)

Currently translated at 98.5% (730 of 741 strings)

Translated using Weblate (French)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (85 of 85 strings)

Translated using Weblate (French)

Currently translated at 100.0% (85 of 85 strings)

Translated using Weblate (German)

Currently translated at 100.0% (85 of 85 strings)

Translated using Weblate (Sardinian)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (German)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 69.4% (59 of 85 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Latvian)

Currently translated at 21.4% (18 of 84 strings)

Translated using Weblate (Latvian)

Currently translated at 99.7% (739 of 741 strings)

Translated using Weblate (Latvian)

Currently translated at 20.2% (17 of 84 strings)

Translated using Weblate (Latvian)

Currently translated at 16.6% (14 of 84 strings)

Translated using Weblate (Greek)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (French)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Greek)

Currently translated at 32.1% (27 of 84 strings)

Translated using Weblate (Greek)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Portuguese (Portugal))

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Romanian)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Urdu)

Currently translated at 69.2% (513 of 741 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Catalan)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Catalan)

Currently translated at 90.2% (669 of 741 strings)

Translated using Weblate (Estonian)

Currently translated at 17.8% (15 of 84 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Dutch (Belgium))

Currently translated at 76.7% (569 of 741 strings)

Translated using Weblate (Dutch (Belgium))

Currently translated at 76.2% (565 of 741 strings)

Translated using Weblate (Lithuanian)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Hebrew)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Hebrew)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Danish)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Kabyle)

Currently translated at 28.8% (214 of 741 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (84 of 84 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Serbian)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Spanish)

Currently translated at 99.8% (740 of 741 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 67.8% (57 of 84 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Icelandic)

Currently translated at 99.4% (737 of 741 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (French)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Azerbaijani)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (German)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Estonian)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Belarusian)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Breton)

Currently translated at 13.6% (101 of 741 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (741 of 741 strings)

Translated using Weblate (Breton)

Currently translated at 12.1% (90 of 741 strings)

Translated using Weblate (Breton)

Currently translated at 7.6% (57 of 741 strings)

Translated using Weblate (Breton)

Currently translated at 7.4% (55 of 741 strings)

Translated using Weblate (Breton)

Currently translated at 7.4% (55 of 741 strings)

Translated using Weblate (Breton)

Currently translated at 7.4% (55 of 741 strings)

Translated using Weblate (Belarusian)

Currently translated at 99.8% (740 of 741 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (741 of 741 strings)

Added translation using Weblate (Breton)

Translated using Weblate (Romanian)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Portuguese (Portugal))

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Serbian)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Danish)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Estonian)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Finnish)

Currently translated at 98.5% (729 of 740 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (French)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Icelandic)

Currently translated at 99.4% (736 of 740 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (German)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Azerbaijani)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (740 of 740 strings)

Co-authored-by: 439JBYL80IGQTF25UXNR0X1BG <439JBYL80IGQTF25UXNR0X1BG@users.noreply.hosted.weblate.org>
Co-authored-by: AP <kubanto@users.noreply.hosted.weblate.org>
Co-authored-by: Abu Sarim Hindi <sarfaraz.ahmed78@gmail.com>
Co-authored-by: Agnieszka C <aga_04@o2.pl>
Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Co-authored-by: Alex25820 <alexs25820@gmail.com>
Co-authored-by: Balázs Meskó <meskobalazs@mailbox.org>
Co-authored-by: Bastian <basti.anderl774@gmail.com>
Co-authored-by: ButterflyOfFire <boffire@users.noreply.hosted.weblate.org>
Co-authored-by: Danial Behzadi <dani.behzi@ubuntu.com>
Co-authored-by: Darth23G (DarthGamer23) <fref2329@gmail.com>
Co-authored-by: Deleted User <noreply+48943@weblate.org>
Co-authored-by: Dream X <nodem49316@daupload.com>
Co-authored-by: Drugi Sapog <dindrugi@users.noreply.hosted.weblate.org>
Co-authored-by: Emin Tufan Çetin <etcetin@gmail.com>
Co-authored-by: Fareedar Islami <fareedar.islami@gmail.com>
Co-authored-by: Femini <nizamismidov4@gmail.com>
Co-authored-by: Fjuro <fjuro@alius.cz>
Co-authored-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
Co-authored-by: Fjuro <git@alius.cz>
Co-authored-by: Ghost of Sparta <makesocialfoss32@keemail.me>
Co-authored-by: GiannosOB <giannos2105@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Igor Rückert <igorruckert@yahoo.com.br>
Co-authored-by: Igor Sorocean <sorocean.igor@gmail.com>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: J. Lavoie <j.lavoie@net-c.ca>
Co-authored-by: Jay Tromp <jaytromp@pm.me>
Co-authored-by: Jeff Huang <s8321414@gmail.com>
Co-authored-by: Jordi Cambrells <cambrells@users.noreply.hosted.weblate.org>
Co-authored-by: Jordi Cambrells <hanta.hrabal@gmail.com>
Co-authored-by: Juzé <dedakir923@exoular.com>
Co-authored-by: KaGaster <mohamed.kooli@medtech.tn>
Co-authored-by: Languages add-on <noreply-addon-languages@weblate.org>
Co-authored-by: Linerly <linerly@proton.me>
Co-authored-by: MS-PC <MSPCtranslator@gmail.com>
Co-authored-by: Mandeep <mandeeps708@gmail.com>
Co-authored-by: Massimo Pissarello <mapi68@gmail.com>
Co-authored-by: Michael Moroni <michaelmoroni@disroot.org>
Co-authored-by: Mickaël Binos <mickaelbinos@outlook.com>
Co-authored-by: Milan <mobrcian@hotmail.com>
Co-authored-by: Mohammed al-Qubati <mhraqeeb@gmail.com>
Co-authored-by: Mücteba <muctebanesiri@gmail.com>
Co-authored-by: NEXI <nexiphotographer@gmail.com>
Co-authored-by: Nick Wick <NickWick@users.noreply.hosted.weblate.org>
Co-authored-by: Philip Goto <philip.goto@gmail.com>
Co-authored-by: Priit Jõerüüt <hwlate@joeruut.com>
Co-authored-by: Priit Jõerüüt <jrthwlate@users.noreply.hosted.weblate.org>
Co-authored-by: Q. Boii <sf1hks@marketmail.info>
Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Sveinn í Felli <sv1@fellsnet.is>
Co-authored-by: THANOS SIOURDAKIS <siourdakisthanos@gmail.com>
Co-authored-by: Trunars <trunars@abv.bg>
Co-authored-by: Vasilis K <skyhirules@gmail.com>
Co-authored-by: VfBFan <drop0815@posteo.de>
Co-authored-by: Yaron Shahrabani <sh.yaron@gmail.com>
Co-authored-by: Yasser Althuwaini <ymth2000@outlook.com>
Co-authored-by: Yauhen <bugomol@users.noreply.hosted.weblate.org>
Co-authored-by: ab_09 <ab_09@users.noreply.hosted.weblate.org>
Co-authored-by: cat <catsnote@proton.me>
Co-authored-by: dekiw39846 <dekiw39846@bariswc.com>
Co-authored-by: elid <shopisrael12@gmail.com>
Co-authored-by: gfbdrgn <erfvvgtyhbnjhyuu@wireconnected.com>
Co-authored-by: late <late@users.noreply.hosted.weblate.org>
Co-authored-by: moton03 <moton.cat@outlook.com>
Co-authored-by: rehork <cooky@e.email>
Co-authored-by: rimasx <riks_12@hot.ee>
Co-authored-by: ssantos <ssantos@web.de>
Co-authored-by: trunars <trunars@gmail.com>
Co-authored-by: Максим Горпиніч <gorpinicmaksim5@gmail.com>
Co-authored-by: Максим Горпиніч <maksimgorpinic2005a@gmail.com>
Co-authored-by: Максим Горпиніч <maksimgorpinic4@gmail.com>
Co-authored-by: Саша Петровић <salepetronije@gmail.com>
Co-authored-by: தமிழ்நேரம் <anishprabu.t@gmail.com>
Co-authored-by: ℂ𝕠𝕠𝕠𝕝 (𝕘𝕚𝕥𝕙𝕦𝕓.𝕔𝕠𝕞/ℂ𝕠𝕠𝕠𝕝) <coool@mail.lv>
Co-authored-by: 大王叫我来巡山 <hamburger2048@users.noreply.hosted.weblate.org>
Co-authored-by: 李恩霆 <timothylee0802@outlook.com>
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/ar/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/azb/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/cs/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/de/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/el/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/en_GB/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/et/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/fr/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/hu/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/lv/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/pt_PT/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/ru/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/sr/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/sv/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/ta/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/zh_Hant/
Translation: NewPipe/Metadata
2025-07-17 09:16:38 +02:00
Stypox
4e94b2602d Merge pull request #12258 from Profpatsch/show-service-name-in-search 2025-07-16 15:02:43 +02:00
Stypox
4ddc0648ef Merge pull request #12412 from Stypox/fix-ghost-notifications 2025-07-14 21:56:22 +02:00
Tobi
4c920a4406 Merge pull request #12367 from watermelon42/3783_Import_Soundcloud_likes
Support Soundcloud likes in channel and feed
2025-07-14 01:01:02 -07:00
watermelon42
1c0eabf75c Updated extractor version to latest commit 2025-07-13 16:21:42 +02:00
watermelon42
f119a368d8 Added support for importing Soundcloud likes as a new tab before About in a user's channel.
The likes are also retrieved in the feed if the user is subscribed to.
2025-07-11 09:50:33 +02:00
Stypox
f3c20d43be Merge pull request #12410 from Stypox/fix-android-auto-thumbnails 2025-07-08 11:42:20 +02:00
Tobi
c9559fa801 Merge pull request #12416 from Stypox/fix-fullscreen-clear-queue-prompt
Fix fullscreen eliciting "clear queue" prompt
2025-07-07 11:47:27 -07:00
Stypox
8ab79488e9 Merge pull request #12409 from Stypox/readme-badge-size-fix 2025-07-07 16:26:20 +02:00
Stypox
f0b26e208b Update notice about rewrite in the README 2025-07-07 16:25:38 +02:00
Stypox
79084568f2 Fix fullscreen eliciting "clear queue" prompt 2025-07-07 15:07:46 +02:00
Stypox
705b5e5580 Fix ghost notifications on Android 10
Fixes #12400, see there for explanation. Citing from there:

So apparently the problem is onGetRoot always returning a BrowserRoot instance. Making it return null solved the issue (but again, breaks Android Auto compatibility). It turns out (see https://stackoverflow.com/q/63818988/) that onGetRoot is also used for media resumption https://developer.android.com/media/implement/surfaces/mobile#mediabrowserservice_implementation, which causes a new notification to pop up (in this case a useless notification because our onGetRoot does not return something that can be used for resumption). So what needs to be done is to check if rootHints?.getBoolean(EXTRA_RECENT) == true and if that's the case not return anything (as EXTRA_RECENT is used by the system for resumption).

The PackageValidator file is taken from 329a21b63c/common/src/main/java/com/example/android/uamp/media/PackageValidator.kt .
2025-07-07 01:06:59 +02:00
Stypox
a4d457b2b2 Use Kotlin's .toUri() instead of Uri.parse() 2025-07-06 15:05:30 +02:00
Stypox
834c93f22a Fix thumbnails appearing on Android Auto even if disabled 2025-07-06 14:49:09 +02:00
Stypox
a0adeb0099 Fix "Get it on F-Droid" appearing giant in README 2025-07-06 13:51:59 +02:00
Isira Seneviratne
2dd11f70a3 Merge pull request #12356 from dev-victoria/fix-json-importing-oldandroid
check if the JSON MimeType is supported
2025-07-04 07:06:34 +05:30
Stypox
d048bca8b4 Temporarily disable sonarcloud CI step 2025-06-28 15:17:18 +02:00
Diana Victoria Furrer
0c9f5ddcaf change according to Isira-Seneviratne suggestion 2025-06-17 15:42:01 +02:00
Diana Victoria Furrer
aa75a1449f use MimeTypeMap from android webkit to check if the json MimeType is unsupported 2025-06-15 02:19:56 +02:00
Profpatsch
16e32dfc96 SearchFragment: show filter in brackets behind service name
This is still not perfect, but it will show the selected search filter
in addition to the service name, like: “Search YouTube (Playlists)”.

It will not distinguish between a YouTube Music and Youtube filter, so
it will display the same thing. Could be improved, but then the text
gets too long! :(
2025-06-05 14:30:04 +02:00
Stypox
8c4a789f78 Merge pull request #12302 from davidasunmo/update-readmes 2025-06-04 11:59:56 +02:00
Stypox
769e98acd0 Show search filter in search bar hint 2025-06-04 11:54:31 +02:00
Stypox
8e036b5e69 Merge pull request #12325 from dev-victoria/FeedGroupTab 2025-06-04 11:24:32 +02:00
Stypox
571b7bc74b Improve layout of select_feed_group_item 2025-06-04 11:18:04 +02:00
Audric V.
033cc08c26 Merge pull request #12322 from dev-victoria/tiny-code-fixes
Fix equality comparison in Tab class
2025-05-31 16:56:37 +02:00
Diana Victoria Furrer
205d18f4c4 Use GroupName for the Settings Text.
The Tabname displays the default Feed title.
2025-05-31 14:11:26 +02:00
Diana Victoria Furrer
712724211c added FeedGroup to Tab Settings UnitTest 2025-05-31 01:41:06 +02:00
Diana Victoria Furrer
fd09e6147f # Fixed Feed Group Titlebar
- use default fragment_feed_title for TabName
- only clear FeedFragment bar subtitle when it matches the groupName to clear.
2025-05-31 01:30:49 +02:00
Diana Victoria Furrer
279caac915 # Change
Layout select_feed_group_item (FeedGroup Picker in the Settings)
Remove rounded style from the icons
2025-05-30 21:00:37 +02:00
Diana Victoria Furrer
f8ed8e575e # Change
Added FEEDGROUP Tab Code to
 - ChooseTabsFragment
 - Tab

Added strings:
- feed_group_page_summary
2025-05-30 20:47:37 +02:00
Diana Victoria Furrer
436626fa83 # Change
Adjusted select_feed_group_fragment Layout
 - reference select_feed_group_item layout
 - use new Strings

Added strings:
- select_a_feed_group
- no_feed_group_created_yet
2025-05-30 17:54:49 +02:00
Diana Victoria Furrer
7c3989ff93 # Change
Adjusted the new Class SelectFeedGroupFragment for its Role
- Renamed Variables
- adjusted Imports
- adjusted Interface with FeedGroupEntity Values
2025-05-30 17:45:51 +02:00
Diana Victoria Furrer
e6c4690e7d # Copied Layouts
Copied select_channel_fragment to select_feed_group_fragment

Copied select_channel_item to select_feed_group_item

# Change
Replaced the Layout references in the new Class SelectFeedGroupFragment
2025-05-30 17:07:19 +02:00
Diana Victoria Furrer
86869f0a14 Copied SelectFeedGroupFragment from SelectChannelFragment 2025-05-30 16:55:07 +02:00
Diana Victoria Furrer
aa0b45c05f ChannelTab.equals fix comparison 2025-05-30 13:21:45 +02:00
David Asunmo
55bf74b4a7 Fix CI status badge 2025-05-24 02:15:45 +01:00
David Asunmo
de3d11568d Add nightly builds to all readmes
Add matrix to .ru
2025-05-22 03:29:12 +01:00
David Asunmo
16077dee80 Add matrix chat link to all READMEs 2025-05-22 03:29:12 +01:00
Stypox
c9155f7834 Merge pull request #12298 from davidasunmo/add-dev-refactor-nightly-badges
Add dev and refactor nightly build badges
2025-05-20 11:13:38 +02:00
David
7dd1abdf9c Add dev and refactor nightly build badges
bottom text
2025-05-20 02:22:47 +01:00
Profpatsch
f3858e70a3 Merge pull request #11789 from Thompson3142/fix_background_crash_focus
Fix background crash focus
2025-05-09 23:41:38 +02:00
Thompson3142
76202e6b4b Remove no longer needed dependency 2025-05-09 22:29:05 +02:00
Thompson3142
90e2f234e7 Initial commit for better handling of background crashes
Fix crashing behaviour with entry in SharedPreferences

A few minor improvements

Added docs for isInBackground

Some more minor changes

Overwrite methods in MainActivity instead of creating a new class
2025-05-09 22:29:00 +02:00
Profpatsch
42a52b7118 Merge pull request #12259 from Profpatsch/put-@-on-right-side-of-rtl-usernames
Comments: Put @ on the right side of right-to-left usernames
2025-05-08 21:46:00 +02:00
Stypox
d9dccfa8af Merge branch 'master' into dev 2025-05-08 15:04:06 +02:00
Profpatsch
e554c77f2e Comments: Put @ on the right side of right-to-left usernames
From the discussion in
https://github.com/TeamNewPipe/NewPipe/pull/12188 it reads more
natural for RTL readers.
2025-05-07 14:20:44 +02:00
Profpatsch
862a8e8f26 Merge pull request #12188 from VougJo23/commentsfix
fix: support RTL usernames in comment header
2025-05-07 12:20:23 +02:00
Profpatsch
88395fa852 Merge pull request #12202 from AndrianaBilali/fix/timestamp-clicks-in-replies
Fix timestamps not working in comment replies
2025-05-07 12:07:03 +02:00
VougJo23
8d679626f0 fix: support RTL usernames in comment header
The `@` gets added by the youtube API and thus is a fixed member of
the username, so we do some simple detection logic to handle that
case (otherwise the `@` will be at the right side of a RTL username,
which is different of how Youtube displays these usernames in the
browser).

Fixes https://github.com/TeamNewPipe/NewPipe/issues/12141
2025-05-07 12:05:09 +02:00
Profpatsch
d2dc20c551 SearchFragment: show service name in search hint
The only hint (haha) which service one is searching in is currently
the color of the background. This is super confusing, yesterday a
friend tried to search for a video on youtube and the app was set to
Bandcamp, and they were super confused why nothing turned up.

So let’s put the name of the service in the hint!

The `updateService()` thing is a little confused, but I didn’t want
to refactor to improve the logic. It’s not doing anything
computationally intensive anyway.

For PeerTube, the sidebar calls it FramaTube but the service name is
PeerTube, I’m not sure why that is the case. Looks like the string
depends on the name of the instance? Hm, can be improved later I
think.
2025-05-07 10:12:41 +02:00
Andriana
e7f3750f5e Fix timestamps not working in comment replies
Use LinkMovementMethodCompat for comment links

Co-authored-by: Isira Seneviratne <31027858+Isira-Seneviratne@users.noreply.github.com>

Update import

Use LongPressLinkMovementMethod
2025-05-06 17:12:17 +02:00
j-haldane
48e826e912 Fix header crash in History List view (#12214)
* Adapt header handling changes from other recyclerview adapters to fix issue #4475 in StatisticsPlaylistFragment

* Remove unneeded LayoutInflater

* Revert "Remove unneeded LayoutInflater"

This reverts commit ab73dc1e72.

* Revert "Adapt header handling changes from other recyclerview adapters to fix issue #4475 in StatisticsPlaylistFragment"

This reverts commit 2abe71cc98.

* Remove header animation causing view recycling issue
2025-05-06 17:07:45 +02:00
Profpatsch
088cb8353e Merge pull request #12256 from Profpatsch/improve-jitpack-workaraund-docs
build.gradle: Improve jitpack workaround doc & fix hash
2025-05-06 12:56:38 +02:00
Profpatsch
5ca544bc42 build.gradle: Improve jitpack workaround doc & fix hash 2025-05-06 10:48:20 +02:00
Stypox
aa1b7f8584 Merge pull request #12215 from naveensingh/fix-image-minimizer
Fix image minimizer pattern
2025-04-28 07:34:06 +02:00
Naveen Singh
ce16c6df5f Fix image minimizer pattern
Added non-capturing group that matches either:

 - `user-attachments/assets`
 - `owner/repo/assets/digits`
2025-04-27 19:35:31 -04:00
Stypox
276bf390b2 Merge pull request #12117 from malania02/dev
Show download date of downloaded videos
2025-04-11 20:17:27 +02:00
malania02
f39eda086f Fix for overlapping 2025-04-09 23:40:14 +02:00
Stypox
756327da39 Merge pull request #12093 from mileskrell/mileskrell/support-per-app-language-preferences
Support per-app language preferences
2025-04-08 23:13:07 +02:00
Stypox
5840d3a437 Merge pull request #12150 from FineFindus/fix/potoken-index
[YouTube] Access first element if array size is one
2025-04-08 23:06:04 +02:00
FineFindus
e1dedd45ed [YouTube] Access first element if array size is one
Fixes a regression, where if the challenge data array size was one, the second element
would be accessed, leading to a crash.
This was introduced when porting the challenge parsing from JS to
Kotlin.

Ref: 53b599b042
2025-04-02 22:14:01 +02:00
malania02
912f07a1dd Missing lines added 2025-03-30 14:50:05 +02:00
Miles Krell
205466c56a Move call to setApplicationLocales 2025-03-27 19:14:41 -04:00
Miles Krell
7f10312d0a Move migration to NewPipeSettings 2025-03-23 17:39:21 -04:00
malania02
63be3220e7 Show download date 2025-03-22 16:19:26 +01:00
malania02
536b78f2e6 textview for download date added 2025-03-22 16:13:45 +01:00
malania02
6d6b73ef73 textview for download date added 2025-03-22 16:09:58 +01:00
Stypox
196c27792b Merge pull request #12044 from TeamNewPipe/android-auto
Add support for Android Auto *(season 2)*
2025-03-21 11:21:58 +01:00
Stypox
b3789315ad Merge pull request #12104 from TeamNewPipe/update-npe
Update NewPipe Extractor and add new proguard rules
2025-03-21 10:52:37 +01:00
Miles Krell
c7bf498c04 Don't show toast because of changing content language or country 2025-03-16 20:27:05 -04:00
Miles Krell
35abb99dac Only show toast on Android <13 2025-03-16 20:15:38 -04:00
Miles Krell
70416e73f3 Move app language setting migration to SettingMigrations 2025-03-16 19:24:04 -04:00
TobiGr
a0b76c3385 Update NewPipe Extractor and add new proguard rules
New rules are required since Rhino and Rhino Engine 1.8.0
2025-03-16 22:08:10 +01:00
Tobi
c232193a46 Merge pull request #12083 from har-123/bugfix/11894_fix_duplicate_menu_options
Fix duplicate menu options in ChannelFragment
2025-03-16 10:34:52 +01:00
Siddhesh Naik
f289bea6b3 Fix sonar warning 2025-03-16 12:44:05 +05:30
Harshita
48b200868a BF-11894 : Fix the menu disappearing on performing backGesture 2025-03-16 12:44:05 +05:30
Harshita
54bf7f0ced BF-11894 : Fix the Duplicate menu options in ChannelFragment 2025-03-16 12:44:05 +05:30
Miles Krell
980a35a708 Move migration to separate method 2025-03-15 23:00:31 -04:00
Miles Krell
da106e2361 Don't try to migrate "system" app language 2025-03-15 22:54:17 -04:00
Miles Krell
3532ac96b4 Migrate from pre-Android 13 app language pref 2025-03-15 22:13:01 -04:00
Miles Krell
87693a2ad1 Redirect to per-app language settings on Android 13+ 2025-03-15 21:56:02 -04:00
Hosted Weblate
d321e57620 Translated using Weblate (Czech)
Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Catalan)

Currently translated at 88.2% (653 of 740 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 90.4% (76 of 84 strings)

Translated using Weblate (Belarusian)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Hindi)

Currently translated at 100.0% (84 of 84 strings)

Translated using Weblate (Punjabi)

Currently translated at 100.0% (84 of 84 strings)

Translated using Weblate (Belarusian)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Punjabi)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Hindi)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (German)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (German)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Croatian)

Currently translated at 99.7% (738 of 740 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Belarusian)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Belarusian)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Belarusian)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (84 of 84 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (84 of 84 strings)

Translated using Weblate (Portuguese)

Currently translated at 99.8% (739 of 740 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (French)

Currently translated at 100.0% (84 of 84 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (84 of 84 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Belarusian)

Currently translated at 99.5% (737 of 740 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Hebrew)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Icelandic)

Currently translated at 99.4% (736 of 740 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Greek)

Currently translated at 25.0% (21 of 84 strings)

Translated using Weblate (Greek)

Currently translated at 23.8% (20 of 84 strings)

Translated using Weblate (Estonian)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (French)

Currently translated at 99.5% (737 of 740 strings)

Co-authored-by: 439JBYL80IGQTF25UXNR0X1BG <439JBYL80IGQTF25UXNR0X1BG@users.noreply.hosted.weblate.org>
Co-authored-by: Andrey F <firsan777@mail.ru>
Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Antonin Del Fabbro <message@antonin.one>
Co-authored-by: Christian Eichert <c@zp1.net>
Co-authored-by: Drugi Sapog <dindrugi@users.noreply.hosted.weblate.org>
Co-authored-by: Eduardo Calixto <eduardogubertcalixto@gmail.com>
Co-authored-by: Emin Tufan Çetin <etcetin@gmail.com>
Co-authored-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
Co-authored-by: Ghost of Sparta <makesocialfoss32@keemail.me>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Igor Rückert <igorruckert@yahoo.com.br>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Jan Layola <gilajan@protonmail.com>
Co-authored-by: Kevin Wang <wmk153024@gmail.com>
Co-authored-by: Linerly <linerly@proton.me>
Co-authored-by: Massimo Pissarello <mapi68@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Petr Kadlec <mormegil@centrum.cz>
Co-authored-by: Priit Jõerüüt <hwlate@joeruut.com>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Sergio Marques <so.boston.android@gmail.com>
Co-authored-by: Sveinn í Felli <sv1@fellsnet.is>
Co-authored-by: XxVictoriaxX <evakonoob@gmail.com>
Co-authored-by: Yaron Shahrabani <sh.yaron@gmail.com>
Co-authored-by: bittin1ddc447d824349b2 <bittin@reimu.nl>
Co-authored-by: trunars <trunars@gmail.com>
Co-authored-by: whistlingwoods <72640314+whistlingwoods@users.noreply.github.com>
Co-authored-by: Максим Горпиніч <maksimgorpinic2005a@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/ar/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/cs/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/el/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/fr/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/hi/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/it/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/pa/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/zh_Hans/
Translation: NewPipe/Metadata
2025-03-15 17:43:36 +01:00
Tobi
fb4a65a14a Merge pull request #12043 from TeamNewPipe/hide-view-logs
Disable logs about view animations by default
2025-03-15 17:17:59 +01:00
Stypox
3047704e1c Merge pull request #12089 from mileskrell/mileskrell/fix-audio-track-labels
Disambiguate audio track labels
2025-03-15 12:45:20 +01:00
Stypox
3dcfdaf510 Merge pull request #12065 from tfga/YouTubeTemporaryPlaylist
Share as YouTube temporary playlist
2025-03-15 10:11:59 +01:00
Thiago F. G. Albuquerque
2ceb70236e sharePlaylist(): converting javadoc from Markdown back to "classic javadoc"
(request from @Stypox)
2025-03-14 21:56:42 -03:00
Thiago F. G. Albuquerque
be097f26c8 Deleting the "explanatory text" bellow the title
<string name="share_playlist_with_titles_message">Share playlist with details such as playlist name and video titles or as a simple list of video URLs</string>
    Share playlist with details such as playlist name and video titles or as a simple list of video URLs</string>

(Discussion: https://github.com/TeamNewPipe/NewPipe/pull/12065#discussion_r1994349485)
2025-03-13 19:10:26 -03:00
Thiago F. G. Albuquerque
098f60d593 Don't add the title when sharing as YouTube temp playlist 2025-03-13 18:16:09 -03:00
Thiago F. G. Albuquerque
eb0568044a R.string.share_playlist_as_youtube_temporary_playlist: pt-BR
+ Minor fixes to related translations
2025-03-12 19:09:31 -03:00
Thiago F. G. Albuquerque
f3b3d5c3e7 R.string.share_playlist_as_youtube_temporary_playlist 2025-03-12 19:08:09 -03:00
Miles Krell
b888dc72cf Support per-app language preferences 2025-03-11 23:29:23 -04:00
Thiago F. G. Albuquerque
599d86151a Making ktLint happy 2025-03-11 21:26:58 -03:00
tfga
587df093ea YouTube video IDs are 11 characters long
Co-authored-by: Stypox <stypox@pm.me>
2025-03-11 20:35:41 -03:00
tfga
8830e87242 YouTube video IDs are 11 characters long
Co-authored-by: Stypox <stypox@pm.me>
2025-03-11 20:35:18 -03:00
Thiago F. G. Albuquerque
f96b8f7b2a Comment: maximum length of 50 items
(PR review from @Stypox)
2025-03-11 20:19:54 -03:00
Thiago F. G. Albuquerque
c28478ae53 getYouTubeId(): Changing implementation to use YoutubeStreamLinkHandler
(PR review from @Stypox)
2025-03-11 20:12:25 -03:00
Miles Krell
10110397fd Use display name instead of only the language 2025-03-10 22:01:09 -04:00
tfga
d81244e77c YT temp playlist URL: http => https
Co-authored-by: Stypox <stypox@pm.me>
2025-03-10 19:11:20 -03:00
Stypox
ea20ca9e72 Merge pull request #12067 from Isira-Seneviratne/Fix-notification-grouping
Fix stream notification grouping
2025-02-28 11:51:11 +01:00
Isira Seneviratne
f0c89494dd Fix stream notification grouping 2025-02-27 09:15:40 +05:30
Thiago F. G. Albuquerque
0fd2d4fed6 [#11930] Removing Apache Commons Collections
It's no longer needed after the conversion to Kotlin.
2025-02-26 21:29:48 -03:00
Thiago F. G. Albuquerque
3c7b026d7d [#11930] Updating javadoc 2025-02-25 20:23:07 -03:00
Thiago F. G. Albuquerque
998d84de6c [#11930] Converting to Kotlin 2025-02-25 18:56:12 -03:00
Thiago F. G. Albuquerque
76a02d5858 [#11930] Extracting to a separate file 2025-02-24 20:16:40 -03:00
Thiago F. G. Albuquerque
24bb71a23f [#11930] Making it more efficient: Reverse iteration + limit(50) + reverse 2025-02-24 19:22:36 -03:00
Stypox
49b71942ad Fix style and add comment about null player 2025-02-24 14:21:05 +01:00
Thompson3142
c9ec257a5e Ugly fix for broken text colors in dark mode (#12035)
* Ugly fix for broken text colors in dark mode

* Add comment for clarification

* Added error prevention

* Update app/src/main/java/org/schabi/newpipe/MainActivity.java

---------

Co-authored-by: Stypox <stypox@pm.me>
2025-02-21 09:38:58 +00:00
Thiago F. G. Albuquerque
b1f995a78c [#11930] Playlist with more than 50 items 2025-02-20 16:26:03 -03:00
Thiago F. G. Albuquerque
acac50a1d1 [#11930] Non-Youtube URLs should be ignored 2025-02-19 16:29:34 -03:00
Thiago F. G. Albuquerque
c6b87cd316 [#11930] Making CheckStyle happy 2025-02-18 20:59:13 -03:00
Thiago F. G. Albuquerque
94d4c21cc7 [#11930] @Test export_justUrls() 2025-02-18 17:47:22 -03:00
Stypox
a7a7dc5363 Handle player and player service separately
This is, again, a consequence of the commit "Drop some assumptions on how PlayerService is started and reused".
This commit notified VideoDetailFragment of player starting and stopping independently of the player.
Read the comments in the code changes for more information.
2025-02-18 19:27:46 +01:00
Stypox
126f4b0e30 Fix crash when closing video detail fragment
This bug started appearing because the way to close the player is now unified in PlayerHolder.stopService(), which causes the player to reach back to the video detail fragment with a notification of the shutdown (i.e. onServiceStopped() is called). This is fixed by adding a nullability check on the binding.
2025-02-18 18:03:10 +01:00
Stypox
6558794d26 Try to bind to PlayerService when MainActivity starts
Fixes mini-player not appearing on app start if the player service is already playing something.

The PlayerService (and the player) may be started from an external intent that does not involve the MainActivity (e.g. RouterActivity or Android Auto's media browser interface).
This PR tries to bind to the PlayerService as soon as the MainActivity starts, but only does so in a passive way, i.e. if the service is not already running it is not started.
Once the connection between PlayerHolder and PlayerService is setup, the ACTION_PLAYER_STARTED broadcast is sent to MainActivity so that it can setup the bottom mini-player.
Another important thing this commit does is to check whether the player is open before actually adding the mini-player view, since the PlayerService could be bound even without a running player (e.g. Android Auto's media browser is being used). This is a consequence of commit "Drop some assumptions on how PlayerService is started and reused".
2025-02-18 17:49:38 +01:00
Stypox
1d12874937 Merge pull request #12046 from TobiGr/weblate
Update translations
2025-02-16 22:01:41 +01:00
Stypox
1d98518bfa Fix loading remote playlists in media browser 2025-02-16 21:44:50 +01:00
Stypox
e5458bcb14 Properly handle item errors during media browser loading
Non-item errors, i.e. critical parsing errors of the page, are still handled properly.
2025-02-16 21:44:50 +01:00
Stypox
dc62d211f5 Properly stop PlayerService
This commit is a consequence of the commit "Drop some assumptions on how PlayerService is started and reused". Since the assumptions on how the PlayerService is started and reused have changed, we also need to adapt the way it is stopped. This means allowing the service to remain alive even after the player is destroyed, in case the system is still accessing PlayerService e.g. through the media browser interface. The foreground service needs to be stopped and the notification removed in any case.
2025-02-16 21:44:49 +01:00
Stypox
ec6612dd71 Call exoPlayer.prepare() on PlaybackPreparer.onPrepare()
If a playbackPreparer is set, then instead of calling `player.prepare()`, the MediaSessionConnector will call `playbackPreparer.onPrepare(true)` instead, as seen below.
This commit makes it so that playbackPreparer.onPrepare(true) restores the original behavior of just calling player.prepare().

From MediaSessionConnector -> MediaSessionCompat.Callback implementation:
```java
    @Override
    public void onPlay() {
      if (canDispatchPlaybackAction(PlaybackStateCompat.ACTION_PLAY)) {
        if (player.getPlaybackState() == Player.STATE_IDLE) {
          if (playbackPreparer != null) {
            playbackPreparer.onPrepare(/* playWhenReady= */ true);
          } else {
            player.prepare();
          }
        } else if (player.getPlaybackState() == Player.STATE_ENDED) {
          seekTo(player, player.getCurrentMediaItemIndex(), C.TIME_UNSET);
        }
        Assertions.checkNotNull(player).play();
      }
    }
```
2025-02-16 21:44:49 +01:00
Stypox
064e1d39c7 Use the media browser implementation in PlayerService
Now the media browser queries are replied to by MediaBrowserImpl

Co-authored-by: Haggai Eran <haggai.eran@gmail.com>
2025-02-16 21:44:05 +01:00
Stypox
4c88a193bd Add MediaBrowserImpl
This class implements the media browser service interface as a standalone class for clearer separation of concerns (otherwise everything would need to go in PlayerService, since PlayerService overrides MediaBrowserServiceCompat)

Co-authored-by: Haggai Eran <haggai.eran@gmail.com>
Co-authored-by: Profpatsch <mail@profpatsch.de>
2025-02-16 21:43:46 +01:00
Stypox
3fcac10e7f Add MediaBrowserPlaybackPreparer
This class will receive the media URLs generated by [MediaBrowserImpl] and will start playback of the corresponding streams or playlists.

Co-authored-by: Haggai Eran <haggai.eran@gmail.com>
Co-authored-by: Profpatsch <mail@profpatsch.de>
2025-02-16 21:43:35 +01:00
Stypox
6cedd117fe Add StreamHistoryEntry.toStreamInfoItem()
Co-authored-by: Haggai Eran <haggai.eran@gmail.com>
2025-02-16 21:40:55 +01:00
Stypox
5eabcb52b5 Add getThumbnailUrl() to PlaylistLocalItem interface
Co-authored-by: Haggai Eran <haggai.eran@gmail.com>
2025-02-16 21:40:48 +01:00
Stypox
690b40d0c4 Allow creating PlayQueue from ListInfo and index 2025-02-16 21:40:47 +01:00
Stypox
9bb2c0b484 Add getPlaylist(id) to RemotePlaylistManager
Co-authored-by: Haggai Eran <haggai.eran@gmail.com>
2025-02-16 21:40:36 +01:00
Stypox
1e08cc8c8f Add MediaBrowserCommon with info item's and pages' IDs
Co-authored-by: Haggai Eran <haggai.eran@gmail.com>
2025-02-16 21:40:29 +01:00
Stypox
7d17468266 Instantiate media session and connector in PlayerService
This changes significantly how the MediaSessionCompat and MediaSessionConnector objects are used:
- now they are tied to the service and not to the player, and so they might be reused with multiple players (which should be allowed)
- now they can exist even if there is no player (which is fundamental to be able to answer media browser queries)
2025-02-16 21:40:13 +01:00
Stypox
5819546ea9 Have PlayerService implement MediaBrowserServiceCompat
Co-authored-by: Haggai Eran <haggai.eran@gmail.com>
2025-02-16 21:36:59 +01:00
Stypox
cfb6e114d6 Disable logs about view animations by default 2025-02-16 10:31:42 +01:00
Stypox
b764ad33c4 Drop some assumptions on how PlayerService is started and reused
Read the comments in the lines changed to understand more
2025-02-15 17:48:19 +01:00
Hosted Weblate
430b4eb916 Translated using Weblate (Persian)
Currently translated at 92.7% (686 of 740 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (84 of 84 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Georgian)

Currently translated at 83.3% (70 of 84 strings)

Translated using Weblate (Estonian)

Currently translated at 16.6% (14 of 84 strings)

Translated using Weblate (Estonian)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Mainfränkisch)

Currently translated at 1.0% (8 of 740 strings)

Translated using Weblate (Bavarian)

Currently translated at 3.9% (29 of 740 strings)

Translated using Weblate (German)

Currently translated at 100.0% (84 of 84 strings)

Added translation using Weblate (Mainfränkisch)

Translated using Weblate (Thai)

Currently translated at 36.6% (271 of 740 strings)

Translated using Weblate (Armenian)

Currently translated at 28.2% (209 of 740 strings)

Translated using Weblate (Georgian)

Currently translated at 85.7% (72 of 84 strings)

Translated using Weblate (Thai)

Currently translated at 34.3% (254 of 740 strings)

Translated using Weblate (Gujarati)

Currently translated at 11.3% (84 of 740 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (84 of 84 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (84 of 84 strings)

Translated using Weblate (Nepali)

Currently translated at 1.1% (1 of 84 strings)

Translated using Weblate (Slovak)

Currently translated at 100.0% (84 of 84 strings)

Translated using Weblate (French)

Currently translated at 100.0% (84 of 84 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (740 of 740 strings)

Translated using Weblate (Gujarati)

Currently translated at 11.0% (82 of 740 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (740 of 740 strings)

Co-authored-by: Alex25820 <alexs25820@gmail.com>
Co-authored-by: Bruno Fragoso <darth_signa@hotmail.com>
Co-authored-by: Davit Mayilyan <davit.mayilyan@protonmail.ch>
Co-authored-by: Emin Tufan Çetin <etcetin@gmail.com>
Co-authored-by: Garfield2150 <knd.garfield@gmail.com>
Co-authored-by: Ghost of Sparta <makesocialfoss32@keemail.me>
Co-authored-by: Goudarz Jafari <goudarz.jafari@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Kchenik Poudel <Kakapoudel7@gmail.com>
Co-authored-by: Kuko <kuko7@protonmail.ch>
Co-authored-by: Paul Sibila <p.aul@mail.de>
Co-authored-by: Priit Jõerüüt <hwlate@joeruut.com>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Temuri Doghonadze <temuri.doghonadze@gmail.com>
Co-authored-by: freddyLovesUs <compteperso@outlook.com>
Co-authored-by: રાજ ભાતેલીઆ <rajbhatelia@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/de/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/et/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/fr/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/hu/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/ka/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/ne/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/sk/
Translate-URL: https://hosted.weblate.org/projects/newpipe/metadata/sv/
Translation: NewPipe/Metadata
2025-02-15 13:08:00 +01:00
Thiago F. G. Albuquerque
2339f51ad4 [#11930] Share as YouTube temporary playlist
Initial commit.
2025-02-14 21:14:42 -03:00
315 changed files with 5871 additions and 2228 deletions

View File

@@ -72,8 +72,8 @@ jobs:
- api-level: 21
target: default
arch: x86
- api-level: 33
target: google_apis # emulator API 33 only exists with Google APIs
- api-level: 35
target: default
arch: x86_64
permissions:
@@ -111,6 +111,7 @@ jobs:
path: app/build/reports/androidTests/connected/**
sonar:
if: ${{ false }} # the key has expired and needs to be regenerated by the sonar admins
runs-on: ubuntu-latest
permissions:

View File

@@ -33,11 +33,11 @@ module.exports = async ({github, context}) => {
// Regex for finding images (simple variant) ![ALT_TEXT](https://*.githubusercontent.com/<number>/<variousHexStringsAnd->.<fileExtension>)
const REGEX_USER_CONTENT_IMAGE_LOOKUP = /\!\[([^\]]*)\]\((https:\/\/[-a-z0-9]+\.githubusercontent\.com\/\d+\/[-0-9a-f]{32,512}\.(jpg|gif|png))\)/gm;
const REGEX_ASSETS_IMAGE_LOCKUP = /\!\[([^\]]*)\]\((https:\/\/github\.com\/[-\w\d]+\/[-\w\d]+\/assets\/\d+\/[\-0-9a-f]{32,512})\)/gm;
const REGEX_ASSETS_IMAGE_LOOKUP = /\!\[([^\]]*)\]\((https:\/\/github\.com\/(?:user-attachments\/assets|[-\w\d]+\/[-\w\d]+\/assets\/\d+)\/[\-0-9a-f]{32,512})\)/gm;
// Check if we found something
let foundSimpleImages = REGEX_USER_CONTENT_IMAGE_LOOKUP.test(initialBody)
|| REGEX_ASSETS_IMAGE_LOCKUP.test(initialBody);
|| REGEX_ASSETS_IMAGE_LOOKUP.test(initialBody);
if (!foundSimpleImages) {
console.log('Found no simple images to process');
return;
@@ -52,7 +52,7 @@ module.exports = async ({github, context}) => {
// Try to find and replace the images with minimized ones
let newBody = await replaceAsync(initialBody, REGEX_USER_CONTENT_IMAGE_LOOKUP, minimizeAsync);
newBody = await replaceAsync(newBody, REGEX_ASSETS_IMAGE_LOCKUP, minimizeAsync);
newBody = await replaceAsync(newBody, REGEX_ASSETS_IMAGE_LOOKUP, minimizeAsync);
if (!wasMatchModified) {
console.log('Nothing was modified. Skipping update');

View File

@@ -1,20 +1,26 @@
<h3 align="center">We are planning to <i>rewrite</i> large chunks of the codebase, to bring about <a href="https://github.com/TeamNewPipe/NewPipe/discussions/10118">a new, modern and stable NewPipe</a>!</h3>
<h4 align="center">Please do <b>not</b> open pull requests for <i>new features</i> now, only bugfix PRs will be accepted.</h4>
<h3 align="center">We are <i>rewriting</i> large chunks of the codebase, to bring about <a href="https://newpipe.net/blog/pinned/announcement/newpipe-0.27.6-rewrite-team-states/#the-refactor">a modern and stable NewPipe</a>! You can download nightly builds <a href="https://github.com/TeamNewPipe/NewPipe-refactor-nightly/releases">here</a>.</h3>
<h4 align="center">Please work on the <code>refactor</code> branch if you want to contribute <i>new features</i>. The current codebase is in maintenance mode and will only receive <i>bugfixes</i>.</h4>
<p align="center"><a href="https://newpipe.net"><img src="assets/new_pipe_icon_5.png" width="150"></a></p>
<h2 align="center"><b>NewPipe</b></h2>
<h4 align="center">A libre lightweight streaming front-end for Android.</h4>
<p align="center"><a href="https://f-droid.org/packages/org.schabi.newpipe/"><img src="https://fdroid.gitlab.io/artwork/badge/get-it-on-en.svg" alt="Get it on F-Droid" height=80/></a></p>
<p align="center"><a href="https://f-droid.org/packages/org.schabi.newpipe/"><img src="https://fdroid.gitlab.io/artwork/badge/get-it-on-en.svg" alt="Get it on F-Droid" width=206/></a></p>
<p align="center">
<a href="https://github.com/TeamNewPipe/NewPipe/releases" alt="GitHub release"><img src="https://img.shields.io/github/release/TeamNewPipe/NewPipe.svg" ></a>
<a href="https://github.com/TeamNewPipe/NewPipe/releases" alt="GitHub NewPipe releases"><img src="https://img.shields.io/github/release/TeamNewPipe/NewPipe.svg" ></a>
<a href="https://github.com/TeamNewPipe/NewPipe-nightly/releases" alt="GitHub NewPipe nightly releases"><img src="https://img.shields.io/github/release/TeamNewPipe/NewPipe-nightly.svg?labelColor=purple&label=dev%20nightly"></a>
<a href="https://github.com/TeamNewPipe/NewPipe-refactor-nightly/releases" alt="GitHub NewPipe refactor nightly releases"><img src="https://img.shields.io/github/release/TeamNewPipe/NewPipe-refactor-nightly.svg?labelColor=purple&label=refactor%20nightly"></a>
<a href="https://www.gnu.org/licenses/gpl-3.0" alt="License: GPLv3"><img src="https://img.shields.io/badge/License-GPL%20v3-blue.svg"></a>
<a href="https://github.com/TeamNewPipe/NewPipe/actions" alt="Build Status"><img src="https://github.com/TeamNewPipe/NewPipe/workflows/CI/badge.svg?branch=dev&event=push"></a>
<a href="https://github.com/TeamNewPipe/NewPipe/actions" alt="Build Status"><img src="https://github.com/TeamNewPipe/NewPipe/actions/workflows/ci.yml/badge.svg?branch=dev&event=push"></a>
<a href="https://hosted.weblate.org/engage/newpipe/" alt="Translation Status"><img src="https://hosted.weblate.org/widgets/newpipe/-/svg-badge.svg"></a>
</p>
<p align="center">
<a href="https://web.libera.chat/#newpipe" alt="IRC channel: #newpipe"><img src="https://img.shields.io/badge/IRC%20chat-%23newpipe-brightgreen.svg"></a>
<a href="https://matrix.to/#/#newpipe:matrix.newpipe-ev.de" alt="Matrix channel: #newpipe"><img src="https://img.shields.io/badge/Matrix%20chat-%23newpipe-blue"></a>
</p>
<hr>
<p align="center"><a href="#screenshots">Screenshots</a> &bull; <a href="#supported-services">Supported Services</a> &bull; <a href="#description">Description</a> &bull; <a href="#features">Features</a> &bull; <a href="#installation-and-updates">Installation and updates</a> &bull; <a href="#contribution">Contribution</a> &bull; <a href="#donate">Donate</a> &bull; <a href="#license">License</a></p>
<p align="center"><a href="https://newpipe.net">Website</a> &bull; <a href="https://newpipe.net/blog/">Blog</a> &bull; <a href="https://newpipe.net/FAQ/">FAQ</a> &bull; <a href="https://newpipe.net/press/">Press</a></p>

View File

@@ -12,20 +12,20 @@ plugins {
}
android {
compileSdk 34
compileSdk 36
namespace 'org.schabi.newpipe'
defaultConfig {
applicationId "org.schabi.newpipe"
resValue "string", "app_name", "NewPipe"
minSdk 21
targetSdk 33
targetSdk 35
if (System.properties.containsKey('versionCodeOverride')) {
versionCode System.getProperty('versionCodeOverride') as Integer
} else {
versionCode 1004
versionCode 1005
}
versionName "0.27.7"
versionName "0.28.0"
if (System.properties.containsKey('versionNameSuffix')) {
versionNameSuffix System.getProperty('versionNameSuffix')
}
@@ -97,6 +97,10 @@ android {
androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
}
androidResources {
generateLocaleConfig = true
}
buildFeatures {
viewBinding true
buildConfig true
@@ -205,10 +209,12 @@ dependencies {
// Or you can use a commit you pushed to GitHub by just replacing TeamNewPipe with your GitHub
// name and the commit hash with the commit hash of the (pushed) commit you want to test
// This works thanks to JitPack: https://jitpack.io/
implementation 'com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751'
implementation 'com.github.TeamNewPipe:nanojson:e9d656ddb49a412a5a0a5d5ef20ca7ef09549996'
// WORKAROUND: if you get errors with the NewPipeExtractor dependency, replace `v0.24.3` with
// the corresponding commit hash, since JitPack is sometimes buggy
implementation 'com.github.TeamNewPipe.NewPipeExtractor:NewPipeExtractor:v0.24.6'
// the corresponding commit hash, since JitPack sometimes deletes artifacts.
// If theres already a git hash, just add more of it to the end (or remove a letter)
// to cause jitpack to regenerate the artifact.
implementation 'com.github.TeamNewPipe:NewPipeExtractor:0023b22095a2d62a60cdfc87f4b5cd85c8b266c3'
implementation 'com.github.TeamNewPipe:NoNonsense-FilePicker:5.0.0'
/** Checkstyle **/
@@ -219,7 +225,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:${kotlin_version}"
/** AndroidX **/
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.appcompat:appcompat:1.7.1'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.core:core-ktx:1.12.0'
@@ -274,7 +280,7 @@ dependencies {
implementation "com.github.lisawray.groupie:groupie-viewbinding:${groupieVersion}"
// Image loading
//noinspection GradleDependency --> 2.8 is the last version, not 2.71828!
//noinspection NewerVersionAvailable,GradleDependency --> 2.8 is the last version, not 2.71828!
implementation "com.squareup.picasso:picasso:2.8"
// Markdown library for Android

View File

@@ -12,6 +12,7 @@ import org.schabi.newpipe.extractor.ServiceList;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import java.util.Arrays;
import java.util.Objects;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -23,8 +24,23 @@ import static org.junit.Assert.assertTrue;
@LargeTest
public class ErrorInfoTest {
/**
* @param errorInfo the error info to access
* @return the private field errorInfo.message.stringRes using reflection
*/
private int getMessageFromErrorInfo(final ErrorInfo errorInfo)
throws NoSuchFieldException, IllegalAccessException {
final var message = ErrorInfo.class.getDeclaredField("message");
message.setAccessible(true);
final var messageValue = (ErrorInfo.Companion.ErrorMessage) message.get(errorInfo);
final var stringRes = ErrorInfo.Companion.ErrorMessage.class.getDeclaredField("stringRes");
stringRes.setAccessible(true);
return (int) Objects.requireNonNull(stringRes.get(messageValue));
}
@Test
public void errorInfoTestParcelable() {
public void errorInfoTestParcelable() throws NoSuchFieldException, IllegalAccessException {
final ErrorInfo info = new ErrorInfo(new ParsingException("Hello"),
UserAction.USER_REPORT, "request", ServiceList.YouTube.getServiceId());
// Obtain a Parcel object and write the parcelable object to it:
@@ -39,7 +55,7 @@ public class ErrorInfoTest {
assertEquals(ServiceList.YouTube.getServiceInfo().getName(),
infoFromParcel.getServiceName());
assertEquals("request", infoFromParcel.getRequest());
assertEquals(R.string.parsing_error, infoFromParcel.getMessageStringId());
assertEquals(R.string.parsing_error, getMessageFromErrorInfo(infoFromParcel));
parcel.recycle();
}

View File

@@ -9,6 +9,8 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<!-- We need to be able to open links in the browser on API 30+ -->
@@ -57,6 +59,15 @@
</intent-filter>
</receiver>
<service
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
android:enabled="false"
android:exported="false">
<meta-data
android:name="autoStoreLocales"
android:value="true" />
</service>
<service
android:name=".player.PlayerService"
android:exported="true"
@@ -64,6 +75,9 @@
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService"/>
</intent-filter>
</service>
<activity
@@ -82,9 +96,22 @@
android:exported="false"
android:label="@string/title_activity_about" />
<service android:name=".local.subscription.services.SubscriptionsImportService" />
<service android:name=".local.subscription.services.SubscriptionsExportService" />
<service android:name=".local.feed.service.FeedLoadService" />
<service
android:name=".local.subscription.services.SubscriptionsImportService"
android:foregroundServiceType="dataSync" />
<service
android:name=".local.subscription.services.SubscriptionsExportService"
android:foregroundServiceType="dataSync" />
<service
android:name=".local.feed.service.FeedLoadService"
android:foregroundServiceType="dataSync" />
<service
android:name="androidx.work.impl.foreground.SystemForegroundService"
android:foregroundServiceType="dataSync"
tools:node="merge" />
<activity
android:name=".PanicResponderActivity"
@@ -116,7 +143,8 @@
android:label="@string/app_name"
android:launchMode="singleTask" />
<service android:name="us.shandian.giga.service.DownloadManagerService" />
<service android:name="us.shandian.giga.service.DownloadManagerService"
android:foregroundServiceType="dataSync" />
<activity
android:name=".util.FilePickerActivityHelper"
@@ -409,6 +437,7 @@
</activity>
<service
android:name=".RouterActivity$FetcherService"
android:foregroundServiceType="dataSync"
android:exported="false" />
<!-- opting out of sending metrics to Google in Android System WebView -->
@@ -424,5 +453,10 @@
<meta-data
android:name="com.samsung.android.multidisplay.keep_process_alive"
android:value="true" />
<!-- Android Auto -->
<meta-data android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc" />
<meta-data android:name="com.google.android.gms.car.notification.SmallIcon"
android:resource="@mipmap/ic_launcher" />
</application>
</manifest>

View File

@@ -102,7 +102,7 @@ public class App extends Application {
NewPipe.init(getDownloader(),
Localization.getPreferredLocalization(this),
Localization.getPreferredContentCountry(this));
Localization.initPrettyTime(Localization.resolvePrettyTime(getApplicationContext()));
Localization.initPrettyTime(Localization.resolvePrettyTime());
BridgeStateSaverInitializer.init(this);
StateSaver.init(this);

View File

@@ -29,7 +29,7 @@ import okhttp3.ResponseBody;
public final class DownloaderImpl extends Downloader {
public static final String USER_AGENT =
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0";
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0";
public static final String YOUTUBE_RESTRICTED_MODE_COOKIE_KEY =
"youtube_restricted_mode_key";
public static final String YOUTUBE_RESTRICTED_MODE_COOKIE = "PREF=f2=8000000";

File diff suppressed because it is too large Load Diff

View File

@@ -58,20 +58,10 @@ import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.StreamingService.LinkType;
import org.schabi.newpipe.extractor.channel.ChannelInfo;
import org.schabi.newpipe.extractor.exceptions.AgeRestrictedContentException;
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.GeographicRestrictionException;
import org.schabi.newpipe.extractor.exceptions.PaidContentException;
import org.schabi.newpipe.extractor.exceptions.PrivateContentException;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.extractor.exceptions.SoundCloudGoPlusContentException;
import org.schabi.newpipe.extractor.exceptions.YoutubeMusicPremiumContentException;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.ktx.ExceptionUtils;
import org.schabi.newpipe.local.dialog.PlaylistDialog;
import org.schabi.newpipe.player.PlayerType;
import org.schabi.newpipe.player.helper.PlayerHelper;
@@ -84,7 +74,6 @@ import org.schabi.newpipe.util.ChannelTabHelper;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.DeviceUtils;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.ThemeHelper;
@@ -132,7 +121,6 @@ public class RouterActivity extends AppCompatActivity {
ThemeHelper.setDayNightMode(this);
setTheme(ThemeHelper.isLightThemeSelected(this)
? R.style.RouterActivityThemeLight : R.style.RouterActivityThemeDark);
Localization.assureCorrectAppLanguage(this);
// Pass-through touch events to background activities
// so that our transparent window won't lock UI in the mean time
@@ -262,7 +250,8 @@ public class RouterActivity extends AppCompatActivity {
showUnsupportedUrlDialog(url);
}
}, throwable -> handleError(this, new ErrorInfo(throwable,
UserAction.SHARE_TO_NEWPIPE, "Getting service from url: " + url))));
UserAction.SHARE_TO_NEWPIPE, "Getting service from url: " + url,
null, url))));
}
/**
@@ -271,40 +260,19 @@ public class RouterActivity extends AppCompatActivity {
* @param errorInfo the error information
*/
private static void handleError(final Context context, final ErrorInfo errorInfo) {
if (errorInfo.getThrowable() != null) {
errorInfo.getThrowable().printStackTrace();
}
if (errorInfo.getThrowable() instanceof ReCaptchaException) {
if (errorInfo.getRecaptchaUrl() != null) {
Toast.makeText(context, R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show();
// Starting ReCaptcha Challenge Activity
final Intent intent = new Intent(context, ReCaptchaActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(ReCaptchaActivity.RECAPTCHA_URL_EXTRA, errorInfo.getRecaptchaUrl());
context.startActivity(intent);
} else if (errorInfo.getThrowable() != null
&& ExceptionUtils.isNetworkRelated(errorInfo.getThrowable())) {
Toast.makeText(context, R.string.network_error, Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof AgeRestrictedContentException) {
Toast.makeText(context, R.string.restricted_video_no_stream,
Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof GeographicRestrictionException) {
Toast.makeText(context, R.string.georestricted_content, Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof PaidContentException) {
Toast.makeText(context, R.string.paid_content, Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof PrivateContentException) {
Toast.makeText(context, R.string.private_content, Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof SoundCloudGoPlusContentException) {
Toast.makeText(context, R.string.soundcloud_go_plus_content,
Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof YoutubeMusicPremiumContentException) {
Toast.makeText(context, R.string.youtube_music_premium_content,
Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof ContentNotAvailableException) {
Toast.makeText(context, R.string.content_not_available, Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof ContentNotSupportedException) {
Toast.makeText(context, R.string.content_not_supported, Toast.LENGTH_LONG).show();
} else {
} else if (errorInfo.isReportable()) {
ErrorUtil.createNotification(context, errorInfo);
} else {
// this exception does not usually indicate a problem that should be reported,
// so just show a toast instead of the notification
Toast.makeText(context, errorInfo.getMessage(context), Toast.LENGTH_LONG).show();
}
if (context instanceof RouterActivity) {
@@ -667,7 +635,8 @@ public class RouterActivity extends AppCompatActivity {
startActivity(intent);
finish();
}, throwable -> handleError(this, new ErrorInfo(throwable,
UserAction.SHARE_TO_NEWPIPE, "Starting info activity: " + currentUrl)))
UserAction.SHARE_TO_NEWPIPE, "Starting info activity: " + currentUrl,
null, currentUrl)))
);
return;
}
@@ -854,10 +823,10 @@ public class RouterActivity extends AppCompatActivity {
})
)),
throwable -> runOnVisible(ctx -> handleError(ctx, new ErrorInfo(
throwable,
UserAction.REQUESTED_STREAM,
throwable, UserAction.REQUESTED_STREAM,
"Tried to add " + currentUrl + " to a playlist",
((RouterActivity) ctx).currentService.getServiceId())
((RouterActivity) ctx).currentService.getServiceId(),
currentUrl)
))
)
);
@@ -997,7 +966,7 @@ public class RouterActivity extends AppCompatActivity {
}
}, throwable -> handleError(this, new ErrorInfo(throwable, finalUserAction,
choice.url + " opened with " + choice.playerChoice,
choice.serviceId)));
choice.serviceId, choice.url)));
}
}

View File

@@ -16,14 +16,12 @@ import org.schabi.newpipe.BuildConfig
import org.schabi.newpipe.R
import org.schabi.newpipe.databinding.ActivityAboutBinding
import org.schabi.newpipe.databinding.FragmentAboutBinding
import org.schabi.newpipe.util.Localization
import org.schabi.newpipe.util.ThemeHelper
import org.schabi.newpipe.util.external_communication.ShareUtils
class AboutActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
Localization.assureCorrectAppLanguage(this)
super.onCreate(savedInstanceState)
ThemeHelper.setTheme(this)
title = getString(R.string.title_activity_about)

View File

@@ -19,7 +19,6 @@ import org.schabi.newpipe.R
import org.schabi.newpipe.databinding.FragmentLicensesBinding
import org.schabi.newpipe.databinding.ItemSoftwareComponentBinding
import org.schabi.newpipe.ktx.parcelableArrayList
import org.schabi.newpipe.util.Localization
import org.schabi.newpipe.util.external_communication.ShareUtils
/**
@@ -100,7 +99,6 @@ class LicenseFragment : Fragment() {
val webView = WebView(context)
webView.loadData(webViewData, "text/html; charset=UTF-8", "base64")
Localization.assureCorrectAppLanguage(context)
val builder = AlertDialog.Builder(requireContext())
.setTitle(softwareComponent.name)
.setView(webView)

View File

@@ -3,6 +3,8 @@ package org.schabi.newpipe.database.history.model
import androidx.room.ColumnInfo
import androidx.room.Embedded
import org.schabi.newpipe.database.stream.model.StreamEntity
import org.schabi.newpipe.extractor.stream.StreamInfoItem
import org.schabi.newpipe.util.image.ImageStrategy
import java.time.OffsetDateTime
data class StreamHistoryEntry(
@@ -27,4 +29,17 @@ data class StreamHistoryEntry(
return this.streamEntity.uid == other.streamEntity.uid && streamId == other.streamId &&
accessDate.isEqual(other.accessDate)
}
fun toStreamInfoItem(): StreamInfoItem =
StreamInfoItem(
streamEntity.serviceId,
streamEntity.url,
streamEntity.title,
streamEntity.streamType,
).apply {
duration = streamEntity.duration
uploaderName = streamEntity.uploader
uploaderUrl = streamEntity.uploaderUrl
thumbnails = ImageStrategy.dbUrlToImageList(streamEntity.thumbnailUrl)
}
}

View File

@@ -1,5 +1,7 @@
package org.schabi.newpipe.database.playlist;
import androidx.annotation.Nullable;
import org.schabi.newpipe.database.LocalItem;
public interface PlaylistLocalItem extends LocalItem {
@@ -10,4 +12,7 @@ public interface PlaylistLocalItem extends LocalItem {
long getUid();
void setDisplayIndex(long displayIndex);
@Nullable
String getThumbnailUrl();
}

View File

@@ -9,6 +9,8 @@ import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_THUMBNAIL_STREAM_ID;
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_THUMBNAIL_URL;
import androidx.annotation.Nullable;
public class PlaylistMetadataEntry implements PlaylistLocalItem {
public static final String PLAYLIST_STREAM_COUNT = "streamCount";
@@ -71,4 +73,10 @@ public class PlaylistMetadataEntry implements PlaylistLocalItem {
public void setDisplayIndex(final long displayIndex) {
this.displayIndex = displayIndex;
}
@Nullable
@Override
public String getThumbnailUrl() {
return thumbnailUrl;
}
}

View File

@@ -34,7 +34,7 @@ public interface PlaylistRemoteDAO extends BasicDAO<PlaylistRemoteEntity> {
@Query("SELECT * FROM " + REMOTE_PLAYLIST_TABLE + " WHERE "
+ REMOTE_PLAYLIST_ID + " = :playlistId")
Flowable<List<PlaylistRemoteEntity>> getPlaylist(long playlistId);
Flowable<PlaylistRemoteEntity> getPlaylist(long playlistId);
@Query("SELECT * FROM " + REMOTE_PLAYLIST_TABLE + " WHERE "
+ REMOTE_PLAYLIST_URL + " = :url AND " + REMOTE_PLAYLIST_SERVICE_ID + " = :serviceId")

View File

@@ -2,6 +2,7 @@ package org.schabi.newpipe.database.playlist.model;
import android.text.TextUtils;
import androidx.annotation.Nullable;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.Ignore;
@@ -134,6 +135,8 @@ public class PlaylistRemoteEntity implements PlaylistLocalItem {
this.name = name;
}
@Nullable
@Override
public String getThumbnailUrl() {
return thumbnailUrl;
}

View File

@@ -20,8 +20,6 @@ import org.schabi.newpipe.views.FocusOverlayView;
import us.shandian.giga.service.DownloadManagerService;
import us.shandian.giga.ui.fragment.MissionsFragment;
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
public class DownloadActivity extends AppCompatActivity {
private static final String MISSIONS_FRAGMENT_TAG = "fragment_tag";
@@ -33,7 +31,6 @@ public class DownloadActivity extends AppCompatActivity {
i.setClass(this, DownloadManagerService.class);
startService(i);
assureCorrectAppLanguage(this);
ThemeHelper.setTheme(this);
super.onCreate(savedInstanceState);

View File

@@ -2,7 +2,6 @@ package org.schabi.newpipe.download;
import static org.schabi.newpipe.extractor.stream.DeliveryMethod.PROGRESSIVE_HTTP;
import static org.schabi.newpipe.util.ListHelper.getStreamsOfSpecifiedDelivery;
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
import android.app.Activity;
import android.content.ComponentName;
@@ -390,8 +389,7 @@ public class DownloadDialog extends DialogFragment
}
}, throwable -> ErrorUtil.showSnackbar(context,
new ErrorInfo(throwable, UserAction.DOWNLOAD_OPEN_DIALOG,
"Downloading video stream size",
currentInfo.getServiceId()))));
"Downloading video stream size", currentInfo))));
disposables.add(StreamInfoWrapper.fetchMoreInfoForWrapper(getWrappedAudioStreams())
.subscribe(result -> {
if (dialogBinding.videoAudioGroup.getCheckedRadioButtonId()
@@ -400,8 +398,7 @@ public class DownloadDialog extends DialogFragment
}
}, throwable -> ErrorUtil.showSnackbar(context,
new ErrorInfo(throwable, UserAction.DOWNLOAD_OPEN_DIALOG,
"Downloading audio stream size",
currentInfo.getServiceId()))));
"Downloading audio stream size", currentInfo))));
disposables.add(StreamInfoWrapper.fetchMoreInfoForWrapper(wrappedSubtitleStreams)
.subscribe(result -> {
if (dialogBinding.videoAudioGroup.getCheckedRadioButtonId()
@@ -410,8 +407,7 @@ public class DownloadDialog extends DialogFragment
}
}, throwable -> ErrorUtil.showSnackbar(context,
new ErrorInfo(throwable, UserAction.DOWNLOAD_OPEN_DIALOG,
"Downloading subtitle stream size",
currentInfo.getServiceId()))));
"Downloading subtitle stream size", currentInfo))));
}
private void setupAudioTrackSpinner() {
@@ -751,7 +747,6 @@ public class DownloadDialog extends DialogFragment
}
private void showFailedDialog(@StringRes final int msg) {
assureCorrectAppLanguage(requireContext());
new AlertDialog.Builder(context)
.setTitle(R.string.general_error)
.setMessage(msg)

View File

@@ -36,8 +36,8 @@ public class AcraReportSender implements ReportSender {
ErrorUtil.openActivity(context, new ErrorInfo(
new String[]{report.getString(ReportField.STACK_TRACE)},
UserAction.UI_ERROR,
ErrorInfo.SERVICE_NONE,
"ACRA report",
null,
R.string.app_ui_crash));
}
}

View File

@@ -1,7 +1,5 @@
package org.schabi.newpipe.error;
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
@@ -79,7 +77,6 @@ public class ErrorActivity extends AppCompatActivity {
@Override
protected void onCreate(final Bundle savedInstanceState) {
assureCorrectAppLanguage(this);
super.onCreate(savedInstanceState);
ThemeHelper.setDayNightMode(this);
@@ -118,7 +115,7 @@ public class ErrorActivity extends AppCompatActivity {
// normal bugreport
buildInfo(errorInfo);
activityErrorBinding.errorMessageView.setText(errorInfo.getMessageStringId());
activityErrorBinding.errorMessageView.setText(errorInfo.getMessage(this));
activityErrorBinding.errorView.setText(formErrorText(errorInfo.getStackTraces()));
// print stack trace once again for debugging:
@@ -306,7 +303,7 @@ public class ErrorActivity extends AppCompatActivity {
}
private String getAppLanguage() {
return Localization.getAppLocale(getApplicationContext()).toString();
return Localization.getAppLocale().toString();
}
private String getOsString() {

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,6 @@ package org.schabi.newpipe.error
import android.content.Context
import android.content.Intent
import android.util.Log
import android.view.View
import android.widget.Button
import android.widget.TextView
@@ -14,28 +13,14 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.disposables.Disposable
import org.schabi.newpipe.MainActivity
import org.schabi.newpipe.R
import org.schabi.newpipe.extractor.exceptions.AccountTerminatedException
import org.schabi.newpipe.extractor.exceptions.AgeRestrictedContentException
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException
import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException
import org.schabi.newpipe.extractor.exceptions.GeographicRestrictionException
import org.schabi.newpipe.extractor.exceptions.PaidContentException
import org.schabi.newpipe.extractor.exceptions.PrivateContentException
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException
import org.schabi.newpipe.extractor.exceptions.SoundCloudGoPlusContentException
import org.schabi.newpipe.extractor.exceptions.YoutubeMusicPremiumContentException
import org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty
import org.schabi.newpipe.ktx.animate
import org.schabi.newpipe.ktx.isInterruptedCaused
import org.schabi.newpipe.ktx.isNetworkRelated
import org.schabi.newpipe.util.ServiceHelper
import org.schabi.newpipe.util.external_communication.ShareUtils
import java.util.concurrent.TimeUnit
class ErrorPanelHelper(
private val fragment: Fragment,
rootView: View,
onRetry: Runnable
onRetry: Runnable?,
) {
private val context: Context = rootView.context!!
@@ -56,12 +41,15 @@ class ErrorPanelHelper(
errorPanelRoot.findViewById(R.id.error_open_in_browser)
private var errorDisposable: Disposable? = null
private var retryShouldBeShown: Boolean = (onRetry != null)
init {
errorDisposable = errorRetryButton.clicks()
.debounce(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe { onRetry.run() }
if (onRetry != null) {
errorDisposable = errorRetryButton.clicks()
.debounce(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe { onRetry.run() }
}
}
private fun ensureDefaultVisibility() {
@@ -75,64 +63,32 @@ class ErrorPanelHelper(
}
fun showError(errorInfo: ErrorInfo) {
if (errorInfo.throwable != null && errorInfo.throwable!!.isInterruptedCaused) {
if (DEBUG) {
Log.w(TAG, "onError() isInterruptedCaused! = [$errorInfo.throwable]")
}
return
}
ensureDefaultVisibility()
errorTextView.text = errorInfo.getMessage(context)
if (errorInfo.throwable is ReCaptchaException) {
errorTextView.setText(R.string.recaptcha_request_toast)
showAndSetErrorButtonAction(
R.string.recaptcha_solve
) {
if (errorInfo.recaptchaUrl != null) {
showAndSetErrorButtonAction(R.string.recaptcha_solve) {
// Starting ReCaptcha Challenge Activity
val intent = Intent(context, ReCaptchaActivity::class.java)
intent.putExtra(
ReCaptchaActivity.RECAPTCHA_URL_EXTRA,
(errorInfo.throwable as ReCaptchaException).url
)
intent.putExtra(ReCaptchaActivity.RECAPTCHA_URL_EXTRA, errorInfo.recaptchaUrl)
fragment.startActivityForResult(intent, ReCaptchaActivity.RECAPTCHA_REQUEST)
errorActionButton.setOnClickListener(null)
}
errorRetryButton.isVisible = true
showAndSetOpenInBrowserButtonAction(errorInfo)
} else if (errorInfo.throwable is AccountTerminatedException) {
errorTextView.setText(R.string.account_terminated)
if (!isNullOrEmpty((errorInfo.throwable as AccountTerminatedException).message)) {
errorServiceInfoTextView.text = context.resources.getString(
R.string.service_provides_reason,
ServiceHelper.getSelectedService(context)?.serviceInfo?.name ?: "<unknown>"
)
errorServiceInfoTextView.isVisible = true
errorServiceExplanationTextView.text =
(errorInfo.throwable as AccountTerminatedException).message
errorServiceExplanationTextView.isVisible = true
}
} else {
showAndSetErrorButtonAction(
R.string.error_snackbar_action
) {
} else if (errorInfo.isReportable) {
showAndSetErrorButtonAction(R.string.error_snackbar_action) {
ErrorUtil.openActivity(context, errorInfo)
}
}
errorTextView.setText(getExceptionDescription(errorInfo.throwable))
if (errorInfo.isRetryable) {
errorRetryButton.isVisible = retryShouldBeShown
}
if (errorInfo.throwable !is ContentNotAvailableException &&
errorInfo.throwable !is ContentNotSupportedException
) {
// show retry button only for content which is not unavailable or unsupported
errorRetryButton.isVisible = true
if (errorInfo.openInBrowserUrl != null) {
errorOpenInBrowserButton.isVisible = true
errorOpenInBrowserButton.setOnClickListener {
ShareUtils.openUrlInBrowser(context, errorInfo.openInBrowserUrl)
}
showAndSetOpenInBrowserButtonAction(errorInfo)
}
setRootVisible()
@@ -150,15 +106,6 @@ class ErrorPanelHelper(
errorActionButton.setOnClickListener(listener)
}
fun showAndSetOpenInBrowserButtonAction(
errorInfo: ErrorInfo
) {
errorOpenInBrowserButton.isVisible = true
errorOpenInBrowserButton.setOnClickListener {
ShareUtils.openUrlInBrowser(context, errorInfo.request)
}
}
fun showTextError(errorString: String) {
ensureDefaultVisibility()
@@ -189,27 +136,5 @@ class ErrorPanelHelper(
companion object {
val TAG: String = ErrorPanelHelper::class.simpleName!!
val DEBUG: Boolean = MainActivity.DEBUG
@StringRes
fun getExceptionDescription(throwable: Throwable?): Int {
return when (throwable) {
is AgeRestrictedContentException -> R.string.restricted_video_no_stream
is GeographicRestrictionException -> R.string.georestricted_content
is PaidContentException -> R.string.paid_content
is PrivateContentException -> R.string.private_content
is SoundCloudGoPlusContentException -> R.string.soundcloud_go_plus_content
is YoutubeMusicPremiumContentException -> R.string.youtube_music_premium_content
is ContentNotAvailableException -> R.string.content_not_available
is ContentNotSupportedException -> R.string.content_not_supported
else -> {
// show retry button only for content which is not unavailable or unsupported
if (throwable != null && throwable.isNetworkRelated) {
R.string.network_error
} else {
R.string.error_snackbar_message
}
}
}
}
}
}

View File

@@ -10,8 +10,11 @@ import android.widget.Toast
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.app.PendingIntentCompat
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.preference.PreferenceManager
import com.google.android.material.snackbar.Snackbar
import org.schabi.newpipe.MainActivity
import org.schabi.newpipe.R
/**
@@ -35,12 +38,20 @@ class ErrorUtil {
* activity (since the workflow would be interrupted anyway in that case). So never use this
* for background services.
*
* If the crashed occurred while the app was in the background open a notification instead
*
* @param context the context to use to start the new activity
* @param errorInfo the error info to be reported
*/
@JvmStatic
fun openActivity(context: Context, errorInfo: ErrorInfo) {
context.startActivity(getErrorActivityIntent(context, errorInfo))
if (PreferenceManager.getDefaultSharedPreferences(context)
.getBoolean(MainActivity.KEY_IS_IN_BACKGROUND, true)
) {
createNotification(context, errorInfo)
} else {
context.startActivity(getErrorActivityIntent(context, errorInfo))
}
}
/**
@@ -111,7 +122,7 @@ class ErrorUtil {
)
.setSmallIcon(R.drawable.ic_bug_report)
.setContentTitle(context.getString(R.string.error_report_notification_title))
.setContentText(context.getString(errorInfo.messageStringId))
.setContentText(errorInfo.getMessage(context))
.setAutoCancel(true)
.setContentIntent(
PendingIntentCompat.getActivity(
@@ -126,9 +137,11 @@ class ErrorUtil {
NotificationManagerCompat.from(context)
.notify(ERROR_REPORT_NOTIFICATION_ID, notificationBuilder.build())
// since the notification is silent, also show a toast, otherwise the user is confused
Toast.makeText(context, R.string.error_report_notification_toast, Toast.LENGTH_SHORT)
.show()
ContextCompat.getMainExecutor(context).execute {
// since the notification is silent, also show a toast, otherwise the user is confused
Toast.makeText(context, R.string.error_report_notification_toast, Toast.LENGTH_SHORT)
.show()
}
}
private fun getErrorActivityIntent(context: Context, errorInfo: ErrorInfo): Intent {
@@ -143,10 +156,10 @@ class ErrorUtil {
// fallback to showing a notification if no root view is available
createNotification(context, errorInfo)
} else {
Snackbar.make(rootView, R.string.error_snackbar_message, Snackbar.LENGTH_LONG)
Snackbar.make(rootView, errorInfo.getMessage(context), Snackbar.LENGTH_LONG)
.setActionTextColor(Color.YELLOW)
.setAction(context.getString(R.string.error_snackbar_action).uppercase()) {
openActivity(context, errorInfo)
context.startActivity(getErrorActivityIntent(context, errorInfo))
}.show()
}
}

View File

@@ -32,7 +32,10 @@ public enum UserAction {
PREFERENCES_MIGRATION("migration of preferences"),
SHARE_TO_NEWPIPE("share to newpipe"),
CHECK_FOR_NEW_APP_VERSION("check for new app version"),
OPEN_INFO_ITEM_DIALOG("open info item dialog");
OPEN_INFO_ITEM_DIALOG("open info item dialog"),
GETTING_MAIN_SCREEN_TAB("getting main screen tab"),
PLAY_ON_POPUP("play on popup"),
SUBSCRIPTIONS("loading subscriptions");
private final String message;

View File

@@ -7,16 +7,57 @@ import android.view.ViewGroup;
import androidx.annotation.Nullable;
import com.evernote.android.state.State;
import org.schabi.newpipe.BaseFragment;
import org.schabi.newpipe.R;
import org.schabi.newpipe.error.ErrorInfo;
import org.schabi.newpipe.error.ErrorPanelHelper;
public class BlankFragment extends BaseFragment {
@State
@Nullable
ErrorInfo errorInfo;
@Nullable
ErrorPanelHelper errorPanel = null;
/**
* Builds a blank fragment that just says the app name and suggests clicking on search.
*/
public BlankFragment() {
this(null);
}
/**
* @param errorInfo if null acts like {@link BlankFragment}, else shows an error panel.
*/
public BlankFragment(@Nullable final ErrorInfo errorInfo) {
this.errorInfo = errorInfo;
}
@Nullable
@Override
public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container,
final Bundle savedInstanceState) {
setTitle("NewPipe");
return inflater.inflate(R.layout.fragment_blank, container, false);
final View view = inflater.inflate(R.layout.fragment_blank, container, false);
if (errorInfo != null) {
errorPanel = new ErrorPanelHelper(this, view, null);
errorPanel.showError(errorInfo);
view.findViewById(R.id.blank_page_content).setVisibility(View.GONE);
}
return view;
}
@Override
public void onDestroyView() {
super.onDestroyView();
if (errorPanel != null) {
errorPanel.dispose();
errorPanel = null;
}
}
@Override

View File

@@ -36,8 +36,9 @@ import com.google.android.material.tabs.TabLayout;
import org.schabi.newpipe.BaseFragment;
import org.schabi.newpipe.R;
import org.schabi.newpipe.databinding.FragmentMainBinding;
import org.schabi.newpipe.error.ErrorInfo;
import org.schabi.newpipe.error.ErrorUtil;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.error.UserAction;
import org.schabi.newpipe.local.playlist.LocalPlaylistFragment;
import org.schabi.newpipe.settings.tabs.Tab;
import org.schabi.newpipe.settings.tabs.TabsManager;
@@ -303,9 +304,9 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
final Fragment fragment;
try {
fragment = tab.getFragment(context);
} catch (final ExtractionException e) {
ErrorUtil.showUiErrorSnackbar(context, "Getting fragment item", e);
return new BlankFragment();
} catch (final Throwable t) {
return new BlankFragment(new ErrorInfo(t, UserAction.GETTING_MAIN_SCREEN_TAB,
"Tab " + tab.getClass().getSimpleName() + ":" + tab.getTabName(context)));
}
if (fragment instanceof BaseFragment) {

View File

@@ -93,7 +93,7 @@ public class DescriptionFragment extends BaseDescriptionFragment {
if (streamInfo.getLanguageInfo() != null) {
addMetadataItem(inflater, layout, false, R.string.metadata_language,
streamInfo.getLanguageInfo().getDisplayLanguage(getAppLocale(getContext())));
streamInfo.getLanguageInfo().getDisplayLanguage(getAppLocale()));
}
addMetadataItem(inflater, layout, true, R.string.metadata_support,

View File

@@ -93,6 +93,7 @@ import org.schabi.newpipe.local.dialog.PlaylistDialog;
import org.schabi.newpipe.local.history.HistoryRecordManager;
import org.schabi.newpipe.local.playlist.LocalPlaylistFragment;
import org.schabi.newpipe.player.Player;
import org.schabi.newpipe.player.PlayerIntentType;
import org.schabi.newpipe.player.PlayerService;
import org.schabi.newpipe.player.PlayerType;
import org.schabi.newpipe.player.event.OnKeyDownListener;
@@ -236,11 +237,14 @@ public final class VideoDetailFragment
// Service management
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onServiceConnected(final Player connectedPlayer,
final PlayerService connectedPlayerService,
final boolean playAfterConnect) {
player = connectedPlayer;
public void onServiceConnected(@NonNull final PlayerService connectedPlayerService) {
playerService = connectedPlayerService;
}
@Override
public void onPlayerConnected(@NonNull final Player connectedPlayer,
final boolean playAfterConnect) {
player = connectedPlayer;
// It will do nothing if the player is not in fullscreen mode
hideSystemUiIfNeeded();
@@ -272,11 +276,18 @@ public final class VideoDetailFragment
updateOverlayPlayQueueButtonVisibility();
}
@Override
public void onPlayerDisconnected() {
player = null;
// the binding could be null at this point, if the app is finishing
if (binding != null) {
restoreDefaultBrightness();
}
}
@Override
public void onServiceDisconnected() {
playerService = null;
player = null;
restoreDefaultBrightness();
}
@@ -866,7 +877,7 @@ public final class VideoDetailFragment
}
}
}, throwable -> showError(new ErrorInfo(throwable, UserAction.REQUESTED_STREAM,
url == null ? "no url" : url, serviceId)));
url == null ? "no url" : url, serviceId, url)));
}
/*//////////////////////////////////////////////////////////////////////////
@@ -1156,8 +1167,12 @@ public final class VideoDetailFragment
final PlayQueue queue = setupPlayQueueForIntent(false);
tryAddVideoPlayerView();
final Intent playerIntent = NavigationHelper.getPlayerIntent(requireContext(),
PlayerService.class, queue, true, autoPlayEnabled);
final Context context = requireContext();
final Intent playerIntent =
NavigationHelper.getPlayerIntent(context, PlayerService.class, queue,
PlayerIntentType.AllOthers)
.putExtra(Player.PLAY_WHEN_READY, autoPlayEnabled)
.putExtra(Player.RESUME_PLAYBACK, true);
ContextCompat.startForegroundService(activity, playerIntent);
}
@@ -1413,7 +1428,8 @@ public final class VideoDetailFragment
intentFilter.addAction(ACTION_SHOW_MAIN_PLAYER);
intentFilter.addAction(ACTION_HIDE_MAIN_PLAYER);
intentFilter.addAction(ACTION_PLAYER_STARTED);
activity.registerReceiver(broadcastReceiver, intentFilter);
ContextCompat.registerReceiver(activity, broadcastReceiver, intentFilter,
ContextCompat.RECEIVER_EXPORTED);
}
@@ -1582,8 +1598,8 @@ public final class VideoDetailFragment
}
if (!info.getErrors().isEmpty()) {
showSnackBarError(new ErrorInfo(info.getErrors(),
UserAction.REQUESTED_STREAM, info.getUrl(), info));
showSnackBarError(new ErrorInfo(info.getErrors(), UserAction.REQUESTED_STREAM,
"Some info not extracted: " + info.getUrl(), info));
}
}
@@ -1848,13 +1864,16 @@ public final class VideoDetailFragment
@Override
public void onServiceStopped() {
setOverlayPlayPauseImage(false);
if (currentInfo != null) {
updateOverlayData(currentInfo.getName(),
currentInfo.getUploaderName(),
currentInfo.getThumbnails());
// the binding could be null at this point, if the app is finishing
if (binding != null) {
setOverlayPlayPauseImage(false);
if (currentInfo != null) {
updateOverlayData(currentInfo.getName(),
currentInfo.getUploaderName(),
currentInfo.getThumbnails());
}
updateOverlayPlayQueueButtonVisibility();
}
updateOverlayPlayQueueButtonVisibility();
}
@Override

View File

@@ -8,6 +8,7 @@ import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.evernote.android.state.State;
@@ -42,6 +43,7 @@ public abstract class BaseListInfoFragment<I extends InfoItem, L extends ListInf
private final UserAction errorUserAction;
protected L currentInfo;
@Nullable
protected Page currentNextPage;
protected Disposable currentWorker;
@@ -151,7 +153,7 @@ public abstract class BaseListInfoFragment<I extends InfoItem, L extends ListInf
handleResult(result);
}, throwable ->
showError(new ErrorInfo(throwable, errorUserAction,
"Start loading: " + url, serviceId)));
"Start loading: " + url, serviceId, url)));
}
/**
@@ -182,7 +184,7 @@ public abstract class BaseListInfoFragment<I extends InfoItem, L extends ListInf
handleNextItems(infoItemsPage);
}, (@NonNull Throwable throwable) ->
dynamicallyShowErrorPanelOrSnackbar(new ErrorInfo(throwable,
errorUserAction, "Loading more items: " + url, serviceId)));
errorUserAction, "Loading more items: " + url, serviceId, url)));
}
private void forbidDownwardFocusScroll() {
@@ -208,7 +210,7 @@ public abstract class BaseListInfoFragment<I extends InfoItem, L extends ListInf
if (!result.getErrors().isEmpty()) {
dynamicallyShowErrorPanelOrSnackbar(new ErrorInfo(result.getErrors(), errorUserAction,
"Get next items of: " + url, serviceId));
"Get next items of: " + url, serviceId, url));
}
}
@@ -248,7 +250,7 @@ public abstract class BaseListInfoFragment<I extends InfoItem, L extends ListInf
if (!errors.isEmpty()) {
dynamicallyShowErrorPanelOrSnackbar(new ErrorInfo(result.getErrors(),
errorUserAction, "Start loading: " + url, serviceId));
errorUserAction, "Start loading: " + url, serviceId, url));
}
}
}

View File

@@ -81,9 +81,7 @@ public class ChannelAboutFragment extends BaseDescriptionFragment {
if (channelInfo.getSubscriberCount() != UNKNOWN_SUBSCRIBER_COUNT) {
addMetadataItem(inflater, layout, false, R.string.metadata_subscribers,
Localization.localizeNumber(
requireContext(),
channelInfo.getSubscriberCount()));
Localization.localizeNumber(channelInfo.getSubscriberCount()));
}
addImagesMetadataItem(inflater, layout, R.string.metadata_avatars,

View File

@@ -120,67 +120,6 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo>
// LifeCycle
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
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
public void onAttach(@NonNull final Context context) {
super.onAttach(context);
@@ -195,6 +134,67 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo>
return binding.getRoot();
}
@Override
public void onViewCreated(@NonNull final View rootView, final Bundle savedInstanceState) {
super.onViewCreated(rootView, savedInstanceState);
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 // called from onViewCreated in BaseFragment.onViewCreated
protected void initViews(final View rootView, final Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState);
@@ -232,6 +232,14 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo>
binding.subChannelTitleView.setOnClickListener(openSubChannel);
}
@Override
public void onDestroyView() {
super.onDestroyView();
if (menuProvider != null) {
activity.removeMenuProvider(menuProvider);
}
}
@Override
public void onDestroy() {
super.onDestroy();
@@ -240,7 +248,6 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo>
}
disposables.clear();
binding = null;
activity.removeMenuProvider(menuProvider);
menuProvider = null;
}
@@ -570,7 +577,7 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo>
isLoading.set(false);
handleResult(result);
}, throwable -> showError(new ErrorInfo(throwable, UserAction.REQUESTED_CHANNEL,
url == null ? "No URL" : url, serviceId)));
url == null ? "No URL" : url, serviceId, url)));
}
@Override

Some files were not shown because too many files have changed in this diff Show More