1
mirror of https://github.com/TeamNewPipe/NewPipe synced 2025-09-22 10:00:55 +02:00

Compare commits

..

250 Commits

Author SHA1 Message Date
Stypox
abfde872f1 Merge pull request #12623 from Isira-Seneviratne/Merge-dev-to-refactor 2025-09-10 09:17:40 +02:00
Isira Seneviratne
c98f56bf7b Merge branch 'dev' into Merge-dev-to-refactor
# Conflicts:
#	app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
#	app/src/main/java/org/schabi/newpipe/player/Player.java
#	app/src/main/java/org/schabi/newpipe/player/PlayerService.java
#	app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java
#	app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueItem.java
2025-09-10 08:37:57 +05:30
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
89c4eb5237 Refactor player intent logic 2025-09-08 07:56:13 +05:30
Stypox
759342fa62 Merge pull request #12612 from Profpatsch/rename-fullscreen-buttons 2025-09-07 18:00:47 +02:00
Profpatsch
0382cfd2ae MainPlayerUi: separate enter/exit fullscreen methods
Most usages of `toggleFullscreen` are clear about whether they want to
enter or exit fullscreen, so let’s split the setup into two functions
for easier debugging.

The two remaining uses of `toggleFullscreen` *should* really toggle,
but I’m not 100% sure.

Also rename `onScreenRotationButtonClicked` to
`onFullscreenToggleButtonClicked`, since we renamed the button id
earlier.
2025-09-07 13:23:15 +02:00
Profpatsch
753a3e68d5 player.xml: Rename fullscreen buttons
Their names must have come from a very old version of the app, they
both toggle the fullscreen mode and rotate the screen depending on
circumstances.

So
`fullscreenButton` -> `fullscreenToggleButtonSecondaryMenu` because it
is only visible in the secondary video menu on some configurations.
and
`screenRotationButton` -> `fullscreenToggleButton` because it is the main
fullscreen button next to the video progress bar.
2025-09-07 12:25:56 +02:00
Stypox
b6bd87a4dc Merge pull request #12609 from Stypox/image-vector-app-icon 2025-09-06 18:01:10 +02:00
Stypox
b36201442d Use ImageVector to render NewPipe squircle app icon 2025-09-06 17:59:42 +02:00
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
Stypox
9d3775f132 Rewrite logo SVGs to make them line-only
Also optimize them with svgo
2025-09-06 17:23:31 +02:00
Stypox
b2d89a41fb Merge pull request #12604 from Isira-Seneviratne/Refactor-EmptyState 2025-09-06 15:33:49 +02:00
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
Isira Seneviratne
01a8c4e584 Clean up EmptyStateComposable code 2025-09-05 18:22:19 +05:30
Stypox
2ee7cc4344 Merge branch 'dev' into refactor 2025-09-05 13:34:53 +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
2cb465c89d Merge branch 'dev' into refactor 2025-09-04 15:25:45 +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
ccca89dc8a Merge pull request #12585 from Isira-Seneviratne/Merge-dev-to-refactor
Merge dev to refactor
2025-09-01 05:37:29 +05:30
Isira Seneviratne
9bf23abcd1 Merge branch 'dev' into Merge-dev-to-refactor
# Conflicts:
#	app/src/main/AndroidManifest.xml
2025-09-01 05:22:51 +05:30
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
Tobi
10c5a5d02c Merge pull request #12569 from Isira-Seneviratne/Fix-import
Fix database import
2025-08-27 01:55:55 -07:00
Isira Seneviratne
cf4b5e17c5 Fix database import 2025-08-25 14:32:19 +05:30
Stypox
deb5425871 Merge branch 'dev' into refactor 2025-08-17 12:48:30 +02:00
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
2915ab6aef Merge pull request #12462 from Isira-Seneviratne/Merge-dev-to-refactor
Merge dev to refactor
2025-07-25 06:23:19 +05:30
Isira Seneviratne
be662a9f1a Merge branch 'dev' into Merge-dev-to-refactor
# Conflicts:
#	app/build.gradle
#	app/src/main/AndroidManifest.xml
#	app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java
#	app/src/main/java/org/schabi/newpipe/player/mediabrowser/PackageValidator.kt
2025-07-24 19:28:41 +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
Isira Seneviratne
1534c880ef Fix compilation errors 2025-07-21 09:11:24 +05:30
Isira Seneviratne
ded7205588 Merge branch 'dev' into Merge-dev-to-refactor
# Conflicts:
#	app/build.gradle
#	app/src/main/java/org/schabi/newpipe/App.java
#	app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt
#	app/src/main/java/org/schabi/newpipe/about/LicenseFragment.kt
#	app/src/main/java/org/schabi/newpipe/local/subscription/ImportConfirmationDialog.java
#	app/src/main/java/org/schabi/newpipe/player/Player.java
#	app/src/main/java/org/schabi/newpipe/player/PlayerService.java
#	app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java
2025-07-21 09:05:32 +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
1f2e5799f7 Merge branch 'dev' into Merge-dev-to-refactor
# Conflicts:
#	app/build.gradle
#	app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java
#	app/src/main/res/values/strings.xml
2025-07-20 05:57:27 +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
Isira Seneviratne
f93806293a Merge branch 'dev' into Merge-dev-to-refactor
# Conflicts:
#	app/build.gradle
#	app/src/main/java/org/schabi/newpipe/player/PlayerService.java
#	app/src/main/res/values-ca/strings.xml
2025-07-18 19:14:58 +05:30
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
da36b8a140 Merge pull request #12337 from Profpatsch/video-detail-fragment-kotlin-conversion 2025-07-16 15:56:39 +02:00
Stypox
046ea7301b Apply Kotlin suggestion by @Isira-Seneviratne 2025-07-16 15:53:05 +02:00
Stypox
3998982002 Smaller style fixes 2025-07-16 15:53:05 +02:00
Stypox
a4182b474b More improve Kotlin converted from java in various places 2025-07-16 15:53:05 +02:00
Stypox
317db719db Fix comments in PlayerUiList 2025-07-16 15:47:00 +02:00
Stypox
3f62ec7e53 Improve Kotlin converted from java in various places 2025-07-16 15:46:59 +02:00
Profpatsch
7330541499 PlayerUIList: remove remaining uses of getOpt
mediaSession is now `@NonNull`, so the getter is as well.
2025-07-16 15:42:59 +02:00
Profpatsch
ed0051a3f6 Player: small class comment 2025-07-16 15:42:59 +02:00
Profpatsch
91aed1e240 VideoDetailFragment: replace every getOpt() with get() 2025-07-16 15:42:59 +02:00
Profpatsch
38ed1da79e PlayerHolder: use object class to implement singleton pattern 2025-07-16 15:42:59 +02:00
Profpatsch
cc3ecd4169 VideoDetailFragment: convert to kotlin (mechanical, fixup)
Mostly 1:1, I had to fix a few places where the automatic conversion
did not infer the right kotlin types, and places where it tried to
convert to `double` instead of using `float` like the original.

Everything else is the result of automatic conversion.
2025-07-16 15:42:58 +02:00
Profpatsch
bf72fd1fa5 VideoDetailFragment: convert to kotlin (mechanical, failing)
Just the conversion, errors still there for easier rebasing later.
2025-07-16 15:42:12 +02:00
Profpatsch
86b27cf77d PlayerHolder: kotlinify optional calls 2025-07-16 15:15:28 +02:00
Profpatsch
4fd3ddf392 PlayerHolder: kotlinify setListener 2025-07-16 15:14:50 +02:00
Profpatsch
fc7daa96e9 PlayerHolder: kotlinify getters 2025-07-16 15:14:50 +02:00
Profpatsch
9b22773070 PlayerHolder: convert to kotlin (mechanical) 2025-07-16 15:14:49 +02:00
Profpatsch
86063fda6a PlayQueueActivity: inline getServiceConnection() and bind()
Both are only used once, and it’s easier to see what’s going on if
they are just inlined in `onCreate`.
2025-07-16 15:07:43 +02:00
Profpatsch
c9be4066f2 PlayerService: inline init block & make non-optional 2025-07-16 15:07:43 +02:00
Profpatsch
462f0ad5c0 PlayerUIList: inline init block 2025-07-16 15:07:43 +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
Isira Seneviratne
183cc580fc Merge pull request #12415 from Isira-Seneviratne/Zip-migrate-Path
Migrate zip import/export to use Path
2025-07-12 06:04:31 +05:30
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
Isira Seneviratne
52ff052d6a Merge pull request #12347 from Isira-Seneviratne/PlayQueueItem-equals
Add PlayQueueItem equals and hashCode
2025-07-10 02:18:43 +05:30
Isira Seneviratne
31f8dd05a7 Convert play queue classes to Kotlin 2025-07-09 07:48:08 +05:30
Isira Seneviratne
c2b6c71947 Rename .java to .kt 2025-07-09 07:48:07 +05:30
Isira Seneviratne
bb7873d157 Fix Sonar warning 2025-07-09 07:48:07 +05:30
Isira Seneviratne
e2a02a1f86 Fix some issues 2025-07-09 07:48:07 +05:30
Isira Seneviratne
690af88db9 Add PlayQueueItem equals and hashCode 2025-07-09 07:48:07 +05:30
Isira Seneviratne
4ffadc2057 Inline variable 2025-07-09 07:47:25 +05:30
Isira Seneviratne
225cb91105 Use InputStream#transferTo() 2025-07-09 07:47:25 +05:30
Isira Seneviratne
3e106b5e4f Fix DB import/export issue 2025-07-09 07:47:25 +05:30
Isira Seneviratne
72b67ab5d4 Refactor zip import/export using Path 2025-07-09 07:47:25 +05:30
Isira Seneviratne
840084d2c9 Merge pull request #12419 from Isira-Seneviratne/Merge-dev-to-refactor
Merge dev to refactor
2025-07-09 07:46:11 +05:30
Isira Seneviratne
2acfefd0bc Merge branch 'dev' into Merge-dev-to-refactor
# Conflicts:
#	app/src/main/java/org/schabi/newpipe/player/helper/PlayerHolder.java
2025-07-09 07:10:18 +05:30
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
Stypox
d81b571d05 Merge pull request #12348 from Isira-Seneviratne/Merge-dev-to-refactor 2025-06-11 10:13:36 +02:00
Isira Seneviratne
6efb92a38f Merge branch 'dev' into Merge-dev-to-refactor
# Conflicts:
#	app/src/main/java/org/schabi/newpipe/MainActivity.java
2025-06-11 08:20:00 +05:30
Stypox
f16becc872 Merge pull request #12217 from SttApollo/Update-EmptyStateComposable 2025-06-06 15:49:13 +02:00
Stypox
60ea317e61 NoComments -> NoVideos in related items screen 2025-06-06 15:36:07 +02:00
Su TT
e53f0ff94a Update EmptyStateComposable usages to include modifiers 2025-06-05 12:33:21 -04: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
Profpatsch
006b4c9ae1 Merge pull request #11965 from Profpatsch/player-classes-kotlin-conversion
Some simple refactors & beginning of kotlin conversions of the player classes
2025-06-05 14:13:33 +02:00
Profpatsch
73fef268fc PlayerService/PlayerUIList: some small improvements 2025-06-05 13:52:04 +02:00
Profpatsch
cf8fe95abf PlayerService: runtime-assert that we get passed a service
We directly call the `getService` function after receiving the
argument, so resolving the WeakPointer should never return `null` in
our case.
Of course there could be a race condition in theory, but I feel like
if that happens we have bigger problems?
2025-06-05 13:52:04 +02:00
Profpatsch
36115c3164 PlayerService: remove !! where possible
It’s a bit unwieldy in places, but should improve the safety of the
code in the face of possible race conditions.
2025-06-05 13:52:04 +02:00
Profpatsch
be373dca8d PlayerUIList: make UI list private 2025-06-05 13:52:04 +02:00
Profpatsch
f5a4af2d67 Player: destroy -> saveAndShutdown 2025-06-05 13:52:04 +02:00
Profpatsch
06cf511188 PlayerHolder: improve interface docstrings 2025-06-05 13:52:04 +02:00
Profpatsch
26050d808e VideoPlayerUi: suppress warnings
The `R.id` link from the comment cannot be resolved, so let’s not link
it for now.

We are using some exoplayer2 resources, let’s silence the warning.
2025-06-05 13:52:04 +02:00
Profpatsch
0b32738d42 VideoDetailFragment: remove duplicate code in startLoading 2025-06-05 13:52:04 +02:00
Profpatsch
c37db85b97 VideoDetailFragment: apply more IDE suggestions 2025-06-05 13:52:04 +02:00
Profpatsch
4d6e1a4ecf VideoDetailFragment: apply visibility suggestions
Because the class is final, protected does not make sense (Android
Studio auto-suggestions)
2025-06-05 13:52:04 +02:00
Profpatsch
b5dd49ecd3 PlayerService: simplify nullable calls, getters 2025-06-05 13:52:04 +02:00
Profpatsch
945fbd884b PlayerService: Convert to kotlin (mechanical) 2025-06-05 13:52:04 +02:00
Profpatsch
545c4f078f PlayerUIList: restrict superclasses a little 2025-06-05 13:52: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
Stt_lens
d4cd54fd7b Update app/src/main/java/org/schabi/newpipe/ui/emptystate/EmptyStateComposable.kt
Co-authored-by: Stypox <stypox@pm.me>
2025-05-31 12:27:16 -04:00
Stt_lens
15ab3df511 Update app/src/main/java/org/schabi/newpipe/ui/emptystate/EmptyStateComposable.kt
Co-authored-by: Stypox <stypox@pm.me>
2025-05-31 12:07:37 -04: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
Isira Seneviratne
f41b34c090 Merge pull request #11759 from Isira-Seneviratne/Import-export-worker
Rewrite import and export subscriptions functionality using coroutines
2025-05-16 01:16:34 +05:30
Stypox
d1954baf29 Merge pull request #11829 from Profpatsch/PlayerUIList-to-kotlin
Player UI list to kotlin
2025-05-13 23:36:16 +02: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
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
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
Isira Seneviratne
b6144d01f3 Merge branch 'refactor' into Import-export-worker 2025-04-29 10:18:13 +05:30
Su TT
df11e53a74 Refactor EmptyStateComposable to remove modifier from EmptyStateSpec and fix modifier usage ... 2025-04-28 17:24:11 -04:00
Isira Seneviratne
cbe598182a Merge branch 'refactor' into Import-export-worker 2025-04-20 07:10:07 +05:30
Isira Seneviratne
d805679a5e Use workDataOf 2025-03-08 09:18:30 +05:30
Isira Seneviratne
abaf16e12b Merge branch 'refactor' into Import-export-worker
# Conflicts:
#	gradle/libs.versions.toml
2025-02-23 05:10:34 +05:30
Isira Seneviratne
48d682016e Rm ViewPager version 2025-01-25 11:21:58 +05:30
Isira Seneviratne
32a88ab890 Truncate existing file in export 2025-01-25 07:57:01 +05:30
Isira Seneviratne
af3ed992e5 Add error handling for import 2025-01-25 05:40:00 +05:30
Isira Seneviratne
a79516dfff Use fragment arguments 2025-01-25 05:30:14 +05:30
Profpatsch
dbd11a6a8d SubscriptionImportWorker: inputs as sealed class 2025-01-22 15:34:04 +01:00
Isira Seneviratne
21973b362a Use Kotlin Pair 2025-01-22 13:03:57 +01:00
Isira Seneviratne
60586c90d6 Improve subscription upsert methods 2025-01-22 13:03:57 +01:00
Isira Seneviratne
095155d35f Only get subscription extractor when needed 2025-01-22 13:03:57 +01:00
Isira Seneviratne
af8e5646a6 Remove LocalBroadcastManager 2025-01-22 13:03:57 +01:00
Isira Seneviratne
c9d155a335 Combine notification and ForegroundInfo creation methods 2025-01-22 13:03:57 +01:00
Isira Seneviratne
4e31ccebf8 Moved Kotlinx Serialization to library catalog 2025-01-22 13:03:57 +01:00
Isira Seneviratne
c0965a42a1 Added success toasts 2025-01-22 13:03:57 +01:00
Isira Seneviratne
fc7f1b0af0 Convert subscription import service to a worker 2025-01-22 13:03:57 +01:00
Isira Seneviratne
dfb035dfa5 Improve import/export tests 2025-01-22 13:03:57 +01:00
Isira Seneviratne
8e9503cfe4 Convert subscription export service to a worker 2025-01-22 13:03:55 +01:00
Isira Seneviratne
82516dd75c Rename .java to .kt 2025-01-22 13:02:36 +01:00
295 changed files with 8099 additions and 6841 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

@@ -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

@@ -9,6 +9,7 @@ plugins {
alias libs.plugins.kotlin.compose
alias libs.plugins.kotlin.kapt
alias libs.plugins.kotlin.parcelize
alias libs.plugins.kotlinx.serialization
alias libs.plugins.checkstyle
alias libs.plugins.sonarqube
alias libs.plugins.hilt
@@ -16,20 +17,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')
}
@@ -226,7 +227,6 @@ dependencies {
implementation libs.androidx.fragment.compose
implementation libs.androidx.lifecycle.livedata
implementation libs.androidx.lifecycle.viewmodel
implementation libs.androidx.localbroadcastmanager
implementation libs.androidx.media
implementation libs.androidx.preference
implementation libs.androidx.recyclerview
@@ -319,6 +319,9 @@ dependencies {
// Scroll
implementation libs.lazycolumnscrollbar
// Kotlinx Serialization
implementation libs.kotlinx.serialization.json
/** Debugging **/
// Memory leak detection
debugImplementation libs.leakcanary.object.watcher

View File

@@ -34,3 +34,18 @@
## For some reason NotificationModeConfigFragment wasn't kept (only referenced in a preference xml)
-keep class org.schabi.newpipe.settings.notifications.** { *; }
## Keep Kotlinx Serialization classes
-keepclassmembers class kotlinx.serialization.json.** {
*** Companion;
}
-keepclasseswithmembers class kotlinx.serialization.json.** {
kotlinx.serialization.KSerializer serializer(...);
}
-keep,includedescriptorclasses class org.schabi.newpipe.**$$serializer { *; }
-keepclassmembers class org.schabi.newpipe.** {
*** Companion;
}
-keepclasseswithmembers class org.schabi.newpipe.** {
kotlinx.serialization.KSerializer serializer(...);
}

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"
@@ -90,9 +101,14 @@
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="androidx.work.impl.foreground.SystemForegroundService"
android:foregroundServiceType="dataSync"
tools:node="merge" />
<service
android:name=".local.feed.service.FeedLoadService"
android:foregroundServiceType="dataSync" />
<activity
android:name=".PanicResponderActivity"
@@ -124,7 +140,9 @@
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"
@@ -417,6 +435,7 @@
</activity>
<service
android:name=".RouterActivity$FetcherService"
android:foregroundServiceType="dataSync"
android:exported="false" />
<!-- opting out of sending metrics to Google in Android System WebView -->

View File

@@ -97,7 +97,7 @@ open class App :
Localization.getPreferredLocalization(this),
Localization.getPreferredContentCountry(this),
)
Localization.initPrettyTime(Localization.resolvePrettyTime(this))
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";

View File

@@ -20,8 +20,6 @@
package org.schabi.newpipe;
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -49,6 +47,7 @@ import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.Fragment;
@@ -77,6 +76,7 @@ import org.schabi.newpipe.player.event.OnKeyDownListener;
import org.schabi.newpipe.player.helper.PlayerHolder;
import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.settings.UpdateSettingsFragment;
import org.schabi.newpipe.settings.migration.MigrationManager;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.DeviceUtils;
import org.schabi.newpipe.util.KioskTranslator;
@@ -122,7 +122,10 @@ public class MainActivity extends AppCompatActivity {
private static final int ITEM_ID_ABOUT = 2;
private static final int ORDER = 0;
public static final String KEY_IS_IN_BACKGROUND = "is_in_background";
private SharedPreferences sharedPreferences;
private SharedPreferences.Editor sharedPrefEditor;
/*//////////////////////////////////////////////////////////////////////////
// Activity's LifeCycle
//////////////////////////////////////////////////////////////////////////*/
@@ -134,6 +137,7 @@ public class MainActivity extends AppCompatActivity {
+ "savedInstanceState = [" + savedInstanceState + "]");
}
Localization.migrateAppLanguageSettingIfNecessary(getApplicationContext());
ThemeHelper.setDayNightMode(this);
ThemeHelper.setTheme(this, ServiceHelper.getSelectedServiceId(this));
@@ -150,8 +154,9 @@ public class MainActivity extends AppCompatActivity {
}
}
assureCorrectAppLanguage(this);
super.onCreate(savedInstanceState);
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
sharedPrefEditor = sharedPreferences.edit();
mainBinding = ActivityMainBinding.inflate(getLayoutInflater());
drawerLayoutBinding = mainBinding.drawerLayout;
@@ -187,7 +192,7 @@ public class MainActivity extends AppCompatActivity {
UpdateSettingsFragment.askForConsentToUpdateChecks(this);
}
Localization.migrateAppLanguageSettingIfNecessary(getApplicationContext());
MigrationManager.showUserInfoIfPresent(this);
}
@Override
@@ -195,16 +200,29 @@ public class MainActivity extends AppCompatActivity {
super.onPostCreate(savedInstanceState);
final App app = App.getInstance();
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(app);
if (prefs.getBoolean(app.getString(R.string.update_app_key), false)
&& prefs.getBoolean(app.getString(R.string.update_check_consent_key), false)) {
if (sharedPreferences.getBoolean(app.getString(R.string.update_app_key), false)
&& sharedPreferences
.getBoolean(app.getString(R.string.update_check_consent_key), false)) {
// Start the worker which is checking all conditions
// and eventually searching for a new version.
NewVersionWorker.enqueueNewVersionCheckingWork(app, false);
}
}
@Override
protected void onStart() {
super.onStart();
sharedPrefEditor.putBoolean(KEY_IS_IN_BACKGROUND, false).apply();
Log.d(TAG, "App moved to foreground");
}
@Override
protected void onStop() {
super.onStop();
sharedPrefEditor.putBoolean(KEY_IS_IN_BACKGROUND, true).apply();
Log.d(TAG, "App moved to background");
}
private void setupDrawer() throws ExtractionException {
addDrawerMenuForCurrentService();
@@ -242,19 +260,6 @@ public class MainActivity extends AppCompatActivity {
*/
private void addDrawerMenuForCurrentService() throws ExtractionException {
//Tabs
final int currentServiceId = ServiceHelper.getSelectedServiceId(this);
final StreamingService service = NewPipe.getService(currentServiceId);
int kioskMenuItemId = 0;
for (final String ks : service.getKioskList().getAvailableKiosks()) {
drawerLayoutBinding.navigation.getMenu()
.add(R.id.menu_tabs_group, kioskMenuItemId, 0, KioskTranslator
.getTranslatedKioskName(ks, this))
.setIcon(KioskTranslator.getKioskIcon(ks));
kioskMenuItemId++;
}
drawerLayoutBinding.navigation.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_SUBSCRIPTIONS, ORDER,
R.string.tab_subscriptions)
@@ -272,6 +277,20 @@ public class MainActivity extends AppCompatActivity {
.add(R.id.menu_tabs_group, ITEM_ID_HISTORY, ORDER, R.string.action_history)
.setIcon(R.drawable.ic_history);
//Kiosks
final int currentServiceId = ServiceHelper.getSelectedServiceId(this);
final StreamingService service = NewPipe.getService(currentServiceId);
int kioskMenuItemId = 0;
for (final String ks : service.getKioskList().getAvailableKiosks()) {
drawerLayoutBinding.navigation.getMenu()
.add(R.id.menu_kiosks_group, kioskMenuItemId, 0, KioskTranslator
.getTranslatedKioskName(ks, this))
.setIcon(KioskTranslator.getKioskIcon(ks));
kioskMenuItemId++;
}
//Settings and About
drawerLayoutBinding.navigation.getMenu()
.add(R.id.menu_options_about_group, ITEM_ID_SETTINGS, ORDER, R.string.settings)
@@ -291,10 +310,13 @@ public class MainActivity extends AppCompatActivity {
changeService(item);
break;
case R.id.menu_tabs_group:
try {
tabSelected(item);
break;
case R.id.menu_kiosks_group:
try {
kioskSelected(item);
} catch (final Exception e) {
ErrorUtil.showUiErrorSnackbar(this, "Selecting main page tab", e);
ErrorUtil.showUiErrorSnackbar(this, "Selecting drawer kiosk", e);
}
break;
case R.id.menu_options_about_group:
@@ -318,7 +340,7 @@ public class MainActivity extends AppCompatActivity {
.setChecked(true);
}
private void tabSelected(final MenuItem item) throws ExtractionException {
private void tabSelected(final MenuItem item) {
switch (item.getItemId()) {
case ITEM_ID_SUBSCRIPTIONS:
NavigationHelper.openSubscriptionFragment(getSupportFragmentManager());
@@ -335,7 +357,10 @@ public class MainActivity extends AppCompatActivity {
case ITEM_ID_HISTORY:
NavigationHelper.openStatisticFragment(getSupportFragmentManager());
break;
default:
}
}
private void kioskSelected(final MenuItem item) throws ExtractionException {
final StreamingService currentService = ServiceHelper.getSelectedService(this);
int kioskMenuItemId = 0;
for (final String kioskId : currentService.getKioskList().getAvailableKiosks()) {
@@ -346,8 +371,6 @@ public class MainActivity extends AppCompatActivity {
}
kioskMenuItemId++;
}
break;
}
}
private void optionsAboutSelected(final MenuItem item) {
@@ -387,6 +410,7 @@ public class MainActivity extends AppCompatActivity {
drawerLayoutBinding.navigation.getMenu().removeGroup(R.id.menu_services_group);
drawerLayoutBinding.navigation.getMenu().removeGroup(R.id.menu_tabs_group);
drawerLayoutBinding.navigation.getMenu().removeGroup(R.id.menu_kiosks_group);
drawerLayoutBinding.navigation.getMenu().removeGroup(R.id.menu_options_about_group);
// Show up or down arrow
@@ -480,9 +504,8 @@ public class MainActivity extends AppCompatActivity {
@Override
protected void onResume() {
assureCorrectAppLanguage(this);
// Change the date format to match the selected language on resume
Localization.initPrettyTime(Localization.resolvePrettyTime(getApplicationContext()));
Localization.initPrettyTime(Localization.resolvePrettyTime());
super.onResume();
// Close drawer on return, and don't show animation,
@@ -504,13 +527,11 @@ public class MainActivity extends AppCompatActivity {
ErrorUtil.showUiErrorSnackbar(this, "Setting up service toggle", e);
}
final SharedPreferences sharedPreferences =
PreferenceManager.getDefaultSharedPreferences(this);
if (sharedPreferences.getBoolean(Constants.KEY_THEME_CHANGE, false)) {
if (DEBUG) {
Log.d(TAG, "Theme has changed, recreating activity...");
}
sharedPreferences.edit().putBoolean(Constants.KEY_THEME_CHANGE, false).apply();
sharedPrefEditor.putBoolean(Constants.KEY_THEME_CHANGE, false).apply();
ActivityCompat.recreate(this);
}
@@ -518,7 +539,7 @@ public class MainActivity extends AppCompatActivity {
if (DEBUG) {
Log.d(TAG, "main page has changed, recreating main fragment...");
}
sharedPreferences.edit().putBoolean(Constants.KEY_MAIN_PAGE_CHANGE, false).apply();
sharedPrefEditor.putBoolean(Constants.KEY_MAIN_PAGE_CHANGE, false).apply();
NavigationHelper.openMainActivity(this);
}
@@ -833,7 +854,7 @@ public class MainActivity extends AppCompatActivity {
return;
}
if (PlayerHolder.getInstance().isPlayerOpen()) {
if (PlayerHolder.INSTANCE.isPlayerOpen()) {
// if the player is already open, no need for a broadcast receiver
openMiniPlayerIfMissing();
} else {
@@ -843,7 +864,7 @@ public class MainActivity extends AppCompatActivity {
public void onReceive(final Context context, final Intent intent) {
if (Objects.equals(intent.getAction(),
VideoDetailFragment.ACTION_PLAYER_STARTED)
&& PlayerHolder.getInstance().isPlayerOpen()) {
&& PlayerHolder.INSTANCE.isPlayerOpen()) {
openMiniPlayerIfMissing();
// At this point the player is added 100%, we can unregister. Other actions
// are useless since the fragment will not be removed after that.
@@ -854,11 +875,12 @@ public class MainActivity extends AppCompatActivity {
};
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(VideoDetailFragment.ACTION_PLAYER_STARTED);
registerReceiver(broadcastReceiver, intentFilter);
ContextCompat.registerReceiver(this, broadcastReceiver, intentFilter,
ContextCompat.RECEIVER_EXPORTED);
// If the PlayerHolder is not bound yet, but the service is running, try to bind to it.
// Once the connection is established, the ACTION_PLAYER_STARTED will be sent.
PlayerHolder.getInstance().tryBindIfNeeded(this);
PlayerHolder.INSTANCE.tryBindIfNeeded(this);
}
}

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;
}
@@ -701,7 +670,7 @@ public class RouterActivity extends AppCompatActivity {
}
// ...the player is not running or in normal Video-mode/type
final PlayerType playerType = PlayerHolder.getInstance().getType();
final PlayerType playerType = PlayerHolder.INSTANCE.getType();
return playerType == null || playerType == PlayerType.MAIN;
}
@@ -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

@@ -9,11 +9,9 @@ import org.schabi.newpipe.R
import org.schabi.newpipe.ui.components.common.ScaffoldWithToolbar
import org.schabi.newpipe.ui.screens.AboutScreen
import org.schabi.newpipe.ui.theme.AppTheme
import org.schabi.newpipe.util.Localization
class AboutActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
Localization.assureCorrectAppLanguage(this)
enableEdgeToEdge()
super.onCreate(savedInstanceState)

View File

@@ -90,7 +90,7 @@ abstract class SubscriptionDAO : BasicDAO<SubscriptionEntity> {
internal abstract fun silentInsertAllInternal(entities: List<SubscriptionEntity>): List<Long>
@Transaction
open fun upsertAll(entities: List<SubscriptionEntity>): List<SubscriptionEntity> {
open fun upsertAll(entities: List<SubscriptionEntity>) {
val insertUidList = silentInsertAllInternal(entities)
insertUidList.forEachIndexed { index: Int, uidFromInsert: Long ->
@@ -106,7 +106,5 @@ abstract class SubscriptionDAO : BasicDAO<SubscriptionEntity> {
update(entity)
}
}
return entities
}
}

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,13 +41,16 @@ class ErrorPanelHelper(
errorPanelRoot.findViewById(R.id.error_open_in_browser)
private var errorDisposable: Disposable? = null
private var retryShouldBeShown: Boolean = (onRetry != null)
init {
if (onRetry != null) {
errorDisposable = errorRetryButton.clicks()
.debounce(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe { onRetry.run() }
}
}
private fun ensureDefaultVisibility() {
errorTextView.isVisible = true
@@ -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.throwable !is ContentNotAvailableException &&
errorInfo.throwable !is ContentNotSupportedException
) {
// show retry button only for content which is not unavailable or unsupported
errorRetryButton.isVisible = true
}
showAndSetOpenInBrowserButtonAction(errorInfo)
if (errorInfo.isRetryable) {
errorRetryButton.isVisible = retryShouldBeShown
}
if (errorInfo.openInBrowserUrl != null) {
errorOpenInBrowserButton.isVisible = true
errorOpenInBrowserButton.setOnClickListener {
ShareUtils.openUrlInBrowser(context, errorInfo.openInBrowserUrl)
}
}
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,13 +38,21 @@ 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) {
if (PreferenceManager.getDefaultSharedPreferences(context)
.getBoolean(MainActivity.KEY_IS_IN_BACKGROUND, true)
) {
createNotification(context, errorInfo)
} else {
context.startActivity(getErrorActivityIntent(context, errorInfo))
}
}
/**
* Show a bottom snackbar to the user, with a report button that opens the error activity.
@@ -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,10 +137,12 @@ class ErrorUtil {
NotificationManagerCompat.from(context)
.notify(ERROR_REPORT_NOTIFICATION_ID, notificationBuilder.build())
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 {
val intent = Intent(context, ErrorActivity::class.java)
@@ -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,

File diff suppressed because it is too large Load Diff

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

@@ -3,6 +3,7 @@ package org.schabi.newpipe.fragments.list.channel;
import static org.schabi.newpipe.ktx.TextViewUtils.animateTextColor;
import static org.schabi.newpipe.ktx.ViewUtils.animate;
import static org.schabi.newpipe.ktx.ViewUtils.animateBackgroundColor;
import static org.schabi.newpipe.ui.emptystate.EmptyStateUtil.setEmptyStateComposable;
import android.content.Context;
import android.content.SharedPreferences;
@@ -45,7 +46,6 @@ import org.schabi.newpipe.ktx.AnimationType;
import org.schabi.newpipe.local.feed.notifications.NotificationHelper;
import org.schabi.newpipe.local.subscription.SubscriptionManager;
import org.schabi.newpipe.ui.emptystate.EmptyStateSpec;
import org.schabi.newpipe.ui.emptystate.EmptyStateUtil;
import org.schabi.newpipe.util.ChannelTabHelper;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.ExtractorHelper;
@@ -200,10 +200,7 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo>
protected void initViews(final View rootView, final Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState);
EmptyStateUtil.setEmptyStateComposable(
binding.emptyStateView,
EmptyStateSpec.Companion.getContentNotSupported()
);
setEmptyStateComposable(binding.emptyStateView, EmptyStateSpec.ContentNotSupported);
tabAdapter = new TabAdapter(getChildFragmentManager());
binding.viewPager.setAdapter(tabAdapter);
@@ -583,7 +580,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

View File

@@ -3,6 +3,7 @@ package org.schabi.newpipe.fragments.list.search;
import static androidx.recyclerview.widget.ItemTouchHelper.Callback.makeMovementFlags;
import static org.schabi.newpipe.extractor.utils.Utils.isBlank;
import static org.schabi.newpipe.ktx.ViewUtils.animate;
import static org.schabi.newpipe.ui.emptystate.EmptyStateUtil.setEmptyStateComposable;
import static org.schabi.newpipe.util.ExtractorHelper.showMetaInfoInTextView;
import static java.util.Arrays.asList;
@@ -54,6 +55,7 @@ import org.schabi.newpipe.extractor.MetaInfo;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.Page;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.search.SearchExtractor;
import org.schabi.newpipe.extractor.search.SearchInfo;
import org.schabi.newpipe.extractor.services.peertube.linkHandler.PeertubeSearchQueryHandlerFactory;
@@ -65,7 +67,6 @@ import org.schabi.newpipe.ktx.ExceptionUtils;
import org.schabi.newpipe.local.history.HistoryRecordManager;
import org.schabi.newpipe.settings.NewPipeSettings;
import org.schabi.newpipe.ui.emptystate.EmptyStateSpec;
import org.schabi.newpipe.ui.emptystate.EmptyStateUtil;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.DeviceUtils;
import org.schabi.newpipe.util.ExtractorHelper;
@@ -146,6 +147,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
private final SparseArrayCompat<String> menuItemToFilterName = new SparseArrayCompat<>();
private StreamingService service;
@Nullable
private Page nextPage;
private boolean showLocalSuggestions = true;
private boolean showRemoteSuggestions = true;
@@ -221,6 +223,15 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
public void onViewCreated(@NonNull final View rootView, final Bundle savedInstanceState) {
searchBinding = FragmentSearchBinding.bind(rootView);
super.onViewCreated(rootView, savedInstanceState);
updateService();
// Add the service name to search string hint
// to make it more obvious which platform is being searched.
if (service != null) {
searchEditText.setHint(
getString(R.string.search_with_service_name,
service.getServiceInfo().getName()));
}
showSearchOnStart();
initSearchListeners();
}
@@ -346,9 +357,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
protected void initViews(final View rootView, final Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState);
EmptyStateUtil.setEmptyStateComposable(
searchBinding.emptyStateView,
EmptyStateSpec.Companion.getNoSearchResult());
setEmptyStateComposable(searchBinding.emptyStateView, EmptyStateSpec.NoSearchResult);
searchBinding.suggestionsList.setAdapter(suggestionListAdapter);
// animations are just strange and useless, since the suggestions keep changing too much
@@ -930,7 +939,21 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
infoListAdapter.clearStreamItemList();
showEmptyState();
} else {
showError(new ErrorInfo(exception, UserAction.SEARCHED, searchString, serviceId));
showError(new ErrorInfo(exception, UserAction.SEARCHED, searchString, serviceId,
getOpenInBrowserUrlForErrors()));
}
}
@Nullable
private String getOpenInBrowserUrlForErrors() {
if (TextUtils.isEmpty(searchString)) {
return null;
}
try {
return service.getSearchQHFactory().getUrl(searchString,
Arrays.asList(contentFilter), sortFilter);
} catch (final NullPointerException | ParsingException ignored) {
return null;
}
}
@@ -942,6 +965,20 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
filterItemCheckedId = item.getItemId();
item.setChecked(true);
if (service != null) {
final boolean isNotFiltered = theContentFilter.isEmpty()
|| "all".equals(theContentFilter.get(0));
if (isNotFiltered) {
searchEditText.setHint(
getString(R.string.search_with_service_name,
service.getServiceInfo().getName()));
} else {
searchEditText.setHint(getString(R.string.search_with_service_name_and_filter,
service.getServiceInfo().getName(),
item.getTitle()));
}
}
contentFilter = theContentFilter.toArray(new String[0]);
if (!TextUtils.isEmpty(searchString)) {
@@ -1004,7 +1041,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
&& !(exceptions.size() == 1
&& exceptions.get(0) instanceof SearchExtractor.NothingFoundException)) {
showSnackBarError(new ErrorInfo(result.getErrors(), UserAction.SEARCHED,
searchString, serviceId));
searchString, serviceId, getOpenInBrowserUrlForErrors()));
}
searchSuggestion = result.getSearchSuggestion();
@@ -1071,15 +1108,26 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
public void handleNextItems(final ListExtractor.InfoItemsPage<?> result) {
showListFooter(false);
infoListAdapter.addInfoItemList(result.getItems());
nextPage = result.getNextPage();
if (!result.getErrors().isEmpty()) {
// nextPage should be non-null at this point, because it refers to the page
// whose results are handled here, but let's check it anyway
if (nextPage == null) {
showSnackBarError(new ErrorInfo(result.getErrors(), UserAction.SEARCHED,
"\"" + searchString + "\" → nextPage == null", serviceId,
getOpenInBrowserUrlForErrors()));
} else {
showSnackBarError(new ErrorInfo(result.getErrors(), UserAction.SEARCHED,
"\"" + searchString + "\" → pageUrl: " + nextPage.getUrl() + ", "
+ "pageIds: " + nextPage.getIds() + ", "
+ "pageCookies: " + nextPage.getCookies(),
serviceId));
serviceId, getOpenInBrowserUrlForErrors()));
}
}
// keep the reassignment of nextPage after the error handling to ensure that nextPage
// still holds the correct value during the error handling
nextPage = result.getNextPage();
super.handleNextItems(result);
}

View File

@@ -252,7 +252,7 @@ public final class InfoItemDialog {
* @return the current {@link Builder} instance
*/
public Builder addEnqueueEntriesIfNeeded() {
final PlayerHolder holder = PlayerHolder.getInstance();
final PlayerHolder holder = PlayerHolder.INSTANCE;
if (holder.isPlayQueueReady()) {
addEntry(StreamDialogDefaultEntry.ENQUEUE);

View File

@@ -15,6 +15,7 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.compose.ui.platform.ComposeView;
import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
@@ -125,10 +126,8 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL
super.initViews(rootView, savedInstanceState);
itemListAdapter.setUseItemHandle(true);
EmptyStateUtil.setEmptyStateComposable(
rootView.findViewById(R.id.empty_state_view),
EmptyStateSpec.Companion.getNoBookmarkedPlaylist()
);
final ComposeView emptyView = rootView.findViewById(R.id.empty_state_view);
EmptyStateUtil.setEmptyStateComposable(emptyView, EmptyStateSpec.NoBookmarkedPlaylist);
}
@Override

View File

@@ -274,8 +274,13 @@ class FeedFragment : BaseStateFragment<FeedState>() {
@Deprecated("Deprecated in Java")
override fun onDestroyOptionsMenu() {
super.onDestroyOptionsMenu()
if (
(groupName != "") &&
(activity?.supportActionBar?.subtitle == groupName)
) {
activity?.supportActionBar?.subtitle = null
}
}
override fun onDestroy() {
disposables.dispose()
@@ -286,8 +291,14 @@ class FeedFragment : BaseStateFragment<FeedState>() {
}
super.onDestroy()
if (
(groupName != "") &&
(activity?.supportActionBar?.subtitle == groupName)
) {
activity?.supportActionBar?.subtitle = null
}
}
override fun onDestroyView() {
// Ensure that all animations are canceled

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