1
0
mirror of https://github.com/TeamNewPipe/NewPipe synced 2026-01-14 21:48:00 +00:00

Compare commits

..

249 Commits

Author SHA1 Message Date
Christian Schabesberger
05f8ee9747 fix channel links in description part 2 2018-09-07 22:23:32 +02:00
Christian Schabesberger
818ae03928 fix decrypt url and move on to v0.14.1 2018-09-07 21:51:14 +02:00
Christian Schabesberger
97ad3c1e6d Merge pull request #1654 from mauriciocolli/misc-fixes
Improve "selected tabs" and misc fixes
2018-09-05 20:52:29 +02:00
Mauricio Colli
612228bb73 Update extractor version
- Handle case where subscribers count is not available
- Fix NPE when a YouTube playlist is empty
- Quick fix for the kiosks in SoundCloud
2018-09-05 07:29:15 -03:00
Mauricio Colli
6e75d41956 Use current volume as the start value in the volume gesture
- Renamed some variables/classes to increase readability
2018-09-04 23:54:17 -03:00
Mauricio Colli
9883a38698 Fix registering of broadcast receiver 2018-09-04 23:54:17 -03:00
Mauricio Colli
07256e2e34 Handle case where subscribers count is not available 2018-09-04 23:54:17 -03:00
Mauricio Colli
43674ae80a Improve tabs UX and saving/loading
- Show icons in the tabs list and dialog chooser
- Add a "restore to defaults" button
- Make removing gesture more user intuitive
2018-09-04 23:54:17 -03:00
Christian Schabesberger
c066ebd76f merge extractor fix for empty subscriptioin count 2018-09-04 14:31:08 +02:00
Christian Schabesberger
6e382c64a4 Reciever not registered 2018-09-04 13:07:39 +02:00
Christian Schabesberger
81e76f260c fix drawer header font color for white theme 2018-09-01 12:33:08 +02:00
Christian Schabesberger
93571961ee merge weblate changes 2018-08-31 14:11:16 +02:00
MadderRagax
cb24347b23 Translated using Weblate (Swedish)
Currently translated at 100.0% (383 of 383 strings)
2018-08-30 23:23:27 +02:00
Vincent Tam
146b7be825 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (383 of 383 strings)
2018-08-29 12:35:17 +02:00
Vincent Tam
50203c5f87 Translated using Weblate (Chinese (Simplified))
Currently translated at 27.4% (105 of 383 strings)
2018-08-29 12:35:16 +02:00
Vincent Tam
b188073fb0 Translated using Weblate (Chinese (Hong Kong))
Currently translated at 36.2% (139 of 383 strings)
2018-08-29 12:35:14 +02:00
Christian Schabesberger
91c60df0e9 move on to v0.14.0 2018-08-28 18:04:45 +02:00
Christian Schabesberger
ad065e9281 Merge pull request #1623 from TeamNewPipe/refactor_and_bugfix
Refactor and bugfix
2018-08-28 16:30:09 +02:00
Christian Schabesberger
b1429366da fixes acording to code review
fixes moreacording to code review

fixed link handling once more
2018-08-28 12:19:07 +02:00
ButterflyOfFire
8bf7af2e74 Translated using Weblate (Arabic)
Currently translated at 100.0% (383 of 383 strings)
2018-08-28 02:34:24 +02:00
Christian Schabesberger
2003f51d49 fix thumbnail not shown in background player 2018-08-27 16:37:21 +02:00
Christian Schabesberger
ce83fd9a10 make dash parser ignore segmented streams 2018-08-27 16:37:21 +02:00
Christian Schabesberger
eacbaa3680 fix exception on nothing found 2018-08-27 16:37:21 +02:00
Christian Schabesberger
98c65fb9b7 add more debug statements to BasePlayer 2018-08-27 16:37:21 +02:00
Christian Schabesberger
44a71d8565 add reset extSD card folder dialog 2018-08-27 16:37:21 +02:00
Christian Schabesberger
badd4d3207 fix linkhandling in description
bla
2018-08-27 16:37:21 +02:00
Christian Schabesberger
0f517b803b fix layout width of currentPlayTime 2018-08-27 16:37:21 +02:00
Christian Schabesberger
c2d11e786f rename Search Query handler 2018-08-27 16:37:21 +02:00
Christian Schabesberger
b0efe49e29 fix cycling search results 2018-08-27 16:37:21 +02:00
Christian Schabesberger
2d029b9f76 fix exception when loading premium videos 2018-08-27 16:37:21 +02:00
Dual Natan
8ad917cff0 Translated using Weblate (Macedonian)
Currently translated at 100.0% (383 of 383 strings)
2018-08-25 15:56:40 +02:00
Mauricio Colli
4e478c65d3 Merge pull request #1604 from TheMatten/player_controls
Gesture controls
2018-08-24 13:36:58 -03:00
TheMatten
a469086915 Add background to fast rewind icon, change android:src to tools:src
-White icon was barely visible on bright backgrounds
-Secondly, drawable is set programmatically anyway and so it's setting in
 XML is good just for a confusion
2018-08-24 13:24:35 -03:00
TheMatten
bf05ff6048 Use animated circular design for gesture control (brightness and volume)
-Previous version used emojis for brightness and volume icons, which may
 be inconsistent across devices and do not fit well with other parts of UI
 (Frankly, previous version was more informative than eye-candy)

-This commit replaces old version with circular progress bar that shows
 current value (before conversion). Gesture mode (volume/brightness) is
 indicated by icon that changes between (4/3) modes according to current
 value

-Text information about current value was removed, because with progress
 bar present it does not add any real value to UI.
2018-08-24 13:24:35 -03:00
Christian Schabesberger
a817d8cbf9 git replace getFragmentManager() with getFM() 2018-08-24 12:30:23 +02:00
Christian Schabesberger
4a19c78fa5 despaget certain parts of the new design 2018-08-24 12:27:02 +02:00
Somethingweirdhere
e8bb7da906 Put listener initialization into onCreate 2018-08-24 12:26:16 +02:00
Somethingweirdhere
523477fc2b Added swiping to remove, which is enabled by long-pressing 2018-08-24 12:26:16 +02:00
Somethingweirdhere
c730426be0 Fixed dragging 2018-08-24 12:26:15 +02:00
Somethingweirdhere
57d6c97203 Fixed revert 2018-08-24 12:26:15 +02:00
Somethingweirdhere
fce17aa1d4 Revert "Revert "Changed the default preferences to show trending.""
This reverts commit b441665
2018-08-24 12:26:15 +02:00
Somethingweirdhere
01abc244b1 Fixed revert 2018-08-24 12:26:15 +02:00
Somethingweirdhere
7bedacf5ad Revert "Revert "Changed the way how kiosks are handled""
This reverts commit b020567
2018-08-24 12:26:15 +02:00
Somethingweirdhere
552a1d0464 Options here again 2018-08-24 12:26:15 +02:00
Somethingweirdhere
8dde25532a Code reviewed 2018-08-24 12:26:15 +02:00
Somethingweirdhere
f29fa939ab Removing by long pressing no longer removes a random tab, but the pressed one. 2018-08-24 12:23:26 +02:00
Somethingweirdhere
614bdb33b4 Added dragging 2018-08-24 12:23:26 +02:00
Somethingweirdhere
71761675cf Fixes problems 1-3 2018-08-24 12:23:26 +02:00
Somethingweirdhere
d9194aa859 Revert "Changed the way how kiosks are handled"
This reverts commit f3da712
2018-08-24 12:23:26 +02:00
Somethingweirdhere
f15081a474 Revert "Changed the default preferences to show trending."
This reverts commit 25481d0
2018-08-24 12:23:26 +02:00
Somethingweirdhere
2f99ff4a0c Changed the default preferences to show trending. 2018-08-24 12:23:26 +02:00
Somethingweirdhere
3a7d26aa46 Changed the way how kiosks are handled 2018-08-24 12:23:26 +02:00
Somethingweirdhere
3f35bc593c Ever more UI tweaks 2018-08-24 12:23:26 +02:00
Somethingweirdhere
e5e708d781 UI tweaks 2018-08-24 12:23:26 +02:00
Somethingweirdhere
d694561980 Added fab and handles, made cards cardier 2018-08-24 12:23:26 +02:00
Somethingweirdhere
8d6d18e875 UI redisign 2018-08-24 12:23:26 +02:00
Somethingweirdhere
072e27ed27 Code cleanup 2018-08-24 12:23:26 +02:00
Somethingweirdhere
6d64215614 + New Tab is now on the bottom
Made dialog more beautiful
2018-08-24 12:17:42 +02:00
Somethingweirdhere
33f5ed5b14 Reduced Font size, fixed bugs that were created when moving the setting 2018-08-24 12:17:42 +02:00
Somethingweirdhere
27f509c8e0 Fixed 2. Use CardView to reprecent each tab. 2018-08-24 12:17:42 +02:00
Somethingweirdhere
890b3e13c9 Fixed 1. Put the tab settings into Aperence settings 2018-08-24 12:16:41 +02:00
Somethingweirdhere
b730cb099f Fixed 4. buggy behavior when adding a new tab. 2018-08-24 12:16:41 +02:00
Somethingweirdhere
fc94f184d2 Reduced lag and increased button size for older devices&users. 2018-08-24 12:16:41 +02:00
Somethingweirdhere
cbf6540889 New selection menu 2018-08-24 12:16:41 +02:00
Somethingweirdhere
40804a7fb3 Navigation drawer has services in a new menu! 2018-08-24 12:16:41 +02:00
Somethingweirdhere
d4101c4f43 Nav drawer now moves behind the status bar and the colors also work correctly. 2018-08-24 12:14:53 +02:00
Somethingweirdhere
409bebd5bc Nav drawer now moves behind the status bar 2018-08-24 12:14:53 +02:00
Somethingweirdhere
8e3ad69adb Videos now also open from the History Tab. 2018-08-24 12:14:53 +02:00
Somethingweirdhere
c8e46d9e21 PopUp now looks better on hell theme 2018-08-24 12:14:53 +02:00
Somethingweirdhere
c56241ffc1 Tab icons now work correctly in bright theme 2018-08-24 12:14:53 +02:00
Somethingweirdhere
be62a2bfc5 Fixed icons and tab titles 2018-08-24 12:14:53 +02:00
Somethingweirdhere
5cb7771484 Fixed bugs&crashes 2018-08-24 12:14:53 +02:00
Somethingweirdhere
6675d3e2cd Set up custom Main Page tabs 2018-08-24 12:14:53 +02:00
Somethingweirdhere
8ecbe4c8ad Created a dialog for the main page content 2018-08-24 12:13:44 +02:00
Somethingweirdhere
edb75c4bab Fixed crash in Subscriptions section 2018-08-24 12:12:08 +02:00
Somethingweirdhere
54b21c716a Added drawer menu 2018-08-24 12:04:35 +02:00
Somethingweirdhere
4704274b87 New Branch 2018-08-24 11:54:59 +02:00
Mauricio Colli
78547aa119 Merge pull request #1597 from mauriciocolli/close-popup-overlay
New way to close the popup player
2018-08-23 23:56:41 -03:00
Mauricio Colli
3887231c73 Fix popup position when draggable area is resized
A common case where this happens is when the soft input is visible.
2018-08-22 23:58:12 -03:00
Mauricio Colli
8a29cfbb7e Remove popup shutdown gesture in favor of the new close overlay 2018-08-22 23:58:12 -03:00
Mauricio Colli
a01d6eaf72 Don't make controls visible when moving popup 2018-08-22 23:58:12 -03:00
Mauricio Colli
69fc571b56 Add overlay to close popup 2018-08-22 23:57:57 -03:00
DPap
4cfd9c322b Translated using Weblate (Greek)
Currently translated at 96.6% (370 of 383 strings)
2018-08-22 22:38:46 +02:00
DPap
d2dce8801b Translated using Weblate (Greek)
Currently translated at 91.9% (352 of 383 strings)
2018-08-21 19:38:17 +02:00
DPap
5b8bb9f678 Translated using Weblate (Greek)
Currently translated at 79.6% (305 of 383 strings)
2018-08-20 16:38:19 +02:00
AB
2076f146cf Translated using Weblate (Ukrainian)
Currently translated at 100.0% (383 of 383 strings)
2018-08-18 22:43:37 +02:00
Haris Subandie Md. Suhaimin
5d4f2b7862 Translated using Weblate (Malay)
Currently translated at 7.8% (30 of 383 strings)
2018-08-15 21:43:39 +02:00
Igor Nedoboy
e3815e40d2 Translated using Weblate (Russian)
Currently translated at 100.0% (383 of 383 strings)
2018-08-15 21:43:35 +02:00
Ivan Dekovets
6dccfb4774 Translated using Weblate (Belarusian)
Currently translated at 100.0% (383 of 383 strings)
2018-08-15 13:57:25 +02:00
Haris Subandie Md. Suhaimin
1ccc1f4c1a Added translation using Weblate (Malay) 2018-08-15 13:57:23 +02:00
Ivan Dekovets
042809620a Translated using Weblate (Belarusian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-15 08:48:29 +02:00
Tobias Groza
cb4c8abd94 Translated using Weblate (Telugu)
Currently translated at 34.2% (131 of 383 strings)
2018-08-15 03:30:48 +02:00
Mauricio Colli
e86302f5b9 Added translation using Weblate (Belarusian) 2018-08-15 03:30:43 +02:00
AB
de080d5811 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (383 of 383 strings)
2018-08-14 16:36:58 +02:00
Ale-Ma
21b7045f93 Translated using Weblate (Italian)
Currently translated at 100.0% (383 of 383 strings)
2018-08-14 16:36:55 +02:00
Telugu Speaker
627301f83d Translated using Weblate (Telugu)
Currently translated at 34.2% (131 of 383 strings)
2018-08-14 03:44:47 +02:00
Mauricio Colli
607ca84fcc Merge pull request #1545 from MadderRagax/dev
Fixing #1543 - Removed incorrect explanations of the M4A and WebM audio formats
2018-08-12 23:53:00 -03:00
oscar
a7f36248d0 Removed incorrect explanations of the M4A and WebM audio formats 2018-08-12 23:46:21 -03:00
Mauricio Colli
d008d15167 Merge pull request #1560 from kapodamy/wifi-check-fix
Additional checks to obtain WiFi status
2018-08-12 23:27:00 -03:00
Mauricio Colli
607dc436bd Merge branch 'dev' into wifi-check-fix 2018-08-12 23:20:21 -03:00
Praveen0899
4384948f6c Translated using Weblate (Telugu)
Currently translated at 33.9% (130 of 383 strings)
2018-08-13 03:35:37 +02:00
rimasx
5e05e9ec93 Translated using Weblate (Estonian)
Currently translated at 99,7% (382 of 383 strings)
2018-08-12 22:08:40 +02:00
Igor Nedoboy
ac1fe66cf9 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-12 22:08:34 +02:00
Dharmendra
86732b6ae4 Translated using Weblate (Hindi)
Currently translated at 91.3% (350 of 383 strings)
2018-08-09 20:45:17 +02:00
AB
0713f55e9c Translated using Weblate (Ukrainian)
Currently translated at 100.0% (383 of 383 strings)
2018-08-09 20:45:08 +02:00
Igor Nedoboy
5c32d73409 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-08 13:38:05 +02:00
Igor Nedoboy
5e13a1735d Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-08 01:54:07 +02:00
Igor Nedoboy
6a1fbb00d9 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-08 00:04:07 +02:00
Nathan Follens
20c3badfac Translated using Weblate (Flemish)
Currently translated at 100,0% (383 of 383 strings)
2018-08-07 23:16:56 +02:00
Igor Nedoboy
7817cfe0c1 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-07 23:16:53 +02:00
Nathan Follens
9ed823b5a5 Translated using Weblate (Flemish)
Currently translated at 100,0% (383 of 383 strings)
2018-08-07 10:46:08 +02:00
Nathan Follens
c6a5dedf0a Translated using Weblate (Dutch)
Currently translated at 100,0% (383 of 383 strings)
2018-08-07 10:45:30 +02:00
Igor Nedoboy
01c9ab36b7 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-07 02:11:09 +02:00
Igor Nedoboy
27f5bdeef1 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-07 01:54:23 +02:00
Igor Nedoboy
723898f87d Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-07 00:24:15 +02:00
Igor Nedoboy
bc05cc1445 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-07 00:09:20 +02:00
Igor Nedoboy
dcf4e43e28 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-06 22:23:11 +02:00
Igor Nedoboy
3868c53908 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-06 21:59:23 +02:00
Igor Nedoboy
a71c693ca3 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-06 17:24:30 +02:00
Igor Nedoboy
691f93f01c Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-06 16:52:59 +02:00
Igor Nedoboy
4cff749186 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-06 14:23:44 +02:00
AB
e1ac1547fd Translated using Weblate (Ukrainian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-06 11:58:58 +02:00
Igor Nedoboy
e53bd505fb Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-06 11:58:55 +02:00
mesnevi
cfa926542e Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-06 11:58:53 +02:00
Igor Nedoboy
79097eca47 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-05 22:52:07 +02:00
Igor Nedoboy
d1741e40e3 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-05 20:20:01 +02:00
Igor Nedoboy
5d0528d195 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-05 15:32:32 +02:00
Igor Nedoboy
a9ea06f753 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-05 13:30:04 +02:00
Igor Nedoboy
62e121c12c Translated using Weblate (Ukrainian)
Currently translated at 99,7% (382 of 383 strings)
2018-08-05 12:41:09 +02:00
Igor Nedoboy
02ef05160f Translated using Weblate (Turkish)
Currently translated at 99,7% (382 of 383 strings)
2018-08-05 12:41:09 +02:00
Igor Nedoboy
6e66c013c0 Translated using Weblate (Swedish)
Currently translated at 99,7% (382 of 383 strings)
2018-08-05 12:41:08 +02:00
Igor Nedoboy
dcb11f01e1 Translated using Weblate (Spanish)
Currently translated at 99,7% (382 of 383 strings)
2018-08-05 12:41:08 +02:00
Igor Nedoboy
8a0e4b577c Translated using Weblate (Slovak)
Currently translated at 93,7% (359 of 383 strings)
2018-08-05 12:41:07 +02:00
Igor Nedoboy
b57f420261 Translated using Weblate (Portuguese (Brazil))
Currently translated at 99,7% (382 of 383 strings)
2018-08-05 12:41:06 +02:00
Igor Nedoboy
7d1790abe3 Translated using Weblate (Polish)
Currently translated at 96,3% (369 of 383 strings)
2018-08-05 12:41:06 +02:00
Igor Nedoboy
1fc494571b Translated using Weblate (Norwegian Bokmål)
Currently translated at 98,6% (378 of 383 strings)
2018-08-05 12:41:05 +02:00
Igor Nedoboy
e6d97bc773 Translated using Weblate (Macedonian)
Currently translated at 96,3% (369 of 383 strings)
2018-08-05 12:41:05 +02:00
Igor Nedoboy
0e53323fb7 Translated using Weblate (Italian)
Currently translated at 98,6% (378 of 383 strings)
2018-08-05 12:41:04 +02:00
Igor Nedoboy
eb4764d2b2 Translated using Weblate (German)
Currently translated at 99,7% (382 of 383 strings)
2018-08-05 12:41:04 +02:00
Igor Nedoboy
298a91adbf Translated using Weblate (French)
Currently translated at 98,1% (376 of 383 strings)
2018-08-05 12:41:03 +02:00
Igor Nedoboy
2cb9912039 Translated using Weblate (Flemish)
Currently translated at 98,6% (378 of 383 strings)
2018-08-05 12:41:03 +02:00
Igor Nedoboy
e52bfe4335 Translated using Weblate (Estonian)
Currently translated at 91,6% (351 of 383 strings)
2018-08-05 12:41:02 +02:00
Igor Nedoboy
761a249e05 Translated using Weblate (Dutch)
Currently translated at 99,7% (382 of 383 strings)
2018-08-05 12:41:02 +02:00
Igor Nedoboy
c42df3a0c2 Translated using Weblate (Czech)
Currently translated at 93,9% (360 of 383 strings)
2018-08-05 12:41:01 +02:00
Igor Nedoboy
3d359b7a98 Translated using Weblate (Chinese (Traditional))
Currently translated at 99,7% (382 of 383 strings)
2018-08-05 12:41:01 +02:00
Igor Nedoboy
deef6417ad Translated using Weblate (Chinese (Simplified))
Currently translated at 97,1% (372 of 383 strings)
2018-08-05 12:41:00 +02:00
Igor Nedoboy
f55a8deb97 Translated using Weblate (Catalan)
Currently translated at 99,7% (382 of 383 strings)
2018-08-05 12:40:58 +02:00
Igor Nedoboy
333506e00b Translated using Weblate (Bulgarian)
Currently translated at 98,4% (377 of 383 strings)
2018-08-05 12:40:58 +02:00
Igor Nedoboy
bbc1642b90 Translated using Weblate (Basque)
Currently translated at 99,7% (382 of 383 strings)
2018-08-05 12:40:57 +02:00
Igor Nedoboy
8209eda27a Translated using Weblate (Arabic)
Currently translated at 99,7% (382 of 383 strings)
2018-08-05 12:40:56 +02:00
Igor Nedoboy
b13f7a599b Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-05 12:40:54 +02:00
Igor Nedoboy
cb0f700be1 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-05 12:16:07 +02:00
Igor Nedoboy
7b6d6b466a Translated using Weblate (English)
Currently translated at 99,7% (382 of 383 strings)
2018-08-05 12:13:00 +02:00
Igor Nedoboy
76f97e5c2e Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-05 12:12:59 +02:00
Igor Nedoboy
4669a1ab57 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-04 23:46:04 +02:00
mesnevi
b1ad0edbe1 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-04 23:46:03 +02:00
Igor Nedoboy
51a695d047 Translated using Weblate (Slovak)
Currently translated at 93.7% (359 of 383 strings)
2018-08-04 20:36:34 +02:00
Igor Nedoboy
396e2d14f3 Translated using Weblate (Polish)
Currently translated at 96.3% (369 of 383 strings)
2018-08-04 20:36:34 +02:00
Igor Nedoboy
245479c339 Translated using Weblate (Norwegian Bokmål)
Currently translated at 99.7% (382 of 383 strings)
2018-08-04 20:36:33 +02:00
Igor Nedoboy
092215f47a Translated using Weblate (Macedonian)
Currently translated at 96.3% (369 of 383 strings)
2018-08-04 20:36:32 +02:00
Igor Nedoboy
fcb46db718 Translated using Weblate (Italian)
Currently translated at 98.6% (378 of 383 strings)
2018-08-04 20:36:32 +02:00
Igor Nedoboy
60c58c8b9c Translated using Weblate (Indonesian)
Currently translated at 93.7% (359 of 383 strings)
2018-08-04 20:36:31 +02:00
Igor Nedoboy
124a2839b5 Translated using Weblate (French)
Currently translated at 98.4% (377 of 383 strings)
2018-08-04 20:36:31 +02:00
Igor Nedoboy
ededfe10ab Translated using Weblate (Flemish)
Currently translated at 98.6% (378 of 383 strings)
2018-08-04 20:36:30 +02:00
Igor Nedoboy
81895c20d6 Translated using Weblate (English)
Currently translated at 99.7% (382 of 383 strings)
2018-08-04 20:36:30 +02:00
Igor Nedoboy
46fabe065c Translated using Weblate (Czech)
Currently translated at 93.9% (360 of 383 strings)
2018-08-04 20:36:29 +02:00
Igor Nedoboy
7ac338756a Translated using Weblate (Chinese (Simplified))
Currently translated at 97.1% (372 of 383 strings)
2018-08-04 20:36:28 +02:00
Igor Nedoboy
640b8edd78 Translated using Weblate (Bulgarian)
Currently translated at 98.6% (378 of 383 strings)
2018-08-04 20:36:28 +02:00
Igor Nedoboy
c5d98752fa Translated using Weblate (Esperanto)
Currently translated at 25.0% (96 of 383 strings)
2018-08-04 20:36:25 +02:00
MadderRagax
a6a5bef447 Update translation via weblate
Translated using Weblate (Swedish)

Currently translated at 100.0% (383 of 383 strings)

Translated using Weblate (Chinese (Mandarin))

Currently translated at 27.4% (105 of 383 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 97.1% (372 of 383 strings)

Translated using Weblate (German)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Russian)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Russian)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Russian)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Russian)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Russian)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Russian)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Russian)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Russian)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Arabic)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Basque)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Dutch)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Spanish)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Catalan)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (German)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Turkish)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Swedish)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (French)

Currently translated at 97,1% (372 of 383 strings)

Translated using Weblate (French)

Currently translated at 97,1% (372 of 383 strings)

Translated using Weblate (Russian)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Arabic)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Basque)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Dutch)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Spanish)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Catalan)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (German)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Swedish)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Turkish)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Estonian)

Currently translated at 91.6% (351 of 383 strings)

Translated using Weblate (Russian)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Russian)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Russian)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Russian)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Russian)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Russian)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Russian)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Russian)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Russian)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Russian)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Russian)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Russian)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Russian)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Russian)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Russian)

Currently translated at 100,0% (383 of 383 strings)

Translated using Weblate (Russian)

Currently translated at 100,0% (383 of 383 strings)
2018-08-04 17:58:57 +02:00
Igor Nedoboy
042885c155 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-04 16:17:24 +02:00
Igor Nedoboy
cbb9dcf7d0 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-04 14:43:27 +02:00
Igor Nedoboy
9b080800e1 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-04 14:23:07 +02:00
Igor Nedoboy
6effbf50a8 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-04 14:10:52 +02:00
Igor Nedoboy
398f9aa19a Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-04 13:53:37 +02:00
Igor Nedoboy
935d89747f Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-04 13:41:41 +02:00
Igor Nedoboy
21c2fbfd39 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-04 12:54:43 +02:00
Igor Nedoboy
bd337f3aac Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-04 12:30:54 +02:00
Igor Nedoboy
4a673eee81 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-04 12:10:15 +02:00
Igor Nedoboy
cebf349f9a Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-04 11:58:15 +02:00
Igor Nedoboy
3683deb51c Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-04 11:35:35 +02:00
Igor Nedoboy
99ee076db9 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-04 11:13:01 +02:00
Igor Nedoboy
04f759041f Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-04 10:52:31 +02:00
Igor Nedoboy
75f89059e7 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-04 10:51:04 +02:00
Igor Nedoboy
a81f31156d Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-04 02:51:46 +02:00
Igor Nedoboy
f706452e67 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 23:19:08 +02:00
Igor Nedoboy
b0126afbcf Translated using Weblate (Estonian)
Currently translated at 91.6% (351 of 383 strings)
2018-08-03 23:18:13 +02:00
Igor Nedoboy
5b8393ff89 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 21:51:52 +02:00
Igor Nedoboy
d2235da06a Translated using Weblate (Turkish)
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 21:49:53 +02:00
Igor Nedoboy
a87f6a0791 Translated using Weblate (Swedish)
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 21:46:13 +02:00
Igor Nedoboy
7e7cfb79a4 Translated using Weblate (Ukrainian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 21:45:49 +02:00
Igor Nedoboy
fb43a5265c Translated using Weblate (German)
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 21:44:09 +02:00
Igor Nedoboy
439a814133 Translated using Weblate (Chinese (Traditional))
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 21:42:37 +02:00
Igor Nedoboy
d9dfcc04bf Translated using Weblate (Catalan)
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 21:42:13 +02:00
Igor Nedoboy
0cfac137b7 Translated using Weblate (Spanish)
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 21:41:16 +02:00
Igor Nedoboy
4575ee805a Translated using Weblate (Dutch)
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 21:40:02 +02:00
Igor Nedoboy
67f70ce2cc Translated using Weblate (Basque)
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 21:39:06 +02:00
Igor Nedoboy
2f641ffb13 Translated using Weblate (Arabic)
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 21:38:43 +02:00
Igor Nedoboy
50f92269c2 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 21:37:23 +02:00
Igor Nedoboy
f220f397ae Translated using Weblate (French)
Currently translated at 97,1% (372 of 383 strings)
2018-08-03 19:32:56 +02:00
PiR
cdb4096124 Translated using Weblate (French)
Currently translated at 97,1% (372 of 383 strings)
2018-08-03 19:32:51 +02:00
Igor Nedoboy
01938af65b Translated using Weblate (Swedish)
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 19:32:12 +02:00
Igor Nedoboy
47a1fca32f Translated using Weblate (Ukrainian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 19:31:23 +02:00
Igor Nedoboy
321342cf6d Translated using Weblate (Portuguese (Brazil))
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 19:30:30 +02:00
Igor Nedoboy
086e9beb59 Translated using Weblate (Turkish)
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 19:29:40 +02:00
Igor Nedoboy
3519d4b219 Translated using Weblate (German)
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 19:27:39 +02:00
Igor Nedoboy
9034b9a9ae Translated using Weblate (Chinese (Traditional))
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 19:26:01 +02:00
Igor Nedoboy
90ba8440a0 Translated using Weblate (Catalan)
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 19:24:45 +02:00
Igor Nedoboy
4988b37d6f Translated using Weblate (Spanish)
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 19:23:04 +02:00
Igor Nedoboy
ad4799ee60 Translated using Weblate (Dutch)
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 19:22:17 +02:00
Igor Nedoboy
35229f8ae5 Translated using Weblate (Basque)
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 19:19:46 +02:00
Igor Nedoboy
7a011d9e75 Translated using Weblate (Arabic)
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 19:18:48 +02:00
Igor Nedoboy
3d86835979 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 14:06:07 +02:00
Igor Nedoboy
4073306538 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 14:04:42 +02:00
Igor Nedoboy
b86aa28d6a Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 14:03:46 +02:00
Igor Nedoboy
d43cee29f2 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 14:03:05 +02:00
Igor Nedoboy
acad468b4a Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 14:02:07 +02:00
Igor Nedoboy
4cbe842cfa Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 14:01:13 +02:00
Igor Nedoboy
d93e227190 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 13:59:48 +02:00
Igor Nedoboy
a9cf424998 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-08-03 13:10:41 +02:00
ssantos
6d8fdf46d5 Translated using Weblate (German)
Currently translated at 100,0% (383 of 383 strings)
2018-08-02 22:53:59 +02:00
Hosted Weblate
781f98ef91 Merge branch 'origin/dev' into Weblate 2018-08-02 12:43:38 +02:00
YeeVonAngg
1313f685da Translated using Weblate (Chinese (Simplified))
Currently translated at 97.1% (372 of 383 strings)
2018-08-02 12:43:38 +02:00
YeeVonAngg
4779a993d3 Translated using Weblate (Chinese (Mandarin))
Currently translated at 27.4% (105 of 383 strings)
2018-08-02 12:43:37 +02:00
MadderRagax
342b2ae5dc Translated using Weblate (Swedish)
Currently translated at 100.0% (383 of 383 strings)
2018-08-02 12:43:28 +02:00
Christian Schabesberger
ce8ae40206 Merge pull request #1573 from cpba/patch-1
Fix typo in v0.13.7 changelog
2018-08-02 10:20:15 +02:00
Maxime Burlandy
d0704f621f Translated using Weblate (French)
Currently translated at 97.1% (372 of 383 strings)
2018-08-02 09:38:18 +02:00
D D
c6da4043ed Translated using Weblate (Bulgarian)
Currently translated at 98.4% (377 of 383 strings)
2018-08-02 08:34:57 +02:00
Carles Pastor Badosa
1aa3761d1a Fix typo in v0.13.7 changelog 2018-08-02 03:22:26 +02:00
Víctor Manuel Tapia Ramírez
ff769caf82 Translated using Weblate (Spanish)
Currently translated at 100,0% (383 of 383 strings)
2018-08-01 03:25:41 +02:00
MadderRagax
be6bc68b56 Translated using Weblate (Swedish)
Currently translated at 100.0% (383 of 383 strings)
2018-08-01 00:47:30 +02:00
Dual Natan
feb3d11f63 Translated using Weblate (Swedish)
Currently translated at 100.0% (383 of 383 strings)
2018-08-01 00:47:18 +02:00
Igor Nedoboy
d2f9b063b2 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-07-30 22:19:48 +02:00
Emin Tufan Çetin
315089c361 Translated using Weblate (Turkish)
Currently translated at 100.0% (383 of 383 strings)
2018-07-30 17:43:06 +02:00
Igor Nedoboy
e95df0dbd5 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-07-30 08:05:57 +02:00
Igor Nedoboy
8fed029ee3 Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-07-30 06:06:16 +02:00
Igor Nedoboy
522daf5aff Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-07-30 05:56:58 +02:00
Igor Nedoboy
e948eebe84 Translated using Weblate (Russian)
Currently translated at 99,4% (381 of 383 strings)
2018-07-30 05:31:23 +02:00
Igor Nedoboy
783d4e7e8a Translated using Weblate (Russian)
Currently translated at 100,0% (383 of 383 strings)
2018-07-30 05:13:13 +02:00
Eduardo Caron
1ce2198621 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (383 of 383 strings)
2018-07-30 02:43:29 +02:00
Freddy Morán Jr
f521def4a5 Translated using Weblate (Spanish)
Currently translated at 99.4% (381 of 383 strings)
2018-07-29 21:43:48 +02:00
Eduardo Caron
75202921a1 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (383 of 383 strings)
2018-07-29 02:12:45 +02:00
kapodamy
4ef8b93344 patch for ListHelper.java
double check for null
2018-07-28 12:07:10 -03:00
Andrey mm
881b191b8d Translated using Weblate (Russian)
Currently translated at 100.0% (383 of 383 strings)
2018-07-26 12:41:57 +02:00
mesnevi
b39e071d1e Translated using Weblate (Russian)
Currently translated at 100.0% (383 of 383 strings)
2018-07-26 12:41:52 +02:00
mesnevi
84cb3a1060 Translated using Weblate (Russian)
Currently translated at 100.0% (383 of 383 strings)
2018-07-25 12:19:47 +02:00
AB
f4ea3980c2 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (383 of 383 strings)
2018-07-25 10:43:30 +02:00
Hosted Weblate
ef73720a5e Merge branch 'origin/dev' into Weblate 2018-07-24 04:51:05 +02:00
Rex_sa
d8cdc57702 Translated using Weblate (Arabic)
Currently translated at 100,0% (383 of 383 strings)
2018-07-24 04:51:02 +02:00
149 changed files with 4699 additions and 1481 deletions

View File

@@ -8,8 +8,8 @@ android {
applicationId "org.schabi.newpipe"
minSdkVersion 15
targetSdkVersion 27
versionCode 66
versionName "0.13.7"
versionCode 68
versionName "0.14.1"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
@@ -49,12 +49,13 @@ ext {
icepickLibVersion = '3.2.0'
stethoLibVersion = '1.5.0'
}
dependencies {
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2') {
exclude module: 'support-annotations'
}
implementation 'com.github.TeamNewPipe:NewPipeExtractor:1eff8c5708'
implementation 'com.github.TeamNewPipe:NewPipeExtractor:66c3c3f45241d4b0c909'
testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-core:2.8.9'
@@ -93,6 +94,9 @@ dependencies {
debugImplementation "com.squareup.leakcanary:leakcanary-android:$leakCanaryLibVersion"
releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$leakCanaryLibVersion"
implementation "com.squareup.okhttp3:okhttp:$okHttpLibVersion"
debugImplementation "com.facebook.stetho:stetho-okhttp3:$stethoLibVersion"
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
implementation 'com.android.support:cardview-v7:27.1.1'
}

View File

@@ -76,10 +76,6 @@
android:name=".about.AboutActivity"
android:label="@string/title_activity_about"/>
<activity
android:name=".history.HistoryActivity"
android:label="@string/title_activity_history"/>
<service android:name=".local.subscription.services.SubscriptionsImportService"/>
<service android:name=".local.subscription.services.SubscriptionsExportService"/>
@@ -122,6 +118,7 @@
<activity
android:name=".ReCaptchaActivity"
android:label="@string/reCaptchaActivity"/>
<activity android:name=".download.ExtSDDownloadFailedActivity" />
<provider
android:name="android.support.v4.content.FileProvider"

View File

@@ -4,6 +4,7 @@ import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
@@ -11,7 +12,10 @@ import android.view.View;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.squareup.leakcanary.RefWatcher;
import org.schabi.newpipe.report.UserAction;
import icepick.Icepick;
import icepick.State;
public abstract class BaseFragment extends Fragment {
protected final String TAG = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode());
@@ -20,6 +24,15 @@ public abstract class BaseFragment extends Fragment {
protected AppCompatActivity activity;
public static final ImageLoader imageLoader = ImageLoader.getInstance();
//These values are used for controlling framgents when they are part of the frontpage
@State
protected boolean useAsFrontPage = false;
protected boolean mIsVisibleToUser = false;
public void useAsFrontPage(boolean value) {
useAsFrontPage = value;
}
/*//////////////////////////////////////////////////////////////////////////
// Fragment's Lifecycle
//////////////////////////////////////////////////////////////////////////*/
@@ -72,6 +85,12 @@ public abstract class BaseFragment extends Fragment {
if (refWatcher != null) refWatcher.watch(this);
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
mIsVisibleToUser = isVisibleToUser;
}
/*//////////////////////////////////////////////////////////////////////////
// Init
//////////////////////////////////////////////////////////////////////////*/
@@ -88,8 +107,15 @@ public abstract class BaseFragment extends Fragment {
public void setTitle(String title) {
if (DEBUG) Log.d(TAG, "setTitle() called with: title = [" + title + "]");
if (activity != null && activity.getSupportActionBar() != null) {
if((!useAsFrontPage || mIsVisibleToUser)
&& (activity != null && activity.getSupportActionBar() != null)) {
activity.getSupportActionBar().setTitle(title);
}
}
protected FragmentManager getFM() {
return getParentFragment() == null
? getFragmentManager()
: getParentFragment().getFragmentManager();
}
}

View File

@@ -24,6 +24,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -43,18 +44,22 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.fragments.BackPressable;
import org.schabi.newpipe.fragments.MainFragment;
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
import org.schabi.newpipe.fragments.list.search.SearchFragment;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.KioskTranslator;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.ServiceHelper;
@@ -72,6 +77,19 @@ public class MainActivity extends AppCompatActivity {
private NavigationView drawerItems = null;
private TextView headerServiceView = null;
private boolean servicesShown = false;
private ImageView serviceArrow;
private static final int ITEM_ID_SUBSCRIPTIONS = - 1;
private static final int ITEM_ID_FEED = - 2;
private static final int ITEM_ID_BOOKMARKS = - 3;
private static final int ITEM_ID_DOWNLOADS = - 4;
private static final int ITEM_ID_HISTORY = - 5;
private static final int ITEM_ID_SETTINGS = 0;
private static final int ITEM_ID_ABOUT = 1;
private static final int ORDER = 0;
/*//////////////////////////////////////////////////////////////////////////
// Activity's LifeCycle
//////////////////////////////////////////////////////////////////////////*/
@@ -85,42 +103,66 @@ public class MainActivity extends AppCompatActivity {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window w = getWindow();
w.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
if (getSupportFragmentManager() != null && getSupportFragmentManager().getBackStackEntryCount() == 0) {
initFragments();
}
setSupportActionBar(findViewById(R.id.toolbar));
setupDrawer();
try {
setupDrawer();
} catch (Exception e) {
ErrorActivity.reportUiError(this, e);
}
}
private void setupDrawer() {
private void setupDrawer() throws Exception {
final Toolbar toolbar = findViewById(R.id.toolbar);
drawer = findViewById(R.id.drawer_layout);
drawerItems = findViewById(R.id.navigation);
for(StreamingService s : NewPipe.getServices()) {
final String title = s.getServiceInfo().getName() +
(ServiceHelper.isBeta(s) ? " (beta)" : "");
final MenuItem item = drawerItems.getMenu()
.add(R.id.menu_services_group, s.getServiceId(), 0, title);
item.setIcon(ServiceHelper.getIcon(s.getServiceId()));
//Tabs
int currentServiceId = ServiceHelper.getSelectedServiceId(this);
StreamingService service = NewPipe.getService(currentServiceId);
int kioskId = 0;
for (final String ks : service.getKioskList().getAvailableKiosks()) {
drawerItems.getMenu()
.add(R.id.menu_tabs_group, kioskId, 0, KioskTranslator.getTranslatedKioskName(ks, this))
.setIcon(KioskTranslator.getKioskIcons(ks, this));
kioskId ++;
}
drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(true);
drawerItems.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_SUBSCRIPTIONS, ORDER, R.string.tab_subscriptions)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_channel));
drawerItems.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_FEED, ORDER, R.string.fragment_whats_new)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.rss));
drawerItems.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_BOOKMARKS, ORDER, R.string.tab_bookmarks)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_bookmark));
drawerItems.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_DOWNLOADS, ORDER, R.string.downloads)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.download));
drawerItems.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_HISTORY, ORDER, R.string.action_history)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.history));
toggle = new ActionBarDrawerToggle(this, drawer, toolbar,
R.string.drawer_open, R.string.drawer_close) {
@Override
public void onDrawerClosed(View view) { super.onDrawerClosed(view); }
//Settings and About
drawerItems.getMenu()
.add(R.id.menu_options_about_group, ITEM_ID_SETTINGS, ORDER, R.string.settings)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.settings));
drawerItems.getMenu()
.add(R.id.menu_options_about_group, ITEM_ID_ABOUT, ORDER, R.string.tab_about)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.info));
@Override
public void onDrawerOpened(View drawerView) { super.onDrawerOpened(drawerView); }
@Override
public void onDrawerSlide(View drawerView, float slideOffset) {
super.onDrawerSlide(drawerView, 0);
}
};
toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.drawer_open, R.string.drawer_close);
toggle.syncState();
drawer.addDrawerListener(toggle);
drawer.addDrawerListener(new DrawerLayout.SimpleDrawerListener() {
@@ -133,51 +175,179 @@ public class MainActivity extends AppCompatActivity {
@Override
public void onDrawerClosed(View drawerView) {
if(servicesShown) {
toggleServices();
}
if (lastService != ServiceHelper.getSelectedServiceId(MainActivity.this)) {
new Handler(Looper.getMainLooper()).post(MainActivity.this::recreate);
}
}
});
drawerItems.setNavigationItemSelectedListener(this::changeService);
setupDrawerFooter();
drawerItems.setNavigationItemSelectedListener(this::drawerItemSelected);
setupDrawerHeader();
}
private boolean drawerItemSelected(MenuItem item) {
switch (item.getGroupId()) {
case R.id.menu_services_group:
changeService(item);
break;
case R.id.menu_tabs_group:
try {
tabSelected(item);
} catch (Exception e) {
ErrorActivity.reportUiError(this, e);
}
break;
case R.id.menu_options_about_group:
optionsAboutSelected(item);
break;
default:
return false;
}
private boolean changeService(MenuItem item) {
if (item.getGroupId() != R.id.menu_services_group)
return false;
drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(false);
ServiceHelper.setSelectedServiceId(this, item.getItemId());
drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(true);
drawer.closeDrawers();
return true;
}
private void setupDrawerFooter() {
ImageButton settings = findViewById(R.id.drawer_settings);
ImageButton downloads = findViewById(R.id.drawer_downloads);
ImageButton history = findViewById(R.id.drawer_history);
private void changeService(MenuItem item) {
drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(false);
ServiceHelper.setSelectedServiceId(this, item.getItemId());
drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(true);
}
settings.setOnClickListener(view -> NavigationHelper.openSettings(this));
downloads.setOnClickListener(view ->NavigationHelper.openDownloads(this));
history.setOnClickListener(view ->
NavigationHelper.openStatisticFragment(getSupportFragmentManager()));
private void tabSelected(MenuItem item) throws ExtractionException {
switch(item.getItemId()) {
case ITEM_ID_SUBSCRIPTIONS:
NavigationHelper.openSubscriptionFragment(getSupportFragmentManager());
break;
case ITEM_ID_FEED:
NavigationHelper.openWhatsNewFragment(getSupportFragmentManager());
break;
case ITEM_ID_BOOKMARKS:
NavigationHelper.openBookmarksFragment(getSupportFragmentManager());
break;
case ITEM_ID_DOWNLOADS:
NavigationHelper.openDownloads(this);
break;
case ITEM_ID_HISTORY:
NavigationHelper.openStatisticFragment(getSupportFragmentManager());
break;
default:
int currentServiceId = ServiceHelper.getSelectedServiceId(this);
StreamingService service = NewPipe.getService(currentServiceId);
String serviceName = "";
int kioskId = 0;
for (final String ks : service.getKioskList().getAvailableKiosks()) {
if(kioskId == item.getItemId()) {
serviceName = ks;
}
kioskId ++;
}
NavigationHelper.openKioskFragment(getSupportFragmentManager(), currentServiceId, serviceName);
break;
}
}
private void optionsAboutSelected(MenuItem item) {
switch(item.getItemId()) {
case ITEM_ID_SETTINGS:
NavigationHelper.openSettings(this);
break;
case ITEM_ID_ABOUT:
NavigationHelper.openAbout(this);
break;
}
}
private void setupDrawerHeader() {
headerServiceView = findViewById(R.id.drawer_header_service_view);
Button action = findViewById(R.id.drawer_header_action_button);
NavigationView navigationView = findViewById(R.id.navigation);
View hView = navigationView.getHeaderView(0);
serviceArrow = hView.findViewById(R.id.drawer_arrow);
headerServiceView = hView.findViewById(R.id.drawer_header_service_view);
Button action = hView.findViewById(R.id.drawer_header_action_button);
action.setOnClickListener(view -> {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("https://newpipe.schabi.org/blog/"));
startActivity(intent);
drawer.closeDrawers();
toggleServices();
});
}
private void toggleServices() {
servicesShown = !servicesShown;
drawerItems.getMenu().removeGroup(R.id.menu_services_group);
drawerItems.getMenu().removeGroup(R.id.menu_tabs_group);
drawerItems.getMenu().removeGroup(R.id.menu_options_about_group);
if(servicesShown) {
showServices();
} else {
try {
showTabs();
} catch (Exception e) {
ErrorActivity.reportUiError(this, e);
}
}
}
private void showServices() {
serviceArrow.setImageResource(R.drawable.ic_arrow_up_white);
for(StreamingService s : NewPipe.getServices()) {
final String title = s.getServiceInfo().getName() +
(ServiceHelper.isBeta(s) ? " (beta)" : "");
drawerItems.getMenu()
.add(R.id.menu_services_group, s.getServiceId(), ORDER, title)
.setIcon(ServiceHelper.getIcon(s.getServiceId()));
}
drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(true);
}
private void showTabs() throws ExtractionException {
serviceArrow.setImageResource(R.drawable.ic_arrow_down_white);
//Tabs
int currentServiceId = ServiceHelper.getSelectedServiceId(this);
StreamingService service = NewPipe.getService(currentServiceId);
int kioskId = 0;
for (final String ks : service.getKioskList().getAvailableKiosks()) {
drawerItems.getMenu()
.add(R.id.menu_tabs_group, kioskId, ORDER, KioskTranslator.getTranslatedKioskName(ks, this))
.setIcon(KioskTranslator.getKioskIcons(ks, this));
kioskId ++;
}
drawerItems.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_SUBSCRIPTIONS, ORDER, R.string.tab_subscriptions)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_channel));
drawerItems.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_FEED, ORDER, R.string.fragment_whats_new)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.rss));
drawerItems.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_BOOKMARKS, ORDER, R.string.tab_bookmarks)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_bookmark));
drawerItems.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_DOWNLOADS, ORDER, R.string.downloads)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.download));
drawerItems.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_HISTORY, ORDER, R.string.action_history)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.history));
//Settings and About
drawerItems.getMenu()
.add(R.id.menu_options_about_group, ITEM_ID_SETTINGS, ORDER, R.string.settings)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.settings));
drawerItems.getMenu()
.add(R.id.menu_options_about_group, ITEM_ID_ABOUT, ORDER, R.string.tab_about)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.info));
}
@Override
protected void onDestroy() {
super.onDestroy();
@@ -341,16 +511,13 @@ public class MainActivity extends AppCompatActivity {
onHomeButtonPressed();
return true;
case R.id.action_show_downloads:
return NavigationHelper.openDownloads(this);
return NavigationHelper.openDownloads(this);
case R.id.action_history:
NavigationHelper.openStatisticFragment(getSupportFragmentManager());
return true;
case R.id.action_about:
NavigationHelper.openAbout(this);
return true;
NavigationHelper.openStatisticFragment(getSupportFragmentManager());
return true;
case R.id.action_settings:
NavigationHelper.openSettings(this);
return true;
NavigationHelper.openSettings(this);
return true;
default:
return super.onOptionsItemSelected(item);
}

View File

@@ -0,0 +1,38 @@
package org.schabi.newpipe.download;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import org.schabi.newpipe.R;
import org.schabi.newpipe.settings.NewPipeSettings;
import org.schabi.newpipe.util.ServiceHelper;
import org.schabi.newpipe.util.ThemeHelper;
public class ExtSDDownloadFailedActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ThemeHelper.setTheme(this, ServiceHelper.getSelectedServiceId(this));
}
@Override
protected void onStart() {
super.onStart();
new AlertDialog.Builder(this)
.setTitle(R.string.download_to_sdcard_error_title)
.setMessage(R.string.download_to_sdcard_error_message)
.setPositiveButton(R.string.yes, (DialogInterface dialogInterface, int i) -> {
NewPipeSettings.resetDownloadFolders(this);
finish();
})
.setNegativeButton(R.string.cancel, (DialogInterface dialogInterface, int i) -> {
dialogInterface.dismiss();
finish();
})
.create()
.show();
}
}

View File

@@ -51,9 +51,6 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
protected Button errorButtonRetry;
protected TextView errorTextView;
@State
protected boolean useAsFrontPage = false;
@Override
public void onViewCreated(View rootView, Bundle savedInstanceState) {
super.onViewCreated(rootView, savedInstanceState);
@@ -66,9 +63,6 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
wasLoading.set(isLoading.get());
}
public void useAsFrontPage(boolean value) {
useAsFrontPage = value;
}
/*//////////////////////////////////////////////////////////////////////////
// Init
@@ -93,12 +87,7 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
RxView.clicks(errorButtonRetry)
.debounce(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Object>() {
@Override
public void accept(Object o) throws Exception {
onRetryButtonClicked();
}
});
.subscribe(o -> onRetryButtonClicked());
}
protected void onRetryButtonClicked() {

View File

@@ -14,24 +14,16 @@ public class BlankFragment extends BaseFragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
if(activity != null && activity.getSupportActionBar() != null) {
activity.getSupportActionBar()
.setTitle("NewPipe");
}
setTitle("NewPipe");
return inflater.inflate(R.layout.fragment_blank, container, false);
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(isVisibleToUser) {
if(activity != null && activity.getSupportActionBar() != null) {
activity.getSupportActionBar()
.setTitle("NewPipe");
}
// leave this inline. Will make it harder for copy cats.
// If you are a Copy cat FUCK YOU.
// I WILL FIND YOU, AND I WILL ...
}
setTitle("NewPipe");
// leave this inline. Will make it harder for copy cats.
// If you are a Copy cat FUCK YOU.
// I WILL FIND YOU, AND I WILL ...
}
}

View File

@@ -1,6 +1,5 @@
package org.schabi.newpipe.fragments;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@@ -11,48 +10,36 @@ import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.preference.PreferenceManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
import android.view.ViewGroup;
import org.schabi.newpipe.BaseFragment;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.ServiceList;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.kiosk.KioskList;
import org.schabi.newpipe.fragments.list.channel.ChannelFragment;
import org.schabi.newpipe.local.feed.FeedFragment;
import org.schabi.newpipe.fragments.list.kiosk.KioskFragment;
import org.schabi.newpipe.local.bookmark.BookmarkFragment;
import org.schabi.newpipe.local.subscription.SubscriptionFragment;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.KioskTranslator;
import org.schabi.newpipe.settings.tabs.Tab;
import org.schabi.newpipe.settings.tabs.TabsManager;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.ServiceHelper;
import org.schabi.newpipe.util.ThemeHelper;
import java.util.ArrayList;
import java.util.List;
public class MainFragment extends BaseFragment implements TabLayout.OnTabSelectedListener {
public int currentServiceId = -1;
private ViewPager viewPager;
private SelectedTabsPagerAdapter pagerAdapter;
private TabLayout tabLayout;
/*//////////////////////////////////////////////////////////////////////////
// Constants
//////////////////////////////////////////////////////////////////////////*/
private List<Tab> tabsList = new ArrayList<>();
private TabsManager tabsManager;
private static final int FALLBACK_SERVICE_ID = ServiceList.YouTube.getServiceId();
private static final String FALLBACK_CHANNEL_URL = "https://www.youtube.com/channel/UC-9-kyTW8ZkZNDHQJ6FgpwQ";
private static final String FALLBACK_CHANNEL_NAME = "Music";
private static final String FALLBACK_KIOSK_ID = "Trending";
private static final int KIOSK_MENU_OFFSET = 2000;
private boolean hasTabsChanged = false;
/*//////////////////////////////////////////////////////////////////////////
// Fragment's LifeCycle
@@ -62,11 +49,22 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
tabsManager = TabsManager.getManager(activity);
tabsManager.setSavedTabsListener(() -> {
if (DEBUG) {
Log.d(TAG, "TabsManager.SavedTabsChangeListener: onTabsChanged called, isResumed = " + isResumed());
}
if (isResumed()) {
updateTabs();
} else {
hasTabsChanged = true;
}
});
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
currentServiceId = ServiceHelper.getSelectedServiceId(activity);
return inflater.inflate(R.layout.fragment_main, container, false);
}
@@ -74,30 +72,34 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
protected void initViews(View rootView, Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState);
TabLayout tabLayout = rootView.findViewById(R.id.main_tab_layout);
tabLayout = rootView.findViewById(R.id.main_tab_layout);
viewPager = rootView.findViewById(R.id.pager);
/* Nested fragment, use child fragment here to maintain backstack in view pager. */
PagerAdapter adapter = new PagerAdapter(getChildFragmentManager());
viewPager.setAdapter(adapter);
viewPager.setOffscreenPageLimit(adapter.getCount());
pagerAdapter = new SelectedTabsPagerAdapter(getChildFragmentManager());
viewPager.setAdapter(pagerAdapter);
tabLayout.setupWithViewPager(viewPager);
tabLayout.addOnTabSelectedListener(this);
updateTabs();
}
int channelIcon = ThemeHelper.resolveResourceIdFromAttr(activity, R.attr.ic_channel);
int whatsHotIcon = ThemeHelper.resolveResourceIdFromAttr(activity, R.attr.ic_hot);
int bookmarkIcon = ThemeHelper.resolveResourceIdFromAttr(activity, R.attr.ic_bookmark);
@Override
public void onResume() {
super.onResume();
if (isSubscriptionsPageOnlySelected()) {
tabLayout.getTabAt(0).setIcon(channelIcon);
tabLayout.getTabAt(1).setIcon(bookmarkIcon);
} else {
tabLayout.getTabAt(0).setIcon(whatsHotIcon);
tabLayout.getTabAt(1).setIcon(channelIcon);
tabLayout.getTabAt(2).setIcon(bookmarkIcon);
if (hasTabsChanged) {
hasTabsChanged = false;
updateTabs();
}
}
@Override
public void onDestroy() {
super.onDestroy();
tabsManager.unsetSavedTabsListener();
}
/*//////////////////////////////////////////////////////////////////////////
// Menu
//////////////////////////////////////////////////////////////////////////*/
@@ -107,16 +109,6 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
super.onCreateOptionsMenu(menu, inflater);
if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu + "], inflater = [" + inflater + "]");
inflater.inflate(R.menu.main_fragment_menu, menu);
SubMenu kioskMenu = menu.addSubMenu(Menu.NONE, Menu.NONE, 200, getString(R.string.kiosk));
try {
createKioskMenu(kioskMenu, inflater);
} catch (Exception e) {
ErrorActivity.reportError(activity, e,
activity.getClass(),
null,
ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
"none", "", R.string.app_ui_crash));
}
ActionBar supportActionBar = activity.getSupportActionBar();
if (supportActionBar != null) {
@@ -145,9 +137,33 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
// Tabs
//////////////////////////////////////////////////////////////////////////*/
public void updateTabs() {
tabsList.clear();
tabsList.addAll(tabsManager.getTabs());
pagerAdapter.notifyDataSetChanged();
viewPager.setOffscreenPageLimit(pagerAdapter.getCount());
updateTabsIcon();
updateCurrentTitle();
}
private void updateTabsIcon() {
for (int i = 0; i < tabsList.size(); i++) {
final TabLayout.Tab tabToSet = tabLayout.getTabAt(i);
if (tabToSet != null) {
tabToSet.setIcon(tabsList.get(i).getTabIconRes(activity));
}
}
}
private void updateCurrentTitle() {
setTitle(tabsList.get(viewPager.getCurrentItem()).getTabName(requireContext()));
}
@Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
public void onTabSelected(TabLayout.Tab selectedTab) {
if (DEBUG) Log.d(TAG, "onTabSelected() called with: selectedTab = [" + selectedTab + "]");
updateCurrentTitle();
}
@Override
@@ -156,124 +172,58 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
@Override
public void onTabReselected(TabLayout.Tab tab) {
if (DEBUG) Log.d(TAG, "onTabReselected() called with: tab = [" + tab + "]");
updateCurrentTitle();
}
private class PagerAdapter extends FragmentPagerAdapter {
PagerAdapter(FragmentManager fm) {
super(fm);
private class SelectedTabsPagerAdapter extends FragmentPagerAdapter {
private SelectedTabsPagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return isSubscriptionsPageOnlySelected() ? new SubscriptionFragment() : getMainPageFragment();
case 1:
if(PreferenceManager.getDefaultSharedPreferences(getActivity())
.getString(getString(R.string.main_page_content_key), getString(R.string.blank_page_key))
.equals(getString(R.string.subscription_page_key))) {
return new BookmarkFragment();
} else {
return new SubscriptionFragment();
}
case 2:
return new BookmarkFragment();
default:
return new BlankFragment();
final Tab tab = tabsList.get(position);
Throwable throwable = null;
Fragment fragment = null;
try {
fragment = tab.getFragment();
} catch (ExtractionException e) {
throwable = e;
}
if (throwable != null) {
ErrorActivity.reportError(activity, throwable, activity.getClass(), null,
ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR, "none", "", R.string.app_ui_crash));
return new BlankFragment();
}
if (fragment instanceof BaseFragment) {
((BaseFragment) fragment).useAsFrontPage(true);
}
return fragment;
}
@Override
public CharSequence getPageTitle(int position) {
//return getString(this.tabTitles[position]);
return "";
public int getItemPosition(Object object) {
// Causes adapter to reload all Fragments when
// notifyDataSetChanged is called
return POSITION_NONE;
}
@Override
public int getCount() {
return isSubscriptionsPageOnlySelected() ? 2 : 3;
return tabsList.size();
}
}
/*//////////////////////////////////////////////////////////////////////////
// Main page content
//////////////////////////////////////////////////////////////////////////*/
private boolean isSubscriptionsPageOnlySelected() {
return PreferenceManager.getDefaultSharedPreferences(activity)
.getString(getString(R.string.main_page_content_key), getString(R.string.blank_page_key))
.equals(getString(R.string.subscription_page_key));
}
private Fragment getMainPageFragment() {
if (getActivity() == null) return new BlankFragment();
try {
SharedPreferences preferences =
PreferenceManager.getDefaultSharedPreferences(getActivity());
final String setMainPage = preferences.getString(getString(R.string.main_page_content_key),
getString(R.string.main_page_selectd_kiosk_id));
if (setMainPage.equals(getString(R.string.blank_page_key))) {
return new BlankFragment();
} else if (setMainPage.equals(getString(R.string.kiosk_page_key))) {
int serviceId = preferences.getInt(getString(R.string.main_page_selected_service),
FALLBACK_SERVICE_ID);
String kioskId = preferences.getString(getString(R.string.main_page_selectd_kiosk_id),
FALLBACK_KIOSK_ID);
KioskFragment fragment = KioskFragment.getInstance(serviceId, kioskId);
fragment.useAsFrontPage(true);
return fragment;
} else if (setMainPage.equals(getString(R.string.feed_page_key))) {
FeedFragment fragment = new FeedFragment();
fragment.useAsFrontPage(true);
return fragment;
} else if (setMainPage.equals(getString(R.string.channel_page_key))) {
int serviceId = preferences.getInt(getString(R.string.main_page_selected_service),
FALLBACK_SERVICE_ID);
String url = preferences.getString(getString(R.string.main_page_selected_channel_url),
FALLBACK_CHANNEL_URL);
String name = preferences.getString(getString(R.string.main_page_selected_channel_name),
FALLBACK_CHANNEL_NAME);
ChannelFragment fragment = ChannelFragment.getInstance(serviceId,
url,
name);
fragment.useAsFrontPage(true);
return fragment;
} else {
return new BlankFragment();
}
} catch (Exception e) {
ErrorActivity.reportError(activity, e,
activity.getClass(),
null,
ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
"none", "", R.string.app_ui_crash));
return new BlankFragment();
}
}
/*//////////////////////////////////////////////////////////////////////////
// Select Kiosk
//////////////////////////////////////////////////////////////////////////*/
private void createKioskMenu(Menu menu, MenuInflater menuInflater)
throws Exception {
StreamingService service = NewPipe.getService(currentServiceId);
KioskList kl = service.getKioskList();
int i = 0;
for (final String ks : kl.getAvailableKiosks()) {
menu.add(0, KIOSK_MENU_OFFSET + i, Menu.NONE,
KioskTranslator.getTranslatedKioskName(ks, getContext()))
.setOnMenuItemClickListener(menuItem -> {
try {
NavigationHelper.openKioskFragment(getFragmentManager(), currentServiceId, ks);
} catch (Exception e) {
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
}
return true;
});
i++;
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
getChildFragmentManager()
.beginTransaction()
.remove((Fragment) object)
.commitNowAllowingStateLoss();
}
}
}

View File

@@ -1227,10 +1227,10 @@ public class VideoDetailFragment
spinnerToolbar.setVisibility(View.GONE);
break;
default:
if(info.getAudioStreams().isEmpty()) detailControlsBackground.setVisibility(View.GONE);
if (!info.getVideoStreams().isEmpty()
|| !info.getVideoOnlyStreams().isEmpty()) break;
detailControlsBackground.setVisibility(View.GONE);
detailControlsPopup.setVisibility(View.GONE);
spinnerToolbar.setVisibility(View.GONE);
thumbnailPlayButton.setImageResource(R.drawable.ic_headset_white_24dp);

View File

@@ -156,9 +156,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
public void selected(ChannelInfoItem selectedItem) {
try {
onItemSelected(selectedItem);
NavigationHelper.openChannelFragment(useAsFrontPage ?
getParentFragment().getFragmentManager()
: getFragmentManager(),
NavigationHelper.openChannelFragment(getFM(),
selectedItem.getServiceId(),
selectedItem.getUrl(),
selectedItem.getName());
@@ -173,10 +171,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
public void selected(PlaylistInfoItem selectedItem) {
try {
onItemSelected(selectedItem);
NavigationHelper.openPlaylistFragment(
useAsFrontPage
? getParentFragment().getFragmentManager()
: getFragmentManager(),
NavigationHelper.openPlaylistFragment(getFM(),
selectedItem.getServiceId(),
selectedItem.getUrl(),
selectedItem.getName());
@@ -197,9 +192,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
private void onStreamSelected(StreamInfoItem selectedItem) {
onItemSelected(selectedItem);
NavigationHelper.openVideoDetailFragment(useAsFrontPage
? getParentFragment().getFragmentManager()
: getFragmentManager(),
NavigationHelper.openVideoDetailFragment(getFM(),
selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());
}

View File

@@ -33,15 +33,14 @@ import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.channel.ChannelInfo;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
import org.schabi.newpipe.info_list.InfoItemDialog;
import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
import org.schabi.newpipe.local.subscription.SubscriptionService;
import org.schabi.newpipe.player.playqueue.ChannelPlayQueue;
import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.local.subscription.SubscriptionService;
import org.schabi.newpipe.util.AnimationUtils;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.ImageDisplayConstants;
@@ -91,6 +90,8 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
private MenuItem menuRssButton;
private boolean mIsVisibleToUser = false;
public static ChannelFragment getInstance(int serviceId, String url, String name) {
ChannelFragment instance = new ChannelFragment();
instance.setInitialData(serviceId, url, name);
@@ -104,6 +105,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
mIsVisibleToUser = isVisibleToUser;
if(activity != null
&& useAsFrontPage
&& isVisibleToUser) {
@@ -166,38 +168,35 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
context.getResources().getString(R.string.share)
};
final DialogInterface.OnClickListener actions = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
final int index = Math.max(infoListAdapter.getItemsList().indexOf(item), 0);
switch (i) {
case 0:
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item));
break;
case 1:
NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(item));
break;
case 2:
NavigationHelper.playOnMainPlayer(context, getPlayQueue(index));
break;
case 3:
NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index));
break;
case 4:
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index));
break;
case 5:
if (getFragmentManager() != null) {
PlaylistAppendDialog.fromStreamInfoItems(Collections.singletonList(item))
.show(getFragmentManager(), TAG);
}
break;
case 6:
shareUrl(item.getName(), item.getUrl());
break;
default:
break;
}
final DialogInterface.OnClickListener actions = (DialogInterface dialogInterface, int i) -> {
final int index = Math.max(infoListAdapter.getItemsList().indexOf(item), 0);
switch (i) {
case 0:
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item));
break;
case 1:
NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(item));
break;
case 2:
NavigationHelper.playOnMainPlayer(context, getPlayQueue(index));
break;
case 3:
NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index));
break;
case 4:
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index));
break;
case 5:
if (getFragmentManager() != null) {
PlaylistAppendDialog.fromStreamInfoItems(Collections.singletonList(item))
.show(getFragmentManager(), TAG);
}
break;
case 6:
shareUrl(item.getName(), item.getUrl());
break;
default:
break;
}
};
@@ -255,12 +254,12 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
private static final int BUTTON_DEBOUNCE_INTERVAL = 100;
private void monitorSubscription(final ChannelInfo info) {
final Consumer<Throwable> onError = new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
final Consumer<Throwable> onError = (Throwable throwable) -> {
animateView(headerSubscribeButton, false, 100);
showSnackBarError(throwable, UserAction.SUBSCRIPTION, NewPipe.getNameOfService(currentInfo.getServiceId()), "Get subscription status", 0);
}
showSnackBarError(throwable, UserAction.SUBSCRIPTION,
NewPipe.getNameOfService(currentInfo.getServiceId()),
"Get subscription status",
0);
};
final Observable<List<SubscriptionEntity>> observable = subscriptionService.subscriptionTable()
@@ -276,50 +275,38 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
// so only update the UI for the latest emission ("sync" the subscribe button's state)
.debounce(100, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<List<SubscriptionEntity>>() {
@Override
public void accept(List<SubscriptionEntity> subscriptionEntities) throws Exception {
updateSubscribeButton(!subscriptionEntities.isEmpty());
}
}, onError));
.subscribe((List<SubscriptionEntity> subscriptionEntities) ->
updateSubscribeButton(!subscriptionEntities.isEmpty())
, onError));
}
private Function<Object, Object> mapOnSubscribe(final SubscriptionEntity subscription) {
return new Function<Object, Object>() {
@Override
public Object apply(@NonNull Object o) throws Exception {
subscriptionService.subscriptionTable().insert(subscription);
return o;
}
return (@NonNull Object o) -> {
subscriptionService.subscriptionTable().insert(subscription);
return o;
};
}
private Function<Object, Object> mapOnUnsubscribe(final SubscriptionEntity subscription) {
return new Function<Object, Object>() {
@Override
public Object apply(@NonNull Object o) throws Exception {
subscriptionService.subscriptionTable().delete(subscription);
return o;
}
return (@NonNull Object o) -> {
subscriptionService.subscriptionTable().delete(subscription);
return o;
};
}
private void updateSubscription(final ChannelInfo info) {
if (DEBUG) Log.d(TAG, "updateSubscription() called with: info = [" + info + "]");
final Action onComplete = new Action() {
@Override
public void run() throws Exception {
final Action onComplete = () -> {
if (DEBUG) Log.d(TAG, "Updated subscription: " + info.getUrl());
}
};
final Consumer<Throwable> onError = new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
onUnrecoverableError(throwable, UserAction.SUBSCRIPTION, NewPipe.getNameOfService(info.getServiceId()), "Updating Subscription for " + info.getUrl(), R.string.subscription_update_failed);
}
};
final Consumer<Throwable> onError = (@NonNull Throwable throwable) ->
onUnrecoverableError(throwable,
UserAction.SUBSCRIPTION,
NewPipe.getNameOfService(info.getServiceId()),
"Updating Subscription for " + info.getUrl(),
R.string.subscription_update_failed);
disposables.add(subscriptionService.updateChannelInfo(info)
.subscribeOn(Schedulers.io())
@@ -328,19 +315,16 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
}
private Disposable monitorSubscribeButton(final Button subscribeButton, final Function<Object, Object> action) {
final Consumer<Object> onNext = new Consumer<Object>() {
@Override
public void accept(@NonNull Object o) throws Exception {
final Consumer<Object> onNext = (@NonNull Object o) -> {
if (DEBUG) Log.d(TAG, "Changed subscription status to this channel!");
}
};
final Consumer<Throwable> onError = new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
onUnrecoverableError(throwable, UserAction.SUBSCRIPTION, NewPipe.getNameOfService(currentInfo.getServiceId()), "Subscription Change", R.string.subscription_change_failed);
}
};
final Consumer<Throwable> onError = (@NonNull Throwable throwable) ->
onUnrecoverableError(throwable,
UserAction.SUBSCRIPTION,
NewPipe.getNameOfService(currentInfo.getServiceId()),
"Subscription Change",
R.string.subscription_change_failed);
/* Emit clicks from main thread unto io thread */
return RxView.clicks(subscribeButton)
@@ -352,25 +336,25 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
}
private Consumer<List<SubscriptionEntity>> getSubscribeUpdateMonitor(final ChannelInfo info) {
return new Consumer<List<SubscriptionEntity>>() {
@Override
public void accept(List<SubscriptionEntity> subscriptionEntities) throws Exception {
if (DEBUG)
Log.d(TAG, "subscriptionService.subscriptionTable.doOnNext() called with: subscriptionEntities = [" + subscriptionEntities + "]");
if (subscribeButtonMonitor != null) subscribeButtonMonitor.dispose();
return (List<SubscriptionEntity> subscriptionEntities) -> {
if (DEBUG)
Log.d(TAG, "subscriptionService.subscriptionTable.doOnNext() called with: subscriptionEntities = [" + subscriptionEntities + "]");
if (subscribeButtonMonitor != null) subscribeButtonMonitor.dispose();
if (subscriptionEntities.isEmpty()) {
if (DEBUG) Log.d(TAG, "No subscription to this channel!");
SubscriptionEntity channel = new SubscriptionEntity();
channel.setServiceId(info.getServiceId());
channel.setUrl(info.getUrl());
channel.setData(info.getName(), info.getAvatarUrl(), info.getDescription(), info.getSubscriberCount());
subscribeButtonMonitor = monitorSubscribeButton(headerSubscribeButton, mapOnSubscribe(channel));
} else {
if (DEBUG) Log.d(TAG, "Found subscription to this channel!");
final SubscriptionEntity subscription = subscriptionEntities.get(0);
subscribeButtonMonitor = monitorSubscribeButton(headerSubscribeButton, mapOnUnsubscribe(subscription));
}
if (subscriptionEntities.isEmpty()) {
if (DEBUG) Log.d(TAG, "No subscription to this channel!");
SubscriptionEntity channel = new SubscriptionEntity();
channel.setServiceId(info.getServiceId());
channel.setUrl(info.getUrl());
channel.setData(info.getName(),
info.getAvatarUrl(),
info.getDescription(),
info.getSubscriberCount());
subscribeButtonMonitor = monitorSubscribeButton(headerSubscribeButton, mapOnSubscribe(channel));
} else {
if (DEBUG) Log.d(TAG, "Found subscription to this channel!");
final SubscriptionEntity subscription = subscriptionEntities.get(0);
subscribeButtonMonitor = monitorSubscribeButton(headerSubscribeButton, mapOnUnsubscribe(subscription));
}
};
}
@@ -437,10 +421,12 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
imageLoader.displayImage(result.getAvatarUrl(), headerAvatarView,
ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS);
if (result.getSubscriberCount() != -1) {
headerSubscribersTextView.setVisibility(View.VISIBLE);
if (result.getSubscriberCount() >= 0) {
headerSubscribersTextView.setText(Localization.localizeSubscribersCount(activity, result.getSubscriberCount()));
headerSubscribersTextView.setVisibility(View.VISIBLE);
} else headerSubscribersTextView.setVisibility(View.GONE);
} else {
headerSubscribersTextView.setText(R.string.subscribers_count_not_available);
}
if (menuRssButton != null) menuRssButton.setVisible(!TextUtils.isEmpty(result.getFeedUrl()));
@@ -488,8 +474,11 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
super.handleNextItems(result);
if (!result.getErrors().isEmpty()) {
showSnackBarError(result.getErrors(), UserAction.REQUESTED_CHANNEL, NewPipe.getNameOfService(serviceId),
"Get next page of: " + url, R.string.general_error);
showSnackBarError(result.getErrors(),
UserAction.REQUESTED_CHANNEL,
NewPipe.getNameOfService(serviceId),
"Get next page of: " + url,
R.string.general_error);
}
}
@@ -517,6 +506,6 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
@Override
public void setTitle(String title) {
super.setTitle(title);
headerTitleView.setText(title);
if (!useAsFrontPage) headerTitleView.setText(title);
}
}

View File

@@ -57,6 +57,7 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
protected String kioskId = "";
protected String kioskTranslatedName;
/*//////////////////////////////////////////////////////////////////////////
// Views
//////////////////////////////////////////////////////////////////////////*/
@@ -167,7 +168,9 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
super.handleResult(result);
name = kioskTranslatedName;
setTitle(kioskTranslatedName);
if(!useAsFrontPage) {
setTitle(kioskTranslatedName);
}
if (!result.getErrors().isEmpty()) {
showSnackBarError(result.getErrors(),

View File

@@ -365,7 +365,7 @@ public class SearchFragment
int itemId = 0;
boolean isFirstItem = true;
final Context c = getContext();
for(String filter : service.getSearchQIHFactory().getAvailableContentFilter()) {
for(String filter : service.getSearchQHFactory().getAvailableContentFilter()) {
menuItemToFilterName.put(itemId, filter);
MenuItem item = menu.add(1,
itemId++,
@@ -575,8 +575,7 @@ public class SearchFragment
.onNext(searchEditText.getText().toString()),
throwable -> showSnackBarError(throwable,
UserAction.DELETE_FROM_HISTORY, "none",
"Deleting item failed", R.string.general_error)
);
"Deleting item failed", R.string.general_error));
disposables.add(onDelete);
})
.show();
@@ -837,7 +836,10 @@ public class SearchFragment
@Override
public void handleResult(@NonNull SearchInfo result) {
if (!result.getErrors().isEmpty()) {
final List<Throwable> exceptions = result.getErrors();
if (!exceptions.isEmpty()
&& !(exceptions.size() == 1
&& exceptions.get(0) instanceof SearchExtractor.NothingFoundException)){
showSnackBarError(result.getErrors(), UserAction.SEARCHED,
NewPipe.getNameOfService(serviceId), searchString, 0);
}
@@ -864,6 +866,7 @@ public class SearchFragment
showListFooter(false);
currentPageUrl = result.getNextPageUrl();
infoListAdapter.addInfoItemList(result.getItems());
nextPageUrl = result.getNextPageUrl();
if (!result.getErrors().isEmpty()) {
showSnackBarError(result.getErrors(), UserAction.SEARCHED,

View File

@@ -6,7 +6,6 @@ import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -20,11 +19,9 @@ import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.database.playlist.PlaylistLocalItem;
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.local.BaseLocalListFragment;
import org.schabi.newpipe.local.playlist.LocalPlaylistManager;
import org.schabi.newpipe.local.playlist.RemotePlaylistManager;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.OnClickGesture;
@@ -69,11 +66,10 @@ public final class BookmarkFragment
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
Bundle savedInstanceState) {
if (activity != null && activity.getSupportActionBar() != null) {
activity.getSupportActionBar().setDisplayShowTitleEnabled(true);
activity.setTitle(R.string.tab_subscriptions);
}
if(!useAsFrontPage) {
setTitle(activity.getString(R.string.tab_bookmarks));
}
return inflater.inflate(R.layout.fragment_bookmarks, container, false);
}
@@ -102,26 +98,20 @@ public final class BookmarkFragment
itemListAdapter.setSelectedListener(new OnClickGesture<LocalItem>() {
@Override
public void selected(LocalItem selectedItem) {
try {
// Requires the parent fragment to find holder for fragment replacement
if (getParentFragment() == null) return;
final FragmentManager fragmentManager = getParentFragment().getFragmentManager();
final FragmentManager fragmentManager = getFM();
if (selectedItem instanceof PlaylistMetadataEntry) {
final PlaylistMetadataEntry entry = ((PlaylistMetadataEntry) selectedItem);
NavigationHelper.openLocalPlaylistFragment(fragmentManager, entry.uid,
entry.name);
if (selectedItem instanceof PlaylistMetadataEntry) {
final PlaylistMetadataEntry entry = ((PlaylistMetadataEntry) selectedItem);
NavigationHelper.openLocalPlaylistFragment(fragmentManager, entry.uid,
entry.name);
} else if (selectedItem instanceof PlaylistRemoteEntity) {
final PlaylistRemoteEntity entry = ((PlaylistRemoteEntity) selectedItem);
NavigationHelper.openPlaylistFragment(
fragmentManager,
entry.getServiceId(),
entry.getUrl(),
entry.getName());
}
} catch (Exception e) {
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
} else if (selectedItem instanceof PlaylistRemoteEntity) {
final PlaylistRemoteEntity entry = ((PlaylistRemoteEntity) selectedItem);
NavigationHelper.openPlaylistFragment(
fragmentManager,
entry.getServiceId(),
entry.getUrl(),
entry.getName());
}
}

View File

@@ -71,6 +71,10 @@ public class FeedFragment extends BaseListFragment<List<SubscriptionEntity>, Voi
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
if(!useAsFrontPage) {
setTitle(activity.getString(R.string.fragment_whats_new));
}
return inflater.inflate(R.layout.fragment_feed, container, false);
}
@@ -105,20 +109,19 @@ public class FeedFragment extends BaseListFragment<List<SubscriptionEntity>, Voi
super.onDestroyView();
}
/*@Override
protected RecyclerView.LayoutManager getListLayoutManager() {
boolean isPortrait = getResources().getDisplayMetrics().heightPixels > getResources().getDisplayMetrics().widthPixels;
return new GridLayoutManager(activity, isPortrait ? 1 : 2);
}*/
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (activity != null && isVisibleToUser) {
setTitle(activity.getString(R.string.fragment_whats_new));
}
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
ActionBar supportActionBar = activity.getSupportActionBar();
if (supportActionBar != null) {
supportActionBar.setTitle(R.string.fragment_whats_new);
}
if(useAsFrontPage) {
supportActionBar.setDisplayShowTitleEnabled(true);
@@ -176,19 +179,9 @@ public class FeedFragment extends BaseListFragment<List<SubscriptionEntity>, Voi
showLoading();
showListFooter(true);
subscriptionObserver = subscriptionService.getSubscription()
.onErrorReturnItem(Collections.<SubscriptionEntity>emptyList())
.onErrorReturnItem(Collections.emptyList())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<List<SubscriptionEntity>>() {
@Override
public void accept(List<SubscriptionEntity> subscriptionEntities) throws Exception {
handleResult(subscriptionEntities);
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
onError(throwable);
}
});
.subscribe(this::handleResult, this::onError);
}
@Override
@@ -239,13 +232,12 @@ public class FeedFragment extends BaseListFragment<List<SubscriptionEntity>, Voi
if (!itemsLoaded.contains(subscriptionEntity.getServiceId() + subscriptionEntity.getUrl())) {
subscriptionService.getChannelInfo(subscriptionEntity)
.observeOn(AndroidSchedulers.mainThread())
.onErrorComplete(new Predicate<Throwable>() {
@Override
public boolean test(@io.reactivex.annotations.NonNull Throwable throwable) throws Exception {
return FeedFragment.super.onError(throwable);
}
})
.subscribe(getChannelInfoObserver(subscriptionEntity.getServiceId(), subscriptionEntity.getUrl()));
.onErrorComplete(
(@io.reactivex.annotations.NonNull Throwable throwable) ->
FeedFragment.super.onError(throwable))
.subscribe(
getChannelInfoObserver(subscriptionEntity.getServiceId(),
subscriptionEntity.getUrl()));
} else {
requestFeed(1);
}
@@ -316,7 +308,10 @@ public class FeedFragment extends BaseListFragment<List<SubscriptionEntity>, Voi
@Override
public void onError(Throwable exception) {
showSnackBarError(exception, UserAction.SUBSCRIPTION, NewPipe.getNameOfService(serviceId), url, 0);
showSnackBarError(exception,
UserAction.SUBSCRIPTION,
NewPipe.getNameOfService(serviceId),
url, 0);
requestFeed(1);
onDone();
}
@@ -361,12 +356,7 @@ public class FeedFragment extends BaseListFragment<List<SubscriptionEntity>, Voi
delayHandler.removeCallbacksAndMessages(null);
// Add a little of a delay when requesting more items because the cache is so fast,
// that the view seems stuck to the user when he scroll to the bottom
delayHandler.postDelayed(new Runnable() {
@Override
public void run() {
requestFeed(FEED_LOAD_COUNT);
}
}, 300);
delayHandler.postDelayed(() -> requestFeed(FEED_LOAD_COUNT), 300);
}
@Override
@@ -423,7 +413,9 @@ public class FeedFragment extends BaseListFragment<List<SubscriptionEntity>, Voi
int heightPixels = getResources().getDisplayMetrics().heightPixels;
int itemHeightPixels = activity.getResources().getDimensionPixelSize(R.dimen.video_item_search_height);
int items = itemHeightPixels > 0 ? heightPixels / itemHeightPixels + OFF_SCREEN_ITEMS_COUNT : MIN_ITEMS_INITIAL_LOAD;
int items = itemHeightPixels > 0
? heightPixels / itemHeightPixels + OFF_SCREEN_ITEMS_COUNT
: MIN_ITEMS_INITIAL_LOAD;
return Math.max(MIN_ITEMS_INITIAL_LOAD, items);
}
@@ -441,8 +433,14 @@ public class FeedFragment extends BaseListFragment<List<SubscriptionEntity>, Voi
protected boolean onError(Throwable exception) {
if (super.onError(exception)) return true;
int errorId = exception instanceof ExtractionException ? R.string.parsing_error : R.string.general_error;
onUnrecoverableError(exception, UserAction.SOMETHING_ELSE, "none", "Requesting feed", errorId);
int errorId = exception instanceof ExtractionException
? R.string.parsing_error
: R.string.general_error;
onUnrecoverableError(exception,
UserAction.SOMETHING_ELSE,
"none",
"Requesting feed",
errorId);
return true;
}
}

View File

@@ -21,6 +21,7 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.database.stream.StreamStatisticsEntry;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.fragments.list.BaseListFragment;
import org.schabi.newpipe.local.BaseLocalListFragment;
import org.schabi.newpipe.info_list.InfoItemDialog;
import org.schabi.newpipe.player.playqueue.PlayQueue;
@@ -73,7 +74,7 @@ public class StatisticsPlaylistFragment
return results;
case MOST_PLAYED:
Collections.sort(results, (left, right) ->
((Long) right.watchCount).compareTo(left.watchCount));
Long.compare(right.watchCount, left.watchCount));
return results;
default: return null;
}
@@ -96,6 +97,14 @@ public class StatisticsPlaylistFragment
return inflater.inflate(R.layout.fragment_playlist, container, false);
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (activity != null && isVisibleToUser) {
setTitle(activity.getString(R.string.title_activity_history));
}
}
///////////////////////////////////////////////////////////////////////////
// Fragment LifeCycle - Views
///////////////////////////////////////////////////////////////////////////
@@ -103,7 +112,9 @@ public class StatisticsPlaylistFragment
@Override
protected void initViews(View rootView, Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState);
setTitle(getString(R.string.title_last_played));
if(!useAsFrontPage) {
setTitle(getString(R.string.title_last_played));
}
}
@Override
@@ -129,8 +140,10 @@ public class StatisticsPlaylistFragment
public void selected(LocalItem selectedItem) {
if (selectedItem instanceof StreamStatisticsEntry) {
final StreamStatisticsEntry item = (StreamStatisticsEntry) selectedItem;
NavigationHelper.openVideoDetailFragment(getFragmentManager(),
item.serviceId, item.url, item.title);
NavigationHelper.openVideoDetailFragment(getFM(),
item.serviceId,
item.url,
item.title);
}
}
@@ -341,7 +354,7 @@ public class StatisticsPlaylistFragment
final Disposable onDelete = recordManager.deleteStreamHistory(entry.streamId)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
howManyDelted -> {
howManyDeleted -> {
if(getView() != null) {
Snackbar.make(getView(), R.string.one_item_deleted,
Snackbar.LENGTH_SHORT).show();

View File

@@ -13,6 +13,7 @@ import android.os.Parcelable;
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentManager;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
@@ -209,7 +210,8 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
}
private void setupImportFromItems(final ViewGroup listHolder) {
final View previousBackupItem = addItemView(getString(R.string.previous_export), ThemeHelper.resolveResourceIdFromAttr(getContext(), R.attr.ic_backup), listHolder);
final View previousBackupItem = addItemView(getString(R.string.previous_export),
ThemeHelper.resolveResourceIdFromAttr(getContext(), R.attr.ic_backup), listHolder);
previousBackupItem.setOnClickListener(item -> onImportPreviousSelected());
final int iconColor = ThemeHelper.isLightThemeSelected(getContext()) ? Color.BLACK : Color.WHITE;
@@ -241,8 +243,8 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
}
private void onImportFromServiceSelected(int serviceId) {
if (getParentFragment() == null) return;
NavigationHelper.openSubscriptionsImportFragment(getParentFragment().getFragmentManager(), serviceId);
FragmentManager fragmentManager = getFM();
NavigationHelper.openSubscriptionsImportFragment(fragmentManager, serviceId);
}
private void onImportPreviousSelected() {
@@ -320,21 +322,19 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
infoListAdapter.setOnChannelSelectedListener(new OnClickGesture<ChannelInfoItem>() {
@Override
public void selected(ChannelInfoItem selectedItem) {
try {
// Requires the parent fragment to find holder for fragment replacement
NavigationHelper.openChannelFragment(getParentFragment().getFragmentManager(),
selectedItem.getServiceId(),
selectedItem.getUrl(),
selectedItem.getName());
} catch (Exception e) {
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
}
final FragmentManager fragmentManager = getFM();
NavigationHelper.openChannelFragment(fragmentManager,
selectedItem.getServiceId(),
selectedItem.getUrl(),
selectedItem.getName());
}
});
//noinspection ConstantConditions
whatsNewItemListHeader.setOnClickListener(v ->
NavigationHelper.openWhatsNewFragment(getParentFragment().getFragmentManager()));
whatsNewItemListHeader.setOnClickListener(v -> {
FragmentManager fragmentManager = getFM();
NavigationHelper.openWhatsNewFragment(fragmentManager);
});
importExportListHeader.setOnClickListener(v -> importExportOptions.switchState());
}
@@ -405,10 +405,13 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
private List<InfoItem> getSubscriptionItems(List<SubscriptionEntity> subscriptions) {
List<InfoItem> items = new ArrayList<>();
for (final SubscriptionEntity subscription : subscriptions) items.add(subscription.toChannelInfoItem());
for (final SubscriptionEntity subscription : subscriptions) {
items.add(subscription.toChannelInfoItem());
}
Collections.sort(items,
(InfoItem o1, InfoItem o2) -> o1.getName().compareToIgnoreCase(o2.getName()));
(InfoItem o1, InfoItem o2) ->
o1.getName().compareToIgnoreCase(o2.getName()));
return items;
}
@@ -437,7 +440,11 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
resetFragment();
if (super.onError(exception)) return true;
onUnrecoverableError(exception, UserAction.SOMETHING_ELSE, "none", "Subscriptions", R.string.general_error);
onUnrecoverableError(exception,
UserAction.SOMETHING_ELSE,
"none",
"Subscriptions",
R.string.general_error);
return true;
}
}

View File

@@ -26,6 +26,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.IBinder;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@@ -343,6 +344,7 @@ public final class BackgroundPlayer extends Service {
if (!shouldUpdateOnProgress) return;
resetNotification();
if(Build.VERSION.SDK_INT >= 26 /*Oreo*/) updateNotificationThumbnail();
if (bigNotRemoteView != null) {
bigNotRemoteView.setProgressBar(R.id.notificationProgressBar, duration, currentProgress, false);
bigNotRemoteView.setTextViewText(R.id.notificationTime, getTimeString(currentProgress) + " / " + getTimeString(duration));

View File

@@ -51,6 +51,7 @@ import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.Downloader;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.stream.StreamInfo;
@@ -96,7 +97,7 @@ import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK_ADJ
public abstract class BasePlayer implements
Player.EventListener, PlaybackListener, ImageLoadingListener {
public static final boolean DEBUG = true;
public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release");
@NonNull public static final String TAG = "BasePlayer";
@NonNull final protected Context context;
@@ -172,7 +173,6 @@ public abstract class BasePlayer implements
};
this.intentFilter = new IntentFilter();
setupBroadcastReceiver(intentFilter);
context.registerReceiver(broadcastReceiver, intentFilter);
this.recordManager = new HistoryRecordManager(context);
@@ -209,6 +209,8 @@ public abstract class BasePlayer implements
audioReactor = new AudioReactor(context, simpleExoPlayer);
mediaSessionManager = new MediaSessionManager(context, simpleExoPlayer,
new BasePlayerMediaSession(this));
registerBroadcastReceiver();
}
public void initListeners() {}
@@ -359,11 +361,17 @@ public abstract class BasePlayer implements
}
}
public void unregisterBroadcastReceiver() {
protected void registerBroadcastReceiver() {
// Try to unregister current first
unregisterBroadcastReceiver();
context.registerReceiver(broadcastReceiver, intentFilter);
}
protected void unregisterBroadcastReceiver() {
try {
context.unregisterReceiver(broadcastReceiver);
} catch (final IllegalArgumentException unregisteredException) {
Log.e(TAG, "Broadcast receiver already unregistered.", unregisteredException);
Log.w(TAG, "Broadcast receiver already unregistered (" + unregisteredException.getMessage() + ")");
}
}
@@ -1001,6 +1009,8 @@ public abstract class BasePlayer implements
try {
metadata = (MediaSourceTag) simpleExoPlayer.getCurrentTag();
} catch (IndexOutOfBoundsException | ClassCastException error) {
if(DEBUG) Log.d(TAG, "Could not update metadata: " + error.getMessage());
if(DEBUG) error.printStackTrace();
return;
}
@@ -1087,6 +1097,9 @@ public abstract class BasePlayer implements
return simpleExoPlayer.isCurrentWindowDynamic();
} catch (@NonNull IndexOutOfBoundsException ignored) {
// Why would this even happen =(
// But lets log it anyway. Save is save
if(DEBUG) Log.d(TAG, "Could not update metadata: " + ignored.getMessage());
if(DEBUG) ignored.printStackTrace();
return false;
}
}

View File

@@ -36,6 +36,7 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.content.res.AppCompatResources;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.DisplayMetrics;
@@ -46,7 +47,9 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.TextView;
@@ -82,6 +85,7 @@ import java.util.UUID;
import static org.schabi.newpipe.player.BasePlayer.STATE_PLAYING;
import static org.schabi.newpipe.player.VideoPlayer.DEFAULT_CONTROLS_DURATION;
import static org.schabi.newpipe.player.VideoPlayer.DEFAULT_CONTROLS_HIDE_TIME;
import static org.schabi.newpipe.util.AnimationUtils.Type.SCALE_AND_ALPHA;
import static org.schabi.newpipe.util.AnimationUtils.Type.SLIDE_AND_ALPHA;
import static org.schabi.newpipe.util.AnimationUtils.animateRotation;
import static org.schabi.newpipe.util.AnimationUtils.animateView;
@@ -365,10 +369,16 @@ public final class MainVideoPlayer extends AppCompatActivity
@SuppressWarnings({"unused", "WeakerAccess"})
private class VideoPlayerImpl extends VideoPlayer {
private final float MAX_GESTURE_LENGTH = 0.75f;
private TextView titleTextView;
private TextView channelTextView;
private TextView volumeTextView;
private TextView brightnessTextView;
private RelativeLayout volumeRelativeLayout;
private ProgressBar volumeProgressBar;
private ImageView volumeImageView;
private RelativeLayout brightnessRelativeLayout;
private ProgressBar brightnessProgressBar;
private ImageView brightnessImageView;
private ImageButton queueButton;
private ImageButton repeatButton;
private ImageButton shuffleButton;
@@ -392,6 +402,8 @@ public final class MainVideoPlayer extends AppCompatActivity
private RelativeLayout windowRootLayout;
private View secondaryControls;
private int maxGestureLength;
VideoPlayerImpl(final Context context) {
super("VideoPlayerImpl" + MainVideoPlayer.TAG, context);
}
@@ -401,8 +413,12 @@ public final class MainVideoPlayer extends AppCompatActivity
super.initViews(rootView);
this.titleTextView = rootView.findViewById(R.id.titleTextView);
this.channelTextView = rootView.findViewById(R.id.channelTextView);
this.volumeTextView = rootView.findViewById(R.id.volumeTextView);
this.brightnessTextView = rootView.findViewById(R.id.brightnessTextView);
this.volumeRelativeLayout = rootView.findViewById(R.id.volumeRelativeLayout);
this.volumeProgressBar = rootView.findViewById(R.id.volumeProgressBar);
this.volumeImageView = rootView.findViewById(R.id.volumeImageView);
this.brightnessRelativeLayout = rootView.findViewById(R.id.brightnessRelativeLayout);
this.brightnessProgressBar = rootView.findViewById(R.id.brightnessProgressBar);
this.brightnessImageView = rootView.findViewById(R.id.brightnessImageView);
this.queueButton = rootView.findViewById(R.id.queueButton);
this.repeatButton = rootView.findViewById(R.id.repeatButton);
this.shuffleButton = rootView.findViewById(R.id.shuffleButton);
@@ -444,7 +460,7 @@ public final class MainVideoPlayer extends AppCompatActivity
public void initListeners() {
super.initListeners();
MySimpleOnGestureListener listener = new MySimpleOnGestureListener();
PlayerGestureListener listener = new PlayerGestureListener();
gestureDetector = new GestureDetector(context, listener);
gestureDetector.setIsLongpressEnabled(false);
getRootView().setOnTouchListener(listener);
@@ -461,6 +477,22 @@ public final class MainVideoPlayer extends AppCompatActivity
toggleOrientationButton.setOnClickListener(this);
switchBackgroundButton.setOnClickListener(this);
switchPopupButton.setOnClickListener(this);
getRootView().addOnLayoutChangeListener((view, l, t, r, b, ol, ot, or, ob) -> {
if (l != ol || t != ot || r != or || b != ob) {
// Use smaller value to be consistent between screen orientations
// (and to make usage easier)
int width = r - l, height = b - t;
maxGestureLength = (int) (Math.min(width, height) * MAX_GESTURE_LENGTH);
if (DEBUG) Log.d(TAG, "maxGestureLength = " + maxGestureLength);
volumeProgressBar.setMax(maxGestureLength);
brightnessProgressBar.setMax(maxGestureLength);
setInitialGestureValues();
}
});
}
public void minimize() {
@@ -769,6 +801,13 @@ public final class MainVideoPlayer extends AppCompatActivity
// Utils
//////////////////////////////////////////////////////////////////////////*/
private void setInitialGestureValues() {
if (getAudioReactor() != null) {
final float currentVolumeNormalized = (float) getAudioReactor().getVolume() / getAudioReactor().getMaxVolume();
volumeProgressBar.setProgress((int) (volumeProgressBar.getMax() * currentVolumeNormalized));
}
}
@Override
public void showControlsThenHide() {
if (queueVisible) return;
@@ -872,12 +911,28 @@ public final class MainVideoPlayer extends AppCompatActivity
return channelTextView;
}
public TextView getVolumeTextView() {
return volumeTextView;
public RelativeLayout getVolumeRelativeLayout() {
return volumeRelativeLayout;
}
public TextView getBrightnessTextView() {
return brightnessTextView;
public ProgressBar getVolumeProgressBar() {
return volumeProgressBar;
}
public ImageView getVolumeImageView() {
return volumeImageView;
}
public RelativeLayout getBrightnessRelativeLayout() {
return brightnessRelativeLayout;
}
public ProgressBar getBrightnessProgressBar() {
return brightnessProgressBar;
}
public ImageView getBrightnessImageView() {
return brightnessImageView;
}
public ImageButton getRepeatButton() {
@@ -887,9 +942,13 @@ public final class MainVideoPlayer extends AppCompatActivity
public ImageButton getPlayPauseButton() {
return playPauseButton;
}
public int getMaxGestureLength() {
return maxGestureLength;
}
}
private class MySimpleOnGestureListener extends GestureDetector.SimpleOnGestureListener implements View.OnTouchListener {
private class PlayerGestureListener extends GestureDetector.SimpleOnGestureListener implements View.OnTouchListener {
private boolean isMoving;
@Override
@@ -928,91 +987,91 @@ public final class MainVideoPlayer extends AppCompatActivity
return super.onDown(e);
}
private static final int MOVEMENT_THRESHOLD = 40;
private final boolean isPlayerGestureEnabled = PlayerHelper.isPlayerGestureEnabled(getApplicationContext());
private final int maxVolume = playerImpl.getAudioReactor().getMaxVolume();
private final float stepsBrightness = 15, stepBrightness = (1f / stepsBrightness), minBrightness = .01f;
private float currentBrightness = getWindow().getAttributes().screenBrightness > 0
? getWindow().getAttributes().screenBrightness
: 0.5f;
private int currentVolume, maxVolume = playerImpl.getAudioReactor().getMaxVolume();
private final float stepsVolume = 15, stepVolume = (float) Math.ceil(maxVolume / stepsVolume), minVolume = 0;
private final String brightnessUnicode = new String(Character.toChars(0x2600));
private final String volumeUnicode = new String(Character.toChars(0x1F508));
private final int MOVEMENT_THRESHOLD = 40;
private final int eventsThreshold = 8;
private boolean triggered = false;
private int eventsNum;
// TODO: Improve video gesture controls
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
public boolean onScroll(MotionEvent initialEvent, MotionEvent movingEvent, float distanceX, float distanceY) {
if (!isPlayerGestureEnabled) return false;
//noinspection PointlessBooleanExpression
if (DEBUG && false) Log.d(TAG, "MainVideoPlayer.onScroll = " +
", e1.getRaw = [" + e1.getRawX() + ", " + e1.getRawY() + "]" +
", e2.getRaw = [" + e2.getRawX() + ", " + e2.getRawY() + "]" +
", e1.getRaw = [" + initialEvent.getRawX() + ", " + initialEvent.getRawY() + "]" +
", e2.getRaw = [" + movingEvent.getRawX() + ", " + movingEvent.getRawY() + "]" +
", distanceXy = [" + distanceX + ", " + distanceY + "]");
float abs = Math.abs(e2.getY() - e1.getY());
if (!triggered) {
triggered = abs > MOVEMENT_THRESHOLD;
final boolean insideThreshold = Math.abs(movingEvent.getY() - initialEvent.getY()) <= MOVEMENT_THRESHOLD;
if (!isMoving && (insideThreshold || Math.abs(distanceX) > Math.abs(distanceY))
|| playerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED) {
return false;
}
if (eventsNum++ % eventsThreshold != 0 || playerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED) return false;
isMoving = true;
// boolean up = !((e2.getY() - e1.getY()) > 0) && distanceY > 0; // Android's origin point is on top
boolean up = distanceY > 0;
if (e1.getX() > playerImpl.getRootView().getWidth() / 2) {
double floor = Math.floor(up ? stepVolume : -stepVolume);
currentVolume = (int) (playerImpl.getAudioReactor().getVolume() + floor);
if (currentVolume >= maxVolume) currentVolume = maxVolume;
if (currentVolume <= minVolume) currentVolume = (int) minVolume;
if (initialEvent.getX() > playerImpl.getRootView().getWidth() / 2) {
playerImpl.getVolumeProgressBar().incrementProgressBy((int) distanceY);
float currentProgressPercent =
(float) playerImpl.getVolumeProgressBar().getProgress() / playerImpl.getMaxGestureLength();
int currentVolume = (int) (maxVolume * currentProgressPercent);
playerImpl.getAudioReactor().setVolume(currentVolume);
currentVolume = playerImpl.getAudioReactor().getVolume();
if (DEBUG) Log.d(TAG, "onScroll().volumeControl, currentVolume = " + currentVolume);
final String volumeText = volumeUnicode + " " + Math.round((((float) currentVolume) / maxVolume) * 100) + "%";
playerImpl.getVolumeTextView().setText(volumeText);
if (playerImpl.getVolumeTextView().getVisibility() != View.VISIBLE) animateView(playerImpl.getVolumeTextView(), true, 200);
if (playerImpl.getBrightnessTextView().getVisibility() == View.VISIBLE) playerImpl.getBrightnessTextView().setVisibility(View.GONE);
final int resId =
currentProgressPercent <= 0 ? R.drawable.ic_volume_off_white_72dp
: currentProgressPercent < 0.25 ? R.drawable.ic_volume_mute_white_72dp
: currentProgressPercent < 0.75 ? R.drawable.ic_volume_down_white_72dp
: R.drawable.ic_volume_up_white_72dp;
playerImpl.getVolumeImageView().setImageDrawable(
AppCompatResources.getDrawable(getApplicationContext(), resId)
);
if (playerImpl.getVolumeRelativeLayout().getVisibility() != View.VISIBLE) {
animateView(playerImpl.getVolumeRelativeLayout(), SCALE_AND_ALPHA, true, 200);
}
if (playerImpl.getBrightnessRelativeLayout().getVisibility() == View.VISIBLE) {
playerImpl.getBrightnessRelativeLayout().setVisibility(View.GONE);
}
} else {
WindowManager.LayoutParams lp = getWindow().getAttributes();
currentBrightness += up ? stepBrightness : -stepBrightness;
if (currentBrightness >= 1f) currentBrightness = 1f;
if (currentBrightness <= minBrightness) currentBrightness = minBrightness;
playerImpl.getBrightnessProgressBar().incrementProgressBy((int) distanceY);
float currentProgressPercent =
(float) playerImpl.getBrightnessProgressBar().getProgress() / playerImpl.getMaxGestureLength();
WindowManager.LayoutParams layoutParams = getWindow().getAttributes();
layoutParams.screenBrightness = currentProgressPercent;
getWindow().setAttributes(layoutParams);
lp.screenBrightness = currentBrightness;
getWindow().setAttributes(lp);
if (DEBUG) Log.d(TAG, "onScroll().brightnessControl, currentBrightness = " + currentBrightness);
int brightnessNormalized = Math.round(currentBrightness * 100);
if (DEBUG) Log.d(TAG, "onScroll().brightnessControl, currentBrightness = " + currentProgressPercent);
final String brightnessText = brightnessUnicode + " " + (brightnessNormalized == 1 ? 0 : brightnessNormalized) + "%";
playerImpl.getBrightnessTextView().setText(brightnessText);
final int resId =
currentProgressPercent < 0.25 ? R.drawable.ic_brightness_low_white_72dp
: currentProgressPercent < 0.75 ? R.drawable.ic_brightness_medium_white_72dp
: R.drawable.ic_brightness_high_white_72dp;
if (playerImpl.getBrightnessTextView().getVisibility() != View.VISIBLE) animateView(playerImpl.getBrightnessTextView(), true, 200);
if (playerImpl.getVolumeTextView().getVisibility() == View.VISIBLE) playerImpl.getVolumeTextView().setVisibility(View.GONE);
playerImpl.getBrightnessImageView().setImageDrawable(
AppCompatResources.getDrawable(getApplicationContext(), resId)
);
if (playerImpl.getBrightnessRelativeLayout().getVisibility() != View.VISIBLE) {
animateView(playerImpl.getBrightnessRelativeLayout(), SCALE_AND_ALPHA, true, 200);
}
if (playerImpl.getVolumeRelativeLayout().getVisibility() == View.VISIBLE) {
playerImpl.getVolumeRelativeLayout().setVisibility(View.GONE);
}
}
return true;
}
private void onScrollEnd() {
if (DEBUG) Log.d(TAG, "onScrollEnd() called");
triggered = false;
eventsNum = 0;
/* if (playerImpl.getVolumeTextView().getVisibility() == View.VISIBLE) playerImpl.getVolumeTextView().setVisibility(View.GONE);
if (playerImpl.getBrightnessTextView().getVisibility() == View.VISIBLE) playerImpl.getBrightnessTextView().setVisibility(View.GONE);*/
if (playerImpl.getVolumeTextView().getVisibility() == View.VISIBLE) {
animateView(playerImpl.getVolumeTextView(), false, 200, 200);
if (playerImpl.getVolumeRelativeLayout().getVisibility() == View.VISIBLE) {
animateView(playerImpl.getVolumeRelativeLayout(), SCALE_AND_ALPHA, false, 200, 200);
}
if (playerImpl.getBrightnessTextView().getVisibility() == View.VISIBLE) {
animateView(playerImpl.getBrightnessTextView(), false, 200, 200);
if (playerImpl.getBrightnessRelativeLayout().getVisibility() == View.VISIBLE) {
animateView(playerImpl.getBrightnessRelativeLayout(), SCALE_AND_ALPHA, false, 200, 200);
}
if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == STATE_PLAYING) {

View File

@@ -19,6 +19,8 @@
package org.schabi.newpipe.player;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.SuppressLint;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -34,6 +36,7 @@ import android.os.Build;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.NotificationCompat;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -41,7 +44,9 @@ import android.view.GestureDetector;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.AnticipateInterpolator;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.PopupMenu;
@@ -104,10 +109,13 @@ public final class PopupVideoPlayer extends Service {
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
private WindowManager windowManager;
private WindowManager.LayoutParams windowLayoutParams;
private GestureDetector gestureDetector;
private WindowManager.LayoutParams popupLayoutParams;
private GestureDetector popupGestureDetector;
private View closeOverlayView;
private FloatingActionButton closeOverlayButton;
private WindowManager.LayoutParams closeOverlayLayoutParams;
private int shutdownFlingVelocity;
private int tossFlingVelocity;
private float screenWidth, screenHeight;
@@ -122,6 +130,7 @@ public final class PopupVideoPlayer extends Service {
private VideoPlayerImpl playerImpl;
private LockManager lockManager;
private boolean isPopupClosing = false;
/*//////////////////////////////////////////////////////////////////////////
// Service-Activity Binder
@@ -150,7 +159,10 @@ public final class PopupVideoPlayer extends Service {
public int onStartCommand(final Intent intent, int flags, int startId) {
if (DEBUG)
Log.d(TAG, "onStartCommand() called with: intent = [" + intent + "], flags = [" + flags + "], startId = [" + startId + "]");
if (playerImpl.getPlayer() == null) initPopup();
if (playerImpl.getPlayer() == null) {
initPopup();
initPopupCloseOverlay();
}
if (!playerImpl.isPlaying()) playerImpl.getPlayer().setPlayWhenReady(true);
playerImpl.handleIntent(intent);
@@ -160,15 +172,16 @@ public final class PopupVideoPlayer extends Service {
@Override
public void onConfigurationChanged(Configuration newConfig) {
if (DEBUG) Log.d(TAG, "onConfigurationChanged() called with: newConfig = [" + newConfig + "]");
updateScreenSize();
updatePopupSize(windowLayoutParams.width, -1);
checkPositionBounds();
updatePopupSize(popupLayoutParams.width, -1);
checkPopupPositionBounds();
}
@Override
public void onDestroy() {
if (DEBUG) Log.d(TAG, "onDestroy() called");
onClose();
closePopup();
}
@Override
@@ -186,7 +199,6 @@ public final class PopupVideoPlayer extends Service {
View rootView = View.inflate(this, R.layout.player_popup, null);
playerImpl.setup(rootView);
shutdownFlingVelocity = PlayerHelper.getShutdownFlingVelocity(this);
tossFlingVelocity = PlayerHelper.getTossFlingVelocity(this);
updateScreenSize();
@@ -200,27 +212,52 @@ public final class PopupVideoPlayer extends Service {
WindowManager.LayoutParams.TYPE_PHONE :
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
windowLayoutParams = new WindowManager.LayoutParams(
popupLayoutParams = new WindowManager.LayoutParams(
(int) popupWidth, (int) getMinimumVideoHeight(popupWidth),
layoutParamType,
IDLE_WINDOW_FLAGS,
PixelFormat.TRANSLUCENT);
windowLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
windowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
popupLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
popupLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
int centerX = (int) (screenWidth / 2f - popupWidth / 2f);
int centerY = (int) (screenHeight / 2f - popupHeight / 2f);
windowLayoutParams.x = popupRememberSizeAndPos ? sharedPreferences.getInt(POPUP_SAVED_X, centerX) : centerX;
windowLayoutParams.y = popupRememberSizeAndPos ? sharedPreferences.getInt(POPUP_SAVED_Y, centerY) : centerY;
popupLayoutParams.x = popupRememberSizeAndPos ? sharedPreferences.getInt(POPUP_SAVED_X, centerX) : centerX;
popupLayoutParams.y = popupRememberSizeAndPos ? sharedPreferences.getInt(POPUP_SAVED_Y, centerY) : centerY;
checkPositionBounds();
checkPopupPositionBounds();
MySimpleOnGestureListener listener = new MySimpleOnGestureListener();
gestureDetector = new GestureDetector(this, listener);
PopupWindowGestureListener listener = new PopupWindowGestureListener();
popupGestureDetector = new GestureDetector(this, listener);
rootView.setOnTouchListener(listener);
playerImpl.getLoadingPanel().setMinimumWidth(windowLayoutParams.width);
playerImpl.getLoadingPanel().setMinimumHeight(windowLayoutParams.height);
windowManager.addView(rootView, windowLayoutParams);
playerImpl.getLoadingPanel().setMinimumWidth(popupLayoutParams.width);
playerImpl.getLoadingPanel().setMinimumHeight(popupLayoutParams.height);
windowManager.addView(rootView, popupLayoutParams);
}
@SuppressLint("RtlHardcoded")
private void initPopupCloseOverlay() {
if (DEBUG) Log.d(TAG, "initPopupCloseOverlay() called");
closeOverlayView = View.inflate(this, R.layout.player_popup_close_overlay, null);
closeOverlayButton = closeOverlayView.findViewById(R.id.closeButton);
final int layoutParamType = Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.O ?
WindowManager.LayoutParams.TYPE_PHONE :
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
final int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
closeOverlayLayoutParams = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
layoutParamType,
flags,
PixelFormat.TRANSLUCENT);
closeOverlayLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
closeOverlayLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
closeOverlayButton.setVisibility(View.GONE);
windowManager.addView(closeOverlayView, closeOverlayLayoutParams);
}
/*//////////////////////////////////////////////////////////////////////////
@@ -280,44 +317,105 @@ public final class PopupVideoPlayer extends Service {
// Misc
//////////////////////////////////////////////////////////////////////////*/
public void onClose() {
if (DEBUG) Log.d(TAG, "onClose() called");
public void closePopup() {
if (DEBUG) Log.d(TAG, "closePopup() called, isPopupClosing = " + isPopupClosing);
if (isPopupClosing) return;
isPopupClosing = true;
if (playerImpl != null) {
if (playerImpl.getRootView() != null) {
windowManager.removeView(playerImpl.getRootView());
playerImpl.setRootView(null);
}
playerImpl.setRootView(null);
playerImpl.stopActivityBinding();
playerImpl.destroy();
playerImpl = null;
}
mBinder = null;
if (lockManager != null) lockManager.releaseWifiAndCpu();
if (notificationManager != null) notificationManager.cancel(NOTIFICATION_ID);
mBinder = null;
playerImpl = null;
stopForeground(true);
stopSelf();
animateOverlayAndFinishService();
}
private void animateOverlayAndFinishService() {
final int targetTranslationY = (int) (closeOverlayButton.getRootView().getHeight() - closeOverlayButton.getY());
closeOverlayButton.animate().setListener(null).cancel();
closeOverlayButton.animate()
.setInterpolator(new AnticipateInterpolator())
.translationY(targetTranslationY)
.setDuration(400)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationCancel(Animator animation) {
end();
}
@Override
public void onAnimationEnd(Animator animation) {
end();
}
private void end() {
windowManager.removeView(closeOverlayView);
stopForeground(true);
stopSelf();
}
}).start();
}
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
private void checkPositionBounds() {
if (windowLayoutParams.x > screenWidth - windowLayoutParams.width)
windowLayoutParams.x = (int) (screenWidth - windowLayoutParams.width);
if (windowLayoutParams.x < 0) windowLayoutParams.x = 0;
if (windowLayoutParams.y > screenHeight - windowLayoutParams.height)
windowLayoutParams.y = (int) (screenHeight - windowLayoutParams.height);
if (windowLayoutParams.y < 0) windowLayoutParams.y = 0;
/**
* @see #checkPopupPositionBounds(float, float)
*/
@SuppressWarnings("UnusedReturnValue")
private boolean checkPopupPositionBounds() {
return checkPopupPositionBounds(screenWidth, screenHeight);
}
/**
* Check if {@link #popupLayoutParams}' position is within a arbitrary boundary that goes from (0,0) to (boundaryWidth,boundaryHeight).
* <p>
* If it's out of these boundaries, {@link #popupLayoutParams}' position is changed and {@code true} is returned
* to represent this change.
*
* @return if the popup was out of bounds and have been moved back to it
*/
private boolean checkPopupPositionBounds(final float boundaryWidth, final float boundaryHeight) {
if (DEBUG) {
Log.d(TAG, "checkPopupPositionBounds() called with: boundaryWidth = [" + boundaryWidth + "], boundaryHeight = [" + boundaryHeight + "]");
}
if (popupLayoutParams.x < 0) {
popupLayoutParams.x = 0;
return true;
} else if (popupLayoutParams.x > boundaryWidth - popupLayoutParams.width) {
popupLayoutParams.x = (int) (boundaryWidth - popupLayoutParams.width);
return true;
}
if (popupLayoutParams.y < 0) {
popupLayoutParams.y = 0;
return true;
} else if (popupLayoutParams.y > boundaryHeight - popupLayoutParams.height) {
popupLayoutParams.y = (int) (boundaryHeight - popupLayoutParams.height);
return true;
}
return false;
}
private void savePositionAndSize() {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(PopupVideoPlayer.this);
sharedPreferences.edit().putInt(POPUP_SAVED_X, windowLayoutParams.x).apply();
sharedPreferences.edit().putInt(POPUP_SAVED_Y, windowLayoutParams.y).apply();
sharedPreferences.edit().putFloat(POPUP_SAVED_WIDTH, windowLayoutParams.width).apply();
sharedPreferences.edit().putInt(POPUP_SAVED_X, popupLayoutParams.x).apply();
sharedPreferences.edit().putInt(POPUP_SAVED_Y, popupLayoutParams.y).apply();
sharedPreferences.edit().putFloat(POPUP_SAVED_WIDTH, popupLayoutParams.width).apply();
}
private float getMinimumVideoHeight(float width) {
@@ -352,13 +450,13 @@ public final class PopupVideoPlayer extends Service {
if (height == -1) height = (int) getMinimumVideoHeight(width);
else height = (int) (height > maximumHeight ? maximumHeight : height < minimumHeight ? minimumHeight : height);
windowLayoutParams.width = width;
windowLayoutParams.height = height;
popupLayoutParams.width = width;
popupLayoutParams.height = height;
popupWidth = width;
popupHeight = height;
if (DEBUG) Log.d(TAG, "updatePopupSize() updated values: width = [" + width + "], height = [" + height + "]");
windowManager.updateViewLayout(playerImpl.getRootView(), windowLayoutParams);
windowManager.updateViewLayout(playerImpl.getRootView(), popupLayoutParams);
}
protected void setRepeatModeRemote(final RemoteViews remoteViews, final int repeatMode) {
@@ -380,10 +478,10 @@ public final class PopupVideoPlayer extends Service {
}
private void updateWindowFlags(final int flags) {
if (windowLayoutParams == null || windowManager == null || playerImpl == null) return;
if (popupLayoutParams == null || windowManager == null || playerImpl == null) return;
windowLayoutParams.flags = flags;
windowManager.updateViewLayout(playerImpl.getRootView(), windowLayoutParams);
popupLayoutParams.flags = flags;
windowManager.updateViewLayout(playerImpl.getRootView(), popupLayoutParams);
}
///////////////////////////////////////////////////////////////////////////
@@ -393,6 +491,7 @@ public final class PopupVideoPlayer extends Service {
private ImageView videoPlayPause;
private View extraOptionsView;
private View closingOverlayView;
@Override
public void handleIntent(Intent intent) {
@@ -413,12 +512,18 @@ public final class PopupVideoPlayer extends Service {
fullScreenButton = rootView.findViewById(R.id.fullScreenButton);
fullScreenButton.setOnClickListener(v -> onFullScreenButtonClicked());
videoPlayPause = rootView.findViewById(R.id.videoPlayPause);
videoPlayPause.setOnClickListener(this::onPlayPauseButtonPressed);
extraOptionsView = rootView.findViewById(R.id.extraOptionsView);
closingOverlayView = rootView.findViewById(R.id.closingOverlay);
rootView.addOnLayoutChangeListener(this);
}
@Override
public void initListeners() {
super.initListeners();
videoPlayPause.setOnClickListener(v -> onPlayPause());
}
@Override
protected void setupSubtitleView(@NonNull SubtitleView view,
final float captionScale,
@@ -429,10 +534,6 @@ public final class PopupVideoPlayer extends Service {
view.setStyle(captionStyle);
}
private void onPlayPauseButtonPressed(View ib) {
onPlayPause();
}
@Override
public void onLayoutChange(final View view, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
@@ -476,7 +577,7 @@ public final class PopupVideoPlayer extends Service {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
context.startActivity(intent);
onClose();
closePopup();
}
@Override
@@ -634,7 +735,7 @@ public final class PopupVideoPlayer extends Service {
@Override
public void onPlaybackShutdown() {
super.onPlaybackShutdown();
onClose();
closePopup();
}
/*//////////////////////////////////////////////////////////////////////////
@@ -660,7 +761,7 @@ public final class PopupVideoPlayer extends Service {
if (DEBUG) Log.d(TAG, "onBroadcastReceived() called with: intent = [" + intent + "]");
switch (intent.getAction()) {
case ACTION_CLOSE:
onClose();
closePopup();
break;
case ACTION_PLAY_PAUSE:
onPlayPause();
@@ -791,12 +892,15 @@ public final class PopupVideoPlayer extends Service {
public TextView getResizingIndicator() {
return resizingIndicator;
}
public View getClosingOverlayView() {
return closingOverlayView;
}
}
private class MySimpleOnGestureListener extends GestureDetector.SimpleOnGestureListener implements View.OnTouchListener {
private class PopupWindowGestureListener extends GestureDetector.SimpleOnGestureListener implements View.OnTouchListener {
private int initialPopupX, initialPopupY;
private boolean isMoving;
private boolean isResizing;
@Override
@@ -832,10 +936,15 @@ public final class PopupVideoPlayer extends Service {
@Override
public boolean onDown(MotionEvent e) {
if (DEBUG) Log.d(TAG, "onDown() called with: e = [" + e + "]");
initialPopupX = windowLayoutParams.x;
initialPopupY = windowLayoutParams.y;
popupWidth = windowLayoutParams.width;
popupHeight = windowLayoutParams.height;
// Fix popup position when the user touch it, it may have the wrong one
// because the soft input is visible (the draggable area is currently resized).
checkPopupPositionBounds(closeOverlayView.getWidth(), closeOverlayView.getHeight());
initialPopupX = popupLayoutParams.x;
initialPopupY = popupLayoutParams.y;
popupWidth = popupLayoutParams.width;
popupHeight = popupLayoutParams.height;
return super.onDown(e);
}
@@ -843,20 +952,22 @@ public final class PopupVideoPlayer extends Service {
public void onLongPress(MotionEvent e) {
if (DEBUG) Log.d(TAG, "onLongPress() called with: e = [" + e + "]");
updateScreenSize();
checkPositionBounds();
checkPopupPositionBounds();
updatePopupSize((int) screenWidth, -1);
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (isResizing || playerImpl == null) return super.onScroll(e1, e2, distanceX, distanceY);
public boolean onScroll(MotionEvent initialEvent, MotionEvent movingEvent, float distanceX, float distanceY) {
if (isResizing || playerImpl == null) return super.onScroll(initialEvent, movingEvent, distanceX, distanceY);
if (!isMoving) {
animateView(closeOverlayButton, true, 200);
}
if (playerImpl.getCurrentState() != BasePlayer.STATE_BUFFERING
&& (!isMoving || playerImpl.getControlsRoot().getAlpha() != 1f)) playerImpl.showControls(0);
isMoving = true;
float diffX = (int) (e2.getRawX() - e1.getRawX()), posX = (int) (initialPopupX + diffX);
float diffY = (int) (e2.getRawY() - e1.getRawY()), posY = (int) (initialPopupY + diffY);
float diffX = (int) (movingEvent.getRawX() - initialEvent.getRawX()), posX = (int) (initialPopupX + diffX);
float diffY = (int) (movingEvent.getRawY() - initialEvent.getRawY()), posY = (int) (initialPopupY + diffY);
if (posX > (screenWidth - popupWidth)) posX = (int) (screenWidth - popupWidth);
else if (posX < 0) posX = 0;
@@ -864,26 +975,49 @@ public final class PopupVideoPlayer extends Service {
if (posY > (screenHeight - popupHeight)) posY = (int) (screenHeight - popupHeight);
else if (posY < 0) posY = 0;
windowLayoutParams.x = (int) posX;
windowLayoutParams.y = (int) posY;
popupLayoutParams.x = (int) posX;
popupLayoutParams.y = (int) posY;
final View closingOverlayView = playerImpl.getClosingOverlayView();
if (isInsideClosingRadius(movingEvent)) {
if (closingOverlayView.getVisibility() == View.GONE) {
animateView(closingOverlayView, true, 250);
}
} else {
if (closingOverlayView.getVisibility() == View.VISIBLE) {
animateView(closingOverlayView, false, 0);
}
}
//noinspection PointlessBooleanExpression
if (DEBUG && false) Log.d(TAG, "PopupVideoPlayer.onScroll = " +
", e1.getRaw = [" + e1.getRawX() + ", " + e1.getRawY() + "]" +
", e2.getRaw = [" + e2.getRawX() + ", " + e2.getRawY() + "]" +
", distanceXy = [" + distanceX + ", " + distanceY + "]" +
", posXy = [" + posX + ", " + posY + "]" +
", popupWh = [" + popupWidth + " x " + popupHeight + "]");
windowManager.updateViewLayout(playerImpl.getRootView(), windowLayoutParams);
if (DEBUG && false) {
Log.d(TAG, "PopupVideoPlayer.onScroll = " +
", e1.getRaw = [" + initialEvent.getRawX() + ", " + initialEvent.getRawY() + "]" + ", e1.getX,Y = [" + initialEvent.getX() + ", " + initialEvent.getY() + "]" +
", e2.getRaw = [" + movingEvent.getRawX() + ", " + movingEvent.getRawY() + "]" + ", e2.getX,Y = [" + movingEvent.getX() + ", " + movingEvent.getY() + "]" +
", distanceX,Y = [" + distanceX + ", " + distanceY + "]" +
", posX,Y = [" + posX + ", " + posY + "]" +
", popupW,H = [" + popupWidth + " x " + popupHeight + "]");
}
windowManager.updateViewLayout(playerImpl.getRootView(), popupLayoutParams);
return true;
}
private void onScrollEnd() {
private void onScrollEnd(MotionEvent event) {
if (DEBUG) Log.d(TAG, "onScrollEnd() called");
if (playerImpl == null) return;
if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == STATE_PLAYING) {
playerImpl.hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME);
}
if (isInsideClosingRadius(event)) {
closePopup();
} else {
animateView(playerImpl.getClosingOverlayView(), false, 0);
if (!isPopupClosing) {
animateView(closeOverlayButton, false, 200);
}
}
}
@Override
@@ -893,14 +1027,11 @@ public final class PopupVideoPlayer extends Service {
final float absVelocityX = Math.abs(velocityX);
final float absVelocityY = Math.abs(velocityY);
if (absVelocityX > shutdownFlingVelocity) {
onClose();
return true;
} else if (Math.max(absVelocityX, absVelocityY) > tossFlingVelocity) {
if (absVelocityX > tossFlingVelocity) windowLayoutParams.x = (int) velocityX;
if (absVelocityY > tossFlingVelocity) windowLayoutParams.y = (int) velocityY;
checkPositionBounds();
windowManager.updateViewLayout(playerImpl.getRootView(), windowLayoutParams);
if (Math.max(absVelocityX, absVelocityY) > tossFlingVelocity) {
if (absVelocityX > tossFlingVelocity) popupLayoutParams.x = (int) velocityX;
if (absVelocityY > tossFlingVelocity) popupLayoutParams.y = (int) velocityY;
checkPopupPositionBounds();
windowManager.updateViewLayout(playerImpl.getRootView(), popupLayoutParams);
return true;
}
return false;
@@ -908,7 +1039,7 @@ public final class PopupVideoPlayer extends Service {
@Override
public boolean onTouch(View v, MotionEvent event) {
gestureDetector.onTouchEvent(event);
popupGestureDetector.onTouchEvent(event);
if (playerImpl == null) return false;
if (event.getPointerCount() == 2 && !isResizing) {
if (DEBUG) Log.d(TAG, "onTouch() 2 finger pointer detected, enabling resizing.");
@@ -931,7 +1062,7 @@ public final class PopupVideoPlayer extends Service {
Log.d(TAG, "onTouch() ACTION_UP > v = [" + v + "], e1.getRaw = [" + event.getRawX() + ", " + event.getRawY() + "]");
if (isMoving) {
isMoving = false;
onScrollEnd();
onScrollEnd(event);
}
if (isResizing) {
@@ -939,7 +1070,10 @@ public final class PopupVideoPlayer extends Service {
animateView(playerImpl.getResizingIndicator(), false, 100, 0);
playerImpl.changeState(playerImpl.getCurrentState());
}
savePositionAndSize();
if (!isPopupClosing) {
savePositionAndSize();
}
}
v.performClick();
@@ -955,13 +1089,13 @@ public final class PopupVideoPlayer extends Service {
final float diff = Math.abs(firstPointerX - secondPointerX);
if (firstPointerX > secondPointerX) {
// second pointer is the anchor (the leftmost pointer)
windowLayoutParams.x = (int) (event.getRawX() - diff);
popupLayoutParams.x = (int) (event.getRawX() - diff);
} else {
// first pointer is the anchor
windowLayoutParams.x = (int) event.getRawX();
popupLayoutParams.x = (int) event.getRawX();
}
checkPositionBounds();
checkPopupPositionBounds();
updateScreenSize();
final int width = (int) Math.min(screenWidth, diff);
@@ -969,5 +1103,29 @@ public final class PopupVideoPlayer extends Service {
return true;
}
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
private int distanceFromCloseButton(MotionEvent popupMotionEvent) {
final int closeOverlayButtonX = closeOverlayButton.getLeft() + closeOverlayButton.getWidth() / 2;
final int closeOverlayButtonY = closeOverlayButton.getTop() + closeOverlayButton.getHeight() / 2;
float fingerX = popupLayoutParams.x + popupMotionEvent.getX();
float fingerY = popupLayoutParams.y + popupMotionEvent.getY();
return (int) Math.sqrt(Math.pow(closeOverlayButtonX - fingerX, 2) + Math.pow(closeOverlayButtonY - fingerY, 2));
}
private float getClosingRadius() {
final int buttonRadius = closeOverlayButton.getWidth() / 2;
// 20% wider than the button itself
return buttonRadius * 1.2f;
}
private boolean isInsideClosingRadius(MotionEvent popupMotionEvent) {
return distanceFromCloseButton(popupMotionEvent) <= getClosingRadius();
}
}
}

View File

@@ -16,6 +16,7 @@ import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.PopupMenu;
@@ -562,6 +563,12 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
if (player != null) {
progressLiveSync.setClickable(!player.isLiveEdge());
}
// this will make shure progressCurrentTime has the same width as progressEndTime
final ViewGroup.LayoutParams endTimeParams = progressEndTime.getLayoutParams();
final ViewGroup.LayoutParams currentTimeParams = progressCurrentTime.getLayoutParams();
currentTimeParams.width = progressEndTime.getWidth();
progressCurrentTime.setLayoutParams(currentTimeParams);
}
@Override

View File

@@ -251,10 +251,6 @@ public class PlayerHelper {
return true;
}
public static int getShutdownFlingVelocity(@NonNull final Context context) {
return 6000;
}
public static int getTossFlingVelocity(@NonNull final Context context) {
return 2500;
}

View File

@@ -6,6 +6,7 @@ import android.util.Log;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.player.playqueue.events.AppendEvent;
import org.schabi.newpipe.player.playqueue.events.ErrorEvent;
import org.schabi.newpipe.player.playqueue.events.InitEvent;
@@ -41,7 +42,7 @@ import io.reactivex.subjects.BehaviorSubject;
public abstract class PlayQueue implements Serializable {
private final String TAG = "PlayQueue@" + Integer.toHexString(hashCode());
public static final boolean DEBUG = true;
public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release");
private ArrayList<PlayQueueItem> backup;
private ArrayList<PlayQueueItem> streams;

View File

@@ -15,7 +15,8 @@ public enum UserAction {
REQUESTED_CHANNEL("requested channel"),
REQUESTED_PLAYLIST("requested playlist"),
REQUESTED_KIOSK("requested kiosk"),
DELETE_FROM_HISTORY("delete from history");
DELETE_FROM_HISTORY("delete from history"),
PLAY_STREAM("Play stream");
private final String message;

View File

@@ -9,6 +9,7 @@ import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.preference.ListPreference;
import android.support.v7.preference.Preference;
import android.util.Log;
@@ -20,15 +21,12 @@ import com.nostra13.universalimageloader.core.ImageLoader;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.FilePickerActivityHelper;
import org.schabi.newpipe.util.KioskTranslator;
import org.schabi.newpipe.util.ZipHelper;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
@@ -42,11 +40,8 @@ import java.util.Date;
import java.util.Locale;
import java.util.Map;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import static android.content.Context.MODE_PRIVATE;
public class ContentSettingsFragment extends BasePreferenceFragment {
private static final int REQUEST_IMPORT_PATH = 8945;
@@ -98,68 +93,6 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
addPreferencesFromResource(R.xml.content_settings);
final ListPreference mainPageContentPref = (ListPreference) findPreference(getString(R.string.main_page_content_key));
mainPageContentPref.setOnPreferenceChangeListener((Preference preference, Object newValueO) -> {
final String newValue = newValueO.toString();
final String mainPrefOldValue =
defaultPreferences.getString(getString(R.string.main_page_content_key), "blank_page");
final String mainPrefOldSummary = getMainPagePrefSummery(mainPrefOldValue, mainPageContentPref);
if(newValue.equals(getString(R.string.kiosk_page_key))) {
SelectKioskFragment selectKioskFragment = new SelectKioskFragment();
selectKioskFragment.setOnSelectedLisener((String kioskId, int service_id) -> {
defaultPreferences.edit()
.putInt(getString(R.string.main_page_selected_service), service_id).apply();
defaultPreferences.edit()
.putString(getString(R.string.main_page_selectd_kiosk_id), kioskId).apply();
String serviceName = "";
try {
serviceName = NewPipe.getService(service_id).getServiceInfo().getName();
} catch (ExtractionException e) {
onError(e);
}
String kioskName = KioskTranslator.getTranslatedKioskName(kioskId,
getContext());
String summary =
String.format(getString(R.string.service_kiosk_string),
serviceName,
kioskName);
mainPageContentPref.setSummary(summary);
});
selectKioskFragment.setOnCancelListener(() -> {
mainPageContentPref.setSummary(mainPrefOldSummary);
mainPageContentPref.setValue(mainPrefOldValue);
});
selectKioskFragment.show(getFragmentManager(), "select_kiosk");
} else if(newValue.equals(getString(R.string.channel_page_key))) {
SelectChannelFragment selectChannelFragment = new SelectChannelFragment();
selectChannelFragment.setOnSelectedLisener((String url, String name, int service) -> {
defaultPreferences.edit()
.putInt(getString(R.string.main_page_selected_service), service).apply();
defaultPreferences.edit()
.putString(getString(R.string.main_page_selected_channel_url), url).apply();
defaultPreferences.edit()
.putString(getString(R.string.main_page_selected_channel_name), name).apply();
mainPageContentPref.setSummary(name);
});
selectChannelFragment.setOnCancelListener(() -> {
mainPageContentPref.setSummary(mainPrefOldSummary);
mainPageContentPref.setValue(mainPrefOldValue);
});
selectChannelFragment.show(getFragmentManager(), "select_channel");
} else {
mainPageContentPref.setSummary(getMainPageSummeryByKey(newValue));
}
defaultPreferences.edit().putBoolean(Constants.KEY_MAIN_PAGE_CHANGE, true).apply();
return true;
});
Preference importDataPreference = findPreference(getString(R.string.import_data));
importDataPreference.setOnPreferenceClickListener((Preference p) -> {
Intent i = new Intent(getActivity(), FilePickerActivityHelper.class)
@@ -349,66 +282,6 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
}
}
@Override
public void onResume() {
super.onResume();
final String mainPageContentKey = getString(R.string.main_page_content_key);
final Preference mainPagePref = findPreference(getString(R.string.main_page_content_key));
final String bpk = getString(R.string.blank_page_key);
if(defaultPreferences.getString(mainPageContentKey, bpk)
.equals(getString(R.string.channel_page_key))) {
mainPagePref.setSummary(defaultPreferences.getString(getString(R.string.main_page_selected_channel_name), "error"));
} else if(defaultPreferences.getString(mainPageContentKey, bpk)
.equals(getString(R.string.kiosk_page_key))) {
try {
StreamingService service = NewPipe.getService(
defaultPreferences.getInt(
getString(R.string.main_page_selected_service), 0));
String kioskName = KioskTranslator.getTranslatedKioskName(
defaultPreferences.getString(
getString(R.string.main_page_selectd_kiosk_id), "Trending"),
getContext());
String summary =
String.format(getString(R.string.service_kiosk_string),
service.getServiceInfo().getName(),
kioskName);
mainPagePref.setSummary(summary);
} catch (Exception e) {
onError(e);
}
}
}
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
private String getMainPagePrefSummery(final String mainPrefOldValue, final ListPreference mainPageContentPref) {
if(mainPrefOldValue.equals(getString(R.string.channel_page_key))) {
return defaultPreferences.getString(getString(R.string.main_page_selected_channel_name), "error");
} else {
return mainPageContentPref.getSummary().toString();
}
}
private int getMainPageSummeryByKey(final String key) {
if(key.equals(getString(R.string.blank_page_key))) {
return R.string.blank_page_summary;
} else if(key.equals(getString(R.string.kiosk_page_key))) {
return R.string.kiosk_page_summary;
} else if(key.equals(getString(R.string.feed_page_key))) {
return R.string.feed_page_summary;
} else if(key.equals(getString(R.string.subscription_page_key))) {
return R.string.subscription_page_summary;
} else if(key.equals(getString(R.string.channel_page_key))) {
return R.string.channel_page_summary;
}
return R.string.blank_page_summary;
}
/*//////////////////////////////////////////////////////////////////////////
// Error
//////////////////////////////////////////////////////////////////////////*/

View File

@@ -71,7 +71,7 @@ public class NewPipeSettings {
}
public static File getVideoDownloadFolder(Context context) {
return getFolder(context, R.string.download_path_key, Environment.DIRECTORY_MOVIES);
return getDir(context, R.string.download_path_key, Environment.DIRECTORY_MOVIES);
}
public static String getVideoDownloadPath(Context context) {
@@ -81,7 +81,7 @@ public class NewPipeSettings {
}
public static File getAudioDownloadFolder(Context context) {
return getFolder(context, R.string.download_path_audio_key, Environment.DIRECTORY_MUSIC);
return getDir(context, R.string.download_path_audio_key, Environment.DIRECTORY_MUSIC);
}
public static String getAudioDownloadPath(Context context) {
@@ -90,21 +90,37 @@ public class NewPipeSettings {
return prefs.getString(key, Environment.DIRECTORY_MUSIC);
}
private static File getFolder(Context context, int keyID, String defaultDirectoryName) {
private static File getDir(Context context, int keyID, String defaultDirectoryName) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
final String key = context.getString(keyID);
String downloadPath = prefs.getString(key, null);
if ((downloadPath != null) && (!downloadPath.isEmpty())) return new File(downloadPath.trim());
final File folder = getFolder(defaultDirectoryName);
final File dir = getDir(defaultDirectoryName);
SharedPreferences.Editor spEditor = prefs.edit();
spEditor.putString(key, new File(folder, "NewPipe").getAbsolutePath());
spEditor.putString(key, getNewPipeChildFolderPathForDir(dir));
spEditor.apply();
return folder;
return dir;
}
@NonNull
private static File getFolder(String defaultDirectoryName) {
private static File getDir(String defaultDirectoryName) {
return new File(Environment.getExternalStorageDirectory(), defaultDirectoryName);
}
public static void resetDownloadFolders(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
resetDownloadFolder(prefs, context.getString(R.string.download_path_audio_key), Environment.DIRECTORY_MUSIC);
resetDownloadFolder(prefs, context.getString(R.string.download_path_key), Environment.DIRECTORY_MOVIES);
}
private static void resetDownloadFolder(SharedPreferences prefs, String key, String defaultDirectoryName) {
SharedPreferences.Editor spEditor = prefs.edit();
spEditor.putString(key, getNewPipeChildFolderPathForDir(getDir(defaultDirectoryName)));
spEditor.apply();
}
private static String getNewPipeChildFolderPathForDir(File dir) {
return new File(dir, "NewPipe").getAbsolutePath();
}
}

View File

@@ -66,7 +66,7 @@ public class SelectChannelFragment extends DialogFragment {
//////////////////////////////////////////////////////////////////////////*/
public interface OnSelectedLisener {
void onChannelSelected(String url, String name, int service);
void onChannelSelected(int serviceId, String url, String name);
}
OnSelectedLisener onSelectedLisener = null;
public void setOnSelectedLisener(OnSelectedLisener listener) {
@@ -126,7 +126,7 @@ public class SelectChannelFragment extends DialogFragment {
private void clickedItem(int position) {
if(onSelectedLisener != null) {
SubscriptionEntity entry = subscriptions.get(position);
onSelectedLisener.onChannelSelected(entry.getUrl(), entry.getName(), entry.getServiceId());
onSelectedLisener.onChannelSelected(entry.getServiceId(), entry.getUrl(), entry.getName());
}
dismiss();
}

View File

@@ -56,7 +56,7 @@ public class SelectKioskFragment extends DialogFragment {
//////////////////////////////////////////////////////////////////////////*/
public interface OnSelectedLisener {
void onKioskSelected(String kioskId, int service_id);
void onKioskSelected(int serviceId, String kioskId, String kioskName);
}
OnSelectedLisener onSelectedLisener = null;
@@ -101,7 +101,7 @@ public class SelectKioskFragment extends DialogFragment {
private void clickedItem(SelectKioskAdapter.Entry entry) {
if(onSelectedLisener != null) {
onSelectedLisener.onKioskSelected(entry.kioskId, entry.serviceId);
onSelectedLisener.onKioskSelected(entry.serviceId, entry.kioskId, entry.kioskName);
}
dismiss();
}

View File

@@ -77,7 +77,8 @@ public class SettingsActivity extends AppCompatActivity implements BasePreferenc
finish();
} else getSupportFragmentManager().popBackStack();
}
return true;
return super.onOptionsItemSelected(item);
}
@Override

View File

@@ -0,0 +1,94 @@
package org.schabi.newpipe.settings.tabs;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.support.v7.widget.AppCompatImageView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import org.schabi.newpipe.R;
import org.schabi.newpipe.util.ThemeHelper;
public class AddTabDialog {
private final AlertDialog dialog;
AddTabDialog(@NonNull final Context context,
@NonNull final ChooseTabListItem[] items,
@NonNull final DialogInterface.OnClickListener actions) {
dialog = new AlertDialog.Builder(context)
.setTitle(context.getString(R.string.tab_choose))
.setAdapter(new DialogListAdapter(context, items), actions)
.create();
}
public void show() {
dialog.show();
}
public static final class ChooseTabListItem {
final int tabId;
final String itemName;
@DrawableRes final int itemIcon;
ChooseTabListItem(Context context, Tab tab) {
this(tab.getTabId(), tab.getTabName(context), tab.getTabIconRes(context));
}
ChooseTabListItem(int tabId, String itemName, @DrawableRes int itemIcon) {
this.tabId = tabId;
this.itemName = itemName;
this.itemIcon = itemIcon;
}
}
private static class DialogListAdapter extends BaseAdapter {
private final LayoutInflater inflater;
private final ChooseTabListItem[] items;
@DrawableRes private final int fallbackIcon;
private DialogListAdapter(Context context, ChooseTabListItem[] items) {
this.inflater = LayoutInflater.from(context);
this.items = items;
this.fallbackIcon = ThemeHelper.resolveResourceIdFromAttr(context, R.attr.ic_hot);
}
@Override
public int getCount() {
return items.length;
}
@Override
public ChooseTabListItem getItem(int position) {
return items[position];
}
@Override
public long getItemId(int position) {
return getItem(position).tabId;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = inflater.inflate(R.layout.list_choose_tabs_dialog, parent, false);
}
final ChooseTabListItem item = getItem(position);
final AppCompatImageView tabIconView = convertView.findViewById(R.id.tabIcon);
final TextView tabNameView = convertView.findViewById(R.id.tabName);
tabIconView.setImageResource(item.itemIcon > 0 ? item.itemIcon : fallbackIcon);
tabNameView.setText(item.itemName);
return convertView;
}
}
}

View File

@@ -0,0 +1,386 @@
package org.schabi.newpipe.settings.tabs;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.Fragment;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.content.res.AppCompatResources;
import android.support.v7.widget.AppCompatImageView;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.settings.SelectChannelFragment;
import org.schabi.newpipe.settings.SelectKioskFragment;
import org.schabi.newpipe.settings.tabs.AddTabDialog.ChooseTabListItem;
import org.schabi.newpipe.util.ThemeHelper;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.schabi.newpipe.settings.tabs.Tab.typeFrom;
public class ChooseTabsFragment extends Fragment {
private TabsManager tabsManager;
private List<Tab> tabList = new ArrayList<>();
public ChooseTabsFragment.SelectedTabsAdapter selectedTabsAdapter;
/*//////////////////////////////////////////////////////////////////////////
// Lifecycle
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
tabsManager = TabsManager.getManager(requireContext());
updateTabList();
setHasOptionsMenu(true);
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_choose_tabs, container, false);
}
@Override
public void onViewCreated(@NonNull View rootView, @Nullable Bundle savedInstanceState) {
super.onViewCreated(rootView, savedInstanceState);
initButton(rootView);
RecyclerView listSelectedTabs = rootView.findViewById(R.id.selectedTabs);
listSelectedTabs.setLayoutManager(new LinearLayoutManager(requireContext()));
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(getItemTouchCallback());
itemTouchHelper.attachToRecyclerView(listSelectedTabs);
selectedTabsAdapter = new SelectedTabsAdapter(requireContext(), itemTouchHelper);
listSelectedTabs.setAdapter(selectedTabsAdapter);
}
@Override
public void onResume() {
super.onResume();
updateTitle();
}
@Override
public void onPause() {
super.onPause();
saveChanges();
}
/*//////////////////////////////////////////////////////////////////////////
// Menu
//////////////////////////////////////////////////////////////////////////*/
private final int MENU_ITEM_RESTORE_ID = 123456;
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
final MenuItem restoreItem = menu.add(Menu.NONE, MENU_ITEM_RESTORE_ID, Menu.NONE, R.string.restore_defaults);
restoreItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
final int restoreIcon = ThemeHelper.resolveResourceIdFromAttr(requireContext(), R.attr.ic_restore_defaults);
restoreItem.setIcon(AppCompatResources.getDrawable(requireContext(), restoreIcon));
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == MENU_ITEM_RESTORE_ID) {
restoreDefaults();
return true;
}
return super.onOptionsItemSelected(item);
}
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
private void updateTabList() {
tabList.clear();
tabList.addAll(tabsManager.getTabs());
}
private void updateTitle() {
if (getActivity() instanceof AppCompatActivity) {
ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
if (actionBar != null) actionBar.setTitle(R.string.main_page_content);
}
}
private void saveChanges() {
tabsManager.saveTabs(tabList);
}
private void restoreDefaults() {
new AlertDialog.Builder(requireContext(), ThemeHelper.getDialogTheme(requireContext()))
.setTitle(R.string.restore_defaults)
.setMessage(R.string.restore_defaults_confirmation)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.yes, (dialog, which) -> {
tabsManager.resetTabs();
updateTabList();
selectedTabsAdapter.notifyDataSetChanged();
})
.show();
}
private void initButton(View rootView) {
final FloatingActionButton fab = rootView.findViewById(R.id.addTabsButton);
fab.setOnClickListener(v -> {
final ChooseTabListItem[] availableTabs = getAvailableTabs(requireContext());
if (availableTabs.length == 0) {
//Toast.makeText(requireContext(), "No available tabs", Toast.LENGTH_SHORT).show();
return;
}
Dialog.OnClickListener actionListener = (dialog, which) -> {
final ChooseTabListItem selected = availableTabs[which];
addTab(selected.tabId);
};
new AddTabDialog(requireContext(), availableTabs, actionListener)
.show();
});
}
private void addTab(final Tab tab) {
tabList.add(tab);
selectedTabsAdapter.notifyDataSetChanged();
}
private void addTab(int tabId) {
final Tab.Type type = typeFrom(tabId);
if (type == null) {
ErrorActivity.reportError(requireContext(), new IllegalStateException("Tab id not found: " + tabId), null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none", "Choosing tabs on settings", 0));
return;
}
switch (type) {
case KIOSK: {
SelectKioskFragment selectFragment = new SelectKioskFragment();
selectFragment.setOnSelectedLisener((serviceId, kioskId, kioskName) ->
addTab(new Tab.KioskTab(serviceId, kioskId)));
selectFragment.show(requireFragmentManager(), "select_kiosk");
return;
}
case CHANNEL: {
SelectChannelFragment selectFragment = new SelectChannelFragment();
selectFragment.setOnSelectedLisener((serviceId, url, name) ->
addTab(new Tab.ChannelTab(serviceId, url, name)));
selectFragment.show(requireFragmentManager(), "select_channel");
return;
}
default:
addTab(type.getTab());
break;
}
}
public ChooseTabListItem[] getAvailableTabs(Context context) {
final ArrayList<ChooseTabListItem> returnList = new ArrayList<>();
for (Tab.Type type : Tab.Type.values()) {
final Tab tab = type.getTab();
switch (type) {
case BLANK:
if (!tabList.contains(tab)) {
returnList.add(new ChooseTabListItem(tab.getTabId(), getString(R.string.blank_page_summary),
tab.getTabIconRes(context)));
}
break;
case KIOSK:
returnList.add(new ChooseTabListItem(tab.getTabId(), getString(R.string.kiosk_page_summary),
ThemeHelper.resolveResourceIdFromAttr(context, R.attr.ic_hot)));
break;
case CHANNEL:
returnList.add(new ChooseTabListItem(tab.getTabId(), getString(R.string.channel_page_summary),
tab.getTabIconRes(context)));
break;
default:
if (!tabList.contains(tab)) {
returnList.add(new ChooseTabListItem(context, tab));
}
break;
}
}
return returnList.toArray(new ChooseTabListItem[0]);
}
/*//////////////////////////////////////////////////////////////////////////
// List Handling
//////////////////////////////////////////////////////////////////////////*/
private class SelectedTabsAdapter extends RecyclerView.Adapter<ChooseTabsFragment.SelectedTabsAdapter.TabViewHolder> {
private ItemTouchHelper itemTouchHelper;
private final LayoutInflater inflater;
SelectedTabsAdapter(Context context, ItemTouchHelper itemTouchHelper) {
this.itemTouchHelper = itemTouchHelper;
this.inflater = LayoutInflater.from(context);
}
public void swapItems(int fromPosition, int toPosition) {
Collections.swap(tabList, fromPosition, toPosition);
notifyItemMoved(fromPosition, toPosition);
}
@NonNull
@Override
public ChooseTabsFragment.SelectedTabsAdapter.TabViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.list_choose_tabs, parent, false);
return new ChooseTabsFragment.SelectedTabsAdapter.TabViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ChooseTabsFragment.SelectedTabsAdapter.TabViewHolder holder, int position) {
holder.bind(position, holder);
}
@Override
public int getItemCount() {
return tabList.size();
}
class TabViewHolder extends RecyclerView.ViewHolder {
private AppCompatImageView tabIconView;
private TextView tabNameView;
private ImageView handle;
TabViewHolder(View itemView) {
super(itemView);
tabNameView = itemView.findViewById(R.id.tabName);
tabIconView = itemView.findViewById(R.id.tabIcon);
handle = itemView.findViewById(R.id.handle);
}
@SuppressLint("ClickableViewAccessibility")
void bind(int position, TabViewHolder holder) {
handle.setOnTouchListener(getOnTouchListener(holder));
final Tab tab = tabList.get(position);
final Tab.Type type = Tab.typeFrom(tab.getTabId());
if (type == null) {
return;
}
String tabName = tab.getTabName(requireContext());
switch (type) {
case BLANK:
tabName = requireContext().getString(R.string.blank_page_summary);
break;
case KIOSK:
tabName = NewPipe.getNameOfService(((Tab.KioskTab) tab).getKioskServiceId()) + "/" + tabName;
break;
case CHANNEL:
tabName = NewPipe.getNameOfService(((Tab.ChannelTab) tab).getChannelServiceId()) + "/" + tabName;
break;
}
tabNameView.setText(tabName);
tabIconView.setImageResource(tab.getTabIconRes(requireContext()));
}
@SuppressLint("ClickableViewAccessibility")
private View.OnTouchListener getOnTouchListener(final RecyclerView.ViewHolder item) {
return (view, motionEvent) -> {
if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
if (itemTouchHelper != null && getItemCount() > 1) {
itemTouchHelper.startDrag(item);
return true;
}
}
return false;
};
}
}
}
private ItemTouchHelper.SimpleCallback getItemTouchCallback() {
return new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN,
ItemTouchHelper.START | ItemTouchHelper.END) {
@Override
public int interpolateOutOfBoundsScroll(RecyclerView recyclerView, int viewSize,
int viewSizeOutOfBounds, int totalSize,
long msSinceStartScroll) {
final int standardSpeed = super.interpolateOutOfBoundsScroll(recyclerView, viewSize,
viewSizeOutOfBounds, totalSize, msSinceStartScroll);
final int minimumAbsVelocity = Math.max(12,
Math.abs(standardSpeed));
return minimumAbsVelocity * (int) Math.signum(viewSizeOutOfBounds);
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source,
RecyclerView.ViewHolder target) {
if (source.getItemViewType() != target.getItemViewType() ||
selectedTabsAdapter == null) {
return false;
}
final int sourceIndex = source.getAdapterPosition();
final int targetIndex = target.getAdapterPosition();
selectedTabsAdapter.swapItems(sourceIndex, targetIndex);
return true;
}
@Override
public boolean isLongPressDragEnabled() {
return false;
}
@Override
public boolean isItemViewSwipeEnabled() {
return true;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
int position = viewHolder.getAdapterPosition();
tabList.remove(position);
selectedTabsAdapter.notifyItemRemoved(position);
if (tabList.isEmpty()) {
tabList.add(Tab.Type.BLANK.getTab());
selectedTabsAdapter.notifyItemInserted(0);
}
}
};
}
}

View File

@@ -0,0 +1,416 @@
package org.schabi.newpipe.settings.tabs;
import android.content.Context;
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import com.grack.nanojson.JsonObject;
import com.grack.nanojson.JsonSink;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.fragments.BlankFragment;
import org.schabi.newpipe.fragments.list.channel.ChannelFragment;
import org.schabi.newpipe.fragments.list.kiosk.KioskFragment;
import org.schabi.newpipe.local.bookmark.BookmarkFragment;
import org.schabi.newpipe.local.feed.FeedFragment;
import org.schabi.newpipe.local.history.StatisticsPlaylistFragment;
import org.schabi.newpipe.local.subscription.SubscriptionFragment;
import org.schabi.newpipe.util.KioskTranslator;
import org.schabi.newpipe.util.ThemeHelper;
public abstract class Tab {
Tab() {
}
Tab(@NonNull JsonObject jsonObject) {
readDataFromJson(jsonObject);
}
public abstract int getTabId();
public abstract String getTabName(Context context);
@DrawableRes public abstract int getTabIconRes(Context context);
/**
* Return a instance of the fragment that this tab represent.
*/
public abstract Fragment getFragment() throws ExtractionException;
@Override
public boolean equals(Object obj) {
return obj instanceof Tab && obj.getClass().equals(this.getClass())
&& ((Tab) obj).getTabId() == this.getTabId();
}
/*//////////////////////////////////////////////////////////////////////////
// JSON Handling
//////////////////////////////////////////////////////////////////////////*/
private static final String JSON_TAB_ID_KEY = "tab_id";
public void writeJsonOn(JsonSink jsonSink) {
jsonSink.object();
jsonSink.value(JSON_TAB_ID_KEY, getTabId());
writeDataToJson(jsonSink);
jsonSink.end();
}
protected void writeDataToJson(JsonSink writerSink) {
// No-op
}
protected void readDataFromJson(JsonObject jsonObject) {
// No-op
}
/*//////////////////////////////////////////////////////////////////////////
// Tab Handling
//////////////////////////////////////////////////////////////////////////*/
@Nullable
public static Tab from(@NonNull JsonObject jsonObject) {
final int tabId = jsonObject.getInt(Tab.JSON_TAB_ID_KEY, -1);
if (tabId == -1) {
return null;
}
return from(tabId, jsonObject);
}
@Nullable
public static Tab from(final int tabId) {
return from(tabId, null);
}
@Nullable
public static Type typeFrom(int tabId) {
for (Type available : Type.values()) {
if (available.getTabId() == tabId) {
return available;
}
}
return null;
}
@Nullable
private static Tab from(final int tabId, @Nullable JsonObject jsonObject) {
final Type type = typeFrom(tabId);
if (type == null) {
return null;
}
if (jsonObject != null) {
switch (type) {
case KIOSK:
return new KioskTab(jsonObject);
case CHANNEL:
return new ChannelTab(jsonObject);
}
}
return type.getTab();
}
/*//////////////////////////////////////////////////////////////////////////
// Implementations
//////////////////////////////////////////////////////////////////////////*/
public enum Type {
BLANK(new BlankTab()),
SUBSCRIPTIONS(new SubscriptionsTab()),
FEED(new FeedTab()),
BOOKMARKS(new BookmarksTab()),
HISTORY(new HistoryTab()),
KIOSK(new KioskTab()),
CHANNEL(new ChannelTab());
private Tab tab;
Type(Tab tab) {
this.tab = tab;
}
public int getTabId() {
return tab.getTabId();
}
public Tab getTab() {
return tab;
}
}
public static class BlankTab extends Tab {
public static final int ID = 0;
@Override
public int getTabId() {
return ID;
}
@Override
public String getTabName(Context context) {
return "NewPipe"; //context.getString(R.string.blank_page_summary);
}
@DrawableRes
@Override
public int getTabIconRes(Context context) {
return ThemeHelper.resolveResourceIdFromAttr(context, R.attr.ic_blank_page);
}
@Override
public BlankFragment getFragment() {
return new BlankFragment();
}
}
public static class SubscriptionsTab extends Tab {
public static final int ID = 1;
@Override
public int getTabId() {
return ID;
}
@Override
public String getTabName(Context context) {
return context.getString(R.string.tab_subscriptions);
}
@DrawableRes
@Override
public int getTabIconRes(Context context) {
return ThemeHelper.resolveResourceIdFromAttr(context, R.attr.ic_channel);
}
@Override
public SubscriptionFragment getFragment() {
return new SubscriptionFragment();
}
}
public static class FeedTab extends Tab {
public static final int ID = 2;
@Override
public int getTabId() {
return ID;
}
@Override
public String getTabName(Context context) {
return context.getString(R.string.fragment_whats_new);
}
@DrawableRes
@Override
public int getTabIconRes(Context context) {
return ThemeHelper.resolveResourceIdFromAttr(context, R.attr.rss);
}
@Override
public FeedFragment getFragment() {
return new FeedFragment();
}
}
public static class BookmarksTab extends Tab {
public static final int ID = 3;
@Override
public int getTabId() {
return ID;
}
@Override
public String getTabName(Context context) {
return context.getString(R.string.tab_bookmarks);
}
@DrawableRes
@Override
public int getTabIconRes(Context context) {
return ThemeHelper.resolveResourceIdFromAttr(context, R.attr.ic_bookmark);
}
@Override
public BookmarkFragment getFragment() {
return new BookmarkFragment();
}
}
public static class HistoryTab extends Tab {
public static final int ID = 4;
@Override
public int getTabId() {
return ID;
}
@Override
public String getTabName(Context context) {
return context.getString(R.string.title_activity_history);
}
@DrawableRes
@Override
public int getTabIconRes(Context context) {
return ThemeHelper.resolveResourceIdFromAttr(context, R.attr.history);
}
@Override
public StatisticsPlaylistFragment getFragment() {
return new StatisticsPlaylistFragment();
}
}
public static class KioskTab extends Tab {
public static final int ID = 5;
private int kioskServiceId;
private String kioskId;
private static final String JSON_KIOSK_SERVICE_ID_KEY = "service_id";
private static final String JSON_KIOSK_ID_KEY = "kiosk_id";
private KioskTab() {
this(-1, "<no-id>");
}
public KioskTab(int kioskServiceId, String kioskId) {
this.kioskServiceId = kioskServiceId;
this.kioskId = kioskId;
}
public KioskTab(JsonObject jsonObject) {
super(jsonObject);
}
@Override
public int getTabId() {
return ID;
}
@Override
public String getTabName(Context context) {
return KioskTranslator.getTranslatedKioskName(kioskId, context);
}
@DrawableRes
@Override
public int getTabIconRes(Context context) {
final int kioskIcon = KioskTranslator.getKioskIcons(kioskId, context);
if (kioskIcon <= 0) {
throw new IllegalStateException("Kiosk ID is not valid: \"" + kioskId + "\"");
}
return kioskIcon;
}
@Override
public KioskFragment getFragment() throws ExtractionException {
return KioskFragment.getInstance(kioskServiceId, kioskId);
}
@Override
protected void writeDataToJson(JsonSink writerSink) {
writerSink.value(JSON_KIOSK_SERVICE_ID_KEY, kioskServiceId)
.value(JSON_KIOSK_ID_KEY, kioskId);
}
@Override
protected void readDataFromJson(JsonObject jsonObject) {
kioskServiceId = jsonObject.getInt(JSON_KIOSK_SERVICE_ID_KEY, -1);
kioskId = jsonObject.getString(JSON_KIOSK_ID_KEY, "<no-id>");
}
public int getKioskServiceId() {
return kioskServiceId;
}
public String getKioskId() {
return kioskId;
}
}
public static class ChannelTab extends Tab {
public static final int ID = 6;
private int channelServiceId;
private String channelUrl;
private String channelName;
private static final String JSON_CHANNEL_SERVICE_ID_KEY = "channel_service_id";
private static final String JSON_CHANNEL_URL_KEY = "channel_url";
private static final String JSON_CHANNEL_NAME_KEY = "channel_name";
private ChannelTab() {
this(-1, "<no-url>", "<no-name>");
}
public ChannelTab(int channelServiceId, String channelUrl, String channelName) {
this.channelServiceId = channelServiceId;
this.channelUrl = channelUrl;
this.channelName = channelName;
}
public ChannelTab(JsonObject jsonObject) {
super(jsonObject);
}
@Override
public int getTabId() {
return ID;
}
@Override
public String getTabName(Context context) {
return channelName;
}
@DrawableRes
@Override
public int getTabIconRes(Context context) {
return ThemeHelper.resolveResourceIdFromAttr(context, R.attr.ic_channel);
}
@Override
public ChannelFragment getFragment() {
return ChannelFragment.getInstance(channelServiceId, channelUrl, channelName);
}
@Override
protected void writeDataToJson(JsonSink writerSink) {
writerSink.value(JSON_CHANNEL_SERVICE_ID_KEY, channelServiceId)
.value(JSON_CHANNEL_URL_KEY, channelUrl)
.value(JSON_CHANNEL_NAME_KEY, channelName);
}
@Override
protected void readDataFromJson(JsonObject jsonObject) {
channelServiceId = jsonObject.getInt(JSON_CHANNEL_SERVICE_ID_KEY, -1);
channelUrl = jsonObject.getString(JSON_CHANNEL_URL_KEY, "<no-url>");
channelName = jsonObject.getString(JSON_CHANNEL_NAME_KEY, "<no-name>");
}
public int getChannelServiceId() {
return channelServiceId;
}
public String getChannelUrl() {
return channelUrl;
}
public String getChannelName() {
return channelName;
}
}
}

View File

@@ -0,0 +1,114 @@
package org.schabi.newpipe.settings.tabs;
import android.support.annotation.Nullable;
import com.grack.nanojson.JsonArray;
import com.grack.nanojson.JsonObject;
import com.grack.nanojson.JsonParser;
import com.grack.nanojson.JsonParserException;
import com.grack.nanojson.JsonStringWriter;
import com.grack.nanojson.JsonWriter;
import org.schabi.newpipe.settings.tabs.Tab.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.schabi.newpipe.extractor.ServiceList.YouTube;
/**
* Class to get a JSON representation of a list of tabs, and the other way around.
*/
public class TabsJsonHelper {
private static final String JSON_TABS_ARRAY_KEY = "tabs";
protected static final List<Tab> FALLBACK_INITIAL_TABS_LIST = Collections.unmodifiableList(Arrays.asList(
new Tab.KioskTab(YouTube.getServiceId(), "Trending"),
Type.SUBSCRIPTIONS.getTab(),
Type.BOOKMARKS.getTab()
));
public static class InvalidJsonException extends Exception {
private InvalidJsonException() {
super();
}
private InvalidJsonException(String message) {
super(message);
}
private InvalidJsonException(Throwable cause) {
super(cause);
}
}
/**
* Try to reads the passed JSON and returns the list of tabs if no error were encountered.
* <p>
* If the JSON is null or empty, or the list of tabs that it represents is empty, the
* {@link #FALLBACK_INITIAL_TABS_LIST fallback list} will be returned.
* <p>
* Tabs with invalid ids (i.e. not in the {@link Tab.Type} enum) will be ignored.
*
* @param tabsJson a JSON string got from {@link #getJsonToSave(List)}.
* @return a list of {@link Tab tabs}.
* @throws InvalidJsonException if the JSON string is not valid
*/
public static List<Tab> getTabsFromJson(@Nullable String tabsJson) throws InvalidJsonException {
if (tabsJson == null || tabsJson.isEmpty()) {
return FALLBACK_INITIAL_TABS_LIST;
}
final List<Tab> returnTabs = new ArrayList<>();
final JsonObject outerJsonObject;
try {
outerJsonObject = JsonParser.object().from(tabsJson);
final JsonArray tabsArray = outerJsonObject.getArray(JSON_TABS_ARRAY_KEY);
if (tabsArray == null) {
throw new InvalidJsonException("JSON doesn't contain \"" + JSON_TABS_ARRAY_KEY + "\" array");
}
for (Object o : tabsArray) {
if (!(o instanceof JsonObject)) continue;
final Tab tab = Tab.from((JsonObject) o);
if (tab != null) {
returnTabs.add(tab);
}
}
} catch (JsonParserException e) {
throw new InvalidJsonException(e);
}
if (returnTabs.isEmpty()) {
return FALLBACK_INITIAL_TABS_LIST;
}
return returnTabs;
}
/**
* Get a JSON representation from a list of tabs.
*
* @param tabList a list of {@link Tab tabs}.
* @return a JSON string representing the list of tabs
*/
public static String getJsonToSave(@Nullable List<Tab> tabList) {
final JsonStringWriter jsonWriter = JsonWriter.string();
jsonWriter.object();
jsonWriter.array(JSON_TABS_ARRAY_KEY);
if (tabList != null) for (Tab tab : tabList) {
tab.writeJsonOn(jsonWriter);
}
jsonWriter.end();
jsonWriter.end();
return jsonWriter.done();
}
}

View File

@@ -0,0 +1,93 @@
package org.schabi.newpipe.settings.tabs;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.widget.Toast;
import org.schabi.newpipe.R;
import java.util.List;
public class TabsManager {
private final SharedPreferences sharedPreferences;
private final String savedTabsKey;
private final Context context;
public static TabsManager getManager(Context context) {
return new TabsManager(context);
}
private TabsManager(Context context) {
this.context = context;
this.sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
this.savedTabsKey = context.getString(R.string.saved_tabs_key);
}
public List<Tab> getTabs() {
final String savedJson = sharedPreferences.getString(savedTabsKey, null);
try {
return TabsJsonHelper.getTabsFromJson(savedJson);
} catch (TabsJsonHelper.InvalidJsonException e) {
Toast.makeText(context, R.string.saved_tabs_invalid_json, Toast.LENGTH_SHORT).show();
return getDefaultTabs();
}
}
public void saveTabs(List<Tab> tabList) {
final String jsonToSave = TabsJsonHelper.getJsonToSave(tabList);
sharedPreferences.edit().putString(savedTabsKey, jsonToSave).apply();
}
public void resetTabs() {
sharedPreferences.edit().remove(savedTabsKey).apply();
}
public List<Tab> getDefaultTabs() {
return TabsJsonHelper.FALLBACK_INITIAL_TABS_LIST;
}
/*//////////////////////////////////////////////////////////////////////////
// Listener
//////////////////////////////////////////////////////////////////////////*/
public interface SavedTabsChangeListener {
void onTabsChanged();
}
private SavedTabsChangeListener savedTabsChangeListener;
private SharedPreferences.OnSharedPreferenceChangeListener preferenceChangeListener;
public void setSavedTabsListener(SavedTabsChangeListener listener) {
if (preferenceChangeListener != null) {
sharedPreferences.unregisterOnSharedPreferenceChangeListener(preferenceChangeListener);
}
savedTabsChangeListener = listener;
preferenceChangeListener = getPreferenceChangeListener();
sharedPreferences.registerOnSharedPreferenceChangeListener(preferenceChangeListener);
}
public void unsetSavedTabsListener() {
if (preferenceChangeListener != null) {
sharedPreferences.unregisterOnSharedPreferenceChangeListener(preferenceChangeListener);
}
preferenceChangeListener = null;
savedTabsChangeListener = null;
}
private SharedPreferences.OnSharedPreferenceChangeListener getPreferenceChangeListener() {
return (sharedPreferences, key) -> {
if (key.equals(savedTabsKey)) {
if (savedTabsChangeListener != null) savedTabsChangeListener.onTabsChanged();
}
};
}
}

View File

@@ -73,7 +73,7 @@ public final class ExtractorHelper {
return Single.fromCallable(() ->
SearchInfo.getInfo(NewPipe.getService(serviceId),
NewPipe.getService(serviceId)
.getSearchQIHFactory()
.getSearchQHFactory()
.fromQuery(searchString, contentFilter, sortFilter),
contentCountry));
}
@@ -88,7 +88,7 @@ public final class ExtractorHelper {
return Single.fromCallable(() ->
SearchInfo.getMoreItems(NewPipe.getService(serviceId),
NewPipe.getService(serviceId)
.getSearchQIHFactory()
.getSearchQHFactory()
.fromQuery(searchString, contentFilter, sortFilter),
contentCountry,
pageUrl));

View File

@@ -24,7 +24,7 @@ import org.schabi.newpipe.R;
public class KioskTranslator {
public static String getTranslatedKioskName(String kioskId, Context c) {
switch(kioskId) {
switch (kioskId) {
case "Trending":
return c.getString(R.string.trending);
case "Top 50":
@@ -35,4 +35,17 @@ public class KioskTranslator {
return kioskId;
}
}
public static int getKioskIcons(String kioskId, Context c) {
switch(kioskId) {
case "Trending":
return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_hot);
case "Top 50":
return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_hot);
case "New & hot":
return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_hot);
default:
return 0;
}
}
}

View File

@@ -443,11 +443,11 @@ public final class ListHelper {
/**
* Are we connected to wifi?
* @param context App context
* @return True if connected to wifi
* @return {@code true} if connected to wifi
*/
private static boolean isWifiActive(Context context)
{
ConnectivityManager manager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
return manager.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI;
return manager != null && manager.getActiveNetworkInfo() != null && manager.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI;
}
}

View File

@@ -31,17 +31,17 @@ import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.Stream;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler;
import org.schabi.newpipe.fragments.MainFragment;
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
import org.schabi.newpipe.fragments.list.channel.ChannelFragment;
import org.schabi.newpipe.local.bookmark.BookmarkFragment;
import org.schabi.newpipe.local.feed.FeedFragment;
import org.schabi.newpipe.fragments.list.kiosk.KioskFragment;
import org.schabi.newpipe.fragments.list.playlist.PlaylistFragment;
import org.schabi.newpipe.fragments.list.search.SearchFragment;
import org.schabi.newpipe.local.history.StatisticsPlaylistFragment;
import org.schabi.newpipe.local.playlist.LocalPlaylistFragment;
import org.schabi.newpipe.local.subscription.SubscriptionFragment;
import org.schabi.newpipe.local.subscription.SubscriptionsImportFragment;
import org.schabi.newpipe.player.BackgroundPlayer;
import org.schabi.newpipe.player.BackgroundPlayerActivity;
@@ -349,6 +349,20 @@ public class NavigationHelper {
.commit();
}
public static void openBookmarksFragment(FragmentManager fragmentManager) {
defaultTransaction(fragmentManager)
.replace(R.id.fragment_holder, new BookmarkFragment())
.addToBackStack(null)
.commit();
}
public static void openSubscriptionFragment(FragmentManager fragmentManager) {
defaultTransaction(fragmentManager)
.replace(R.id.fragment_holder, new SubscriptionFragment())
.addToBackStack(null)
.commit();
}
public static void openKioskFragment(FragmentManager fragmentManager, int serviceId, String kioskId) throws ExtractionException {
defaultTransaction(fragmentManager)
.replace(R.id.fragment_holder, KioskFragment.getInstance(serviceId, kioskId))

View File

@@ -1,10 +1,17 @@
package us.shandian.giga.get;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import org.schabi.newpipe.download.ExtSDDownloadFailedActivity;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
@@ -23,7 +30,9 @@ public class DownloadManagerImpl implements DownloadManager {
private static final String TAG = DownloadManagerImpl.class.getSimpleName();
private final DownloadDataSource mDownloadDataSource;
private final ArrayList<DownloadMission> mMissions = new ArrayList<DownloadMission>();
private final ArrayList<DownloadMission> mMissions = new ArrayList<>();
@NonNull
private final Context context;
/**
* Create a new instance
@@ -33,6 +42,13 @@ public class DownloadManagerImpl implements DownloadManager {
*/
public DownloadManagerImpl(Collection<String> searchLocations, DownloadDataSource downloadDataSource) {
mDownloadDataSource = downloadDataSource;
this.context = null;
loadMissions(searchLocations);
}
public DownloadManagerImpl(Collection<String> searchLocations, DownloadDataSource downloadDataSource, Context context) {
mDownloadDataSource = downloadDataSource;
this.context = context;
loadMissions(searchLocations);
}
@@ -277,10 +293,12 @@ public class DownloadManagerImpl implements DownloadManager {
}
private class Initializer extends Thread {
private DownloadMission mission;
private final DownloadMission mission;
private final Handler handler;
public Initializer(DownloadMission mission) {
this.mission = mission;
this.handler = new Handler();
}
@Override
@@ -335,6 +353,13 @@ public class DownloadManagerImpl implements DownloadManager {
af.close();
mission.start();
} catch (IOException ie) {
if(context == null) throw new RuntimeException(ie);
if(ie.getMessage().contains("Permission denied")) {
handler.post(() ->
context.startActivity(new Intent(context, ExtSDDownloadFailedActivity.class)));
} else throw new RuntimeException(ie);
} catch (Exception e) {
// TODO Notify
throw new RuntimeException(e);

View File

@@ -81,7 +81,7 @@ public class DownloadManagerService extends Service {
ArrayList<String> paths = new ArrayList<>(2);
paths.add(NewPipeSettings.getVideoDownloadPath(this));
paths.add(NewPipeSettings.getAudioDownloadPath(this));
mManager = new DownloadManagerImpl(paths, mDataSource);
mManager = new DownloadManagerImpl(paths, mDataSource, this);
if (DEBUG) {
Log.d(TAG, "mManager == null");
Log.d(TAG, "Download directory: " + paths);

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 363 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 470 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 458 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 611 B

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#64000000" />
</shape>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
</vector>

View File

@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M17,3L7,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2L19,5c0,-1.1 -0.9,-2 -2,-2zM17,19L7,19L7,5h10v14z"/>
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M17,3L7,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2L19,5c0,-1.1 -0.9,-2 -2,-2zM17,19L7,19L7,5h10v14z"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="72dp"
android:height="72dp"
android:tint="#FFFFFF"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M20,8.69L20,4h-4.69L12,0.69 8.69,4L4,4v4.69L0.69,12 4,15.31L4,20h4.69L12,23.31 15.31,20L20,20v-4.69L23.31,12 20,8.69zM12,18c-3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6 6,2.69 6,6 -2.69,6 -6,6zM12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4z" />
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="72dp"
android:height="72dp"
android:tint="#FFFFFF"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M20,15.31L23.31,12 20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69zM12,18c-3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6 6,2.69 6,6 -2.69,6 -6,6z" />
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="72dp"
android:height="72dp"
android:tint="#FFFFFF"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M20,15.31L23.31,12 20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69zM12,18V6c3.31,0 6,2.69 6,6s-2.69,6 -6,6z" />
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M14,12c0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2 0.9,2 2,2 2,-0.9 2,-2zM12,3c-4.97,0 -9,4.03 -9,9L0,12l4,4 4,-4L5,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.51,0 -2.91,-0.49 -4.06,-1.3l-1.42,1.44C8.04,20.3 9.94,21 12,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9z"/>
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M14,12c0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2 0.9,2 2,2 2,-0.9 2,-2zM12,3c-4.97,0 -9,4.03 -9,9L0,12l4,4 4,-4L5,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.51,0 -2.91,-0.49 -4.06,-1.3l-1.42,1.44C8.04,20.3 9.94,21 12,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9z"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="72dp"
android:height="72dp"
android:tint="#FFFFFF"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M18.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM5,9v6h4l5,5V4L9,9H5z" />
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="72dp"
android:height="72dp"
android:tint="#FFFFFF"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M7,9v6h4l5,5V4l-5,5H7z" />
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="72dp"
android:height="72dp"
android:tint="#FFFFFF"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v2.21l2.45,2.45c0.03,-0.2 0.05,-0.41 0.05,-0.63zM19,12c0,0.94 -0.2,1.82 -0.54,2.64l1.51,1.51C20.63,14.91 21,13.5 21,12c0,-4.28 -2.99,-7.86 -7,-8.77v2.06c2.89,0.86 5,3.54 5,6.71zM4.27,3L3,4.27 7.73,9L3,9v6h4l5,5v-6.73l4.25,4.25c-0.67,0.52 -1.42,0.93 -2.25,1.18v2.06c1.38,-0.31 2.63,-0.95 3.69,-1.81L19.73,21 21,19.73l-9,-9L4.27,3zM12,4L9.91,6.09 12,8.18L12,4z" />
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="72dp"
android:height="72dp"
android:tint="#FFFFFF"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z" />
</vector>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="-90"
android:toDegrees="-90">
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:innerRadiusRatio="2.25"
android:shape="ring"
android:thicknessRatio="17.75"
android:useLevel="true">
<solid android:color="@android:color/white" />
</shape>
</rotate>

View File

@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="150dp"
android:clickable="true"
android:focusable="true">
<Button
android:id="@+id/drawer_header_action_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/selectableItemBackground" />
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorPrimary"
android:scaleType="centerCrop"
android:src="@drawable/background_header" />
<ImageView
android:id="@+id/drawer_header_np_nude_view"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_marginLeft="30dp"
android:layout_marginStart="30dp"
android:layout_marginTop="30dp"
android:src="@drawable/np_logo_nude_shadow" />
<TextView
android:id="@+id/drawer_header_np_text_view"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_alignBottom="@id/drawer_header_np_nude_view"
android:layout_alignTop="@id/drawer_header_np_nude_view"
android:layout_toEndOf="@id/drawer_header_np_nude_view"
android:layout_toRightOf="@id/drawer_header_np_nude_view"
android:gravity="center"
android:text="@string/app_name"
android:textSize="30dp"
android:textColor="@color/drawer_header_font_color"
android:textStyle="bold|italic" />
<TextView
android:id="@+id/drawer_header_service_view"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_alignLeft="@id/drawer_header_np_text_view"
android:layout_alignStart="@id/drawer_header_np_text_view"
android:layout_below="@id/drawer_header_np_text_view"
android:text="YouTube"
android:textSize="18dp"
android:textColor="@color/drawer_header_font_color"
android:textStyle="italic" />
<ImageView
android:id="@+id/drawer_arrow"
android:layout_width="30dp"
android:layout_height="35dp"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_gravity="bottom"
android:layout_marginBottom="0dp"
android:paddingBottom="20dp"
android:paddingEnd="20dp"
android:paddingRight="20dp"
android:src="@drawable/ic_arrow_down_white" />
</RelativeLayout>

View File

@@ -4,7 +4,8 @@
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<FrameLayout
@@ -23,4 +24,4 @@
<include layout="@layout/drawer_layout"/>
</android.support.v4.widget.DrawerLayout>
</android.support.v4.widget.DrawerLayout>

View File

@@ -471,10 +471,11 @@
android:id="@+id/controlAnimationView"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@drawable/ic_action_av_fast_rewind"
android:background="@drawable/background_oval_black_transparent"
android:visibility="gone"
tools:ignore="ContentDescription"
tools:visibility="visible"/>
tools:src="@drawable/ic_action_av_fast_rewind"
tools:visibility="visible" />
</LinearLayout>
@@ -503,43 +504,57 @@
android:layout_toRightOf="@+id/loading_panel"
tools:ignore="RtlHardcoded">
<TextView
android:id="@+id/volumeTextView"
<RelativeLayout
android:id="@+id/volumeRelativeLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerInParent="true"
android:layout_marginLeft="20dp"
android:background="#64000000"
android:paddingBottom="10dp"
android:paddingLeft="30dp"
android:paddingRight="30dp"
android:paddingTop="10dp"
android:textColor="@android:color/white"
android:textSize="35sp"
android:background="@drawable/background_oval_black_transparent"
android:visibility="gone"
tools:ignore="RtlHardcoded"
tools:text="Volume 0"
tools:visibility="visible" />
tools:visibility="visible">
<TextView
android:id="@+id/brightnessTextView"
<ProgressBar
android:id="@+id/volumeProgressBar"
style="?android:progressBarStyleHorizontal"
android:layout_width="128dp"
android:layout_height="128dp"
android:indeterminate="false"
android:progressDrawable="@drawable/progress_circular_white" />
<ImageView
android:id="@+id/volumeImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
tools:ignore="ContentDescription"
tools:src="@drawable/ic_volume_up_white_72dp" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/brightnessRelativeLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerInParent="true"
android:layout_marginRight="20dp"
android:background="#64000000"
android:paddingBottom="10dp"
android:paddingLeft="30dp"
android:paddingRight="30dp"
android:paddingTop="10dp"
android:textColor="@android:color/white"
android:textSize="35sp"
android:background="@drawable/background_oval_black_transparent"
android:visibility="gone"
tools:ignore="RtlHardcoded"
tools:text="Brightness 0"
tools:visibility="visible" />
tools:visibility="visible">
<ProgressBar
android:id="@+id/brightnessProgressBar"
style="?android:progressBarStyleHorizontal"
android:layout_width="128dp"
android:layout_height="128dp"
android:indeterminate="false"
android:progressDrawable="@drawable/progress_circular_white" />
<ImageView
android:id="@+id/brightnessImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
tools:ignore="ContentDescription"
tools:src="@drawable/ic_brightness_high_white_72dp" />
</RelativeLayout>
<TextView
android:id="@+id/currentDisplaySeek"

View File

@@ -61,7 +61,7 @@
android:layout_below="@+id/channel_title_view"
android:ellipsize="end"
android:gravity="left|center"
android:lines="1"
android:maxLines="2"
android:textSize="@dimen/channel_subscribers_text_size"
android:visibility="gone"
tools:ignore="RtlHardcoded"

View File

@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="150dp"
android:clickable="true"
android:focusable="true">
<Button
android:id="@+id/drawer_header_action_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/selectableItemBackground"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorPrimary"
android:src="@drawable/background_header"
android:scaleType="centerCrop"/>
<ImageView
android:id="@+id/drawer_header_np_nude_view"
android:layout_marginLeft="30dp"
android:layout_marginStart="30dp"
android:layout_marginTop="20dp"
android:layout_width="70dp"
android:layout_height="70dp"
android:src="@drawable/np_logo_nude_shadow"/>
<TextView
android:id="@+id/drawer_header_np_text_view"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:text="@string/app_name"
android:layout_toRightOf="@id/drawer_header_np_nude_view"
android:layout_toEndOf="@id/drawer_header_np_nude_view"
android:layout_alignTop="@id/drawer_header_np_nude_view"
android:layout_alignBottom="@id/drawer_header_np_nude_view"
android:gravity="center"
android:textSize="30dp"
android:textColor="@color/drawer_header_font_color"
android:textStyle="bold|italic"/>
<TextView
android:id="@+id/drawer_header_service_view"
android:layout_width="100dp"
android:layout_height="100dp"
android:text="YouTube"
android:layout_below="@id/drawer_header_np_text_view"
android:layout_alignLeft="@id/drawer_header_np_text_view"
android:layout_alignStart="@id/drawer_header_np_text_view"
android:textSize="18dp"
android:textColor="@color/drawer_header_font_color"
android:textStyle="italic"/>
<ImageView
android:id="@+id/drawer_arrow"
android:layout_width="30dp"
android:layout_height="35dp"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_gravity="bottom"
android:layout_marginBottom="0dp"
android:paddingBottom="20dp"
android:paddingRight="20dp"
android:src="@drawable/ic_arrow_down_white"
android:paddingEnd="20dp" />
</RelativeLayout>

View File

@@ -1,81 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:id="@+id/navigation_layout"
<android.support.design.widget.NavigationView android:id="@+id/navigation_layout"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:layout_gravity="start"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="?attr/android:windowBackground"
android:clickable="true"
android:focusable="true">
<RelativeLayout
android:id="@+id/drawer_header"
android:layout_width="0dp"
android:layout_height="150dp"
android:layout_alignLeft="@id/navigation"
android:layout_alignRight="@id/navigation"
android:layout_alignStart="@id/navigation"
android:layout_alignEnd="@id/navigation"
android:clickable="true"
android:focusable="true">
<Button
android:id="@+id/drawer_header_action_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/selectableItemBackground"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorPrimary"
android:src="@drawable/background_header"
android:scaleType="centerCrop"/>
<ImageView
android:id="@+id/drawer_header_np_nude_view"
android:layout_marginLeft="30dp"
android:layout_marginStart="30dp"
android:layout_marginTop="20dp"
android:layout_width="70dp"
android:layout_height="70dp"
android:src="@drawable/np_logo_nude_shadow"/>
<TextView
android:id="@+id/drawer_header_np_text_view"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:text="@string/app_name"
android:layout_toRightOf="@id/drawer_header_np_nude_view"
android:layout_toEndOf="@id/drawer_header_np_nude_view"
android:layout_alignTop="@id/drawer_header_np_nude_view"
android:layout_alignBottom="@id/drawer_header_np_nude_view"
android:gravity="center"
android:textSize="30dp"
android:textStyle="bold|italic"/>
<TextView
android:id="@+id/drawer_header_service_view"
android:layout_width="100dp"
android:layout_height="100dp"
android:text="YouTube"
android:layout_below="@id/drawer_header_np_text_view"
android:layout_alignLeft="@id/drawer_header_np_text_view"
android:layout_alignStart="@id/drawer_header_np_text_view"
android:textSize="18dp"
android:textStyle="italic"/>
</RelativeLayout>
android:focusable="true"
>
<android.support.design.widget.NavigationView
android:id="@+id/navigation"
android:layout_below="@id/drawer_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:elevation="0dp"/>
app:elevation="0dp"
android:background="?attr/android:windowBackground"
app:headerLayout="@layout/drawer_header"/>
<!-- app:menu="@menu/drawer_items" -->
<LinearLayout
@@ -89,29 +30,6 @@
android:layout_alignEnd="@id/navigation"
android:layout_alignParentBottom="true">
<ImageButton
android:id="@+id/drawer_settings"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="?attr/settings"/>
<ImageButton
android:id="@+id/drawer_downloads"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="?attr/download" />
<ImageButton
android:id="@+id/drawer_history"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="?attr/history"/>
</LinearLayout>
</RelativeLayout>
</android.support.design.widget.NavigationView>

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="@+id/selectedTabs"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/list_choose_tabs"/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/addTabsButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginBottom="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:clickable="true"
android:focusable="true"
app:backgroundTint="?attr/colorPrimary"
app:fabSize="auto"
app:srcCompat="?attr/ic_add"/>
</RelativeLayout>

View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/layoutCard"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="3dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginTop="3dp"
android:minHeight="?listPreferredItemHeightSmall"
android:orientation="horizontal"
app:cardCornerRadius="5dp"
app:cardElevation="4dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical">
<android.support.v7.widget.AppCompatImageView
android:id="@+id/tabIcon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginLeft="16dp"
tools:ignore="ContentDescription,RtlHardcoded"
tools:src="?attr/ic_hot"/>
<TextView
android:id="@+id/tabName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginBottom="6dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="6dp"
android:layout_toLeftOf="@+id/handle"
android:layout_toRightOf="@+id/tabIcon"
android:ellipsize="end"
android:maxLines="2"
android:textAppearance="?textAppearanceListItem"
tools:ignore="RtlHardcoded"
tools:text="Lorem ipsum dolor sit amet"/>
<android.support.v7.widget.AppCompatImageView
android:id="@+id/handle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:paddingBottom="12dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="12dp"
android:src="?attr/drag_handle"
tools:ignore="ContentDescription,RtlHardcoded"/>
</RelativeLayout>
</android.support.v7.widget.CardView>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?listPreferredItemHeightSmall"
android:orientation="horizontal">
<android.support.v7.widget.AppCompatImageView
android:id="@+id/tabIcon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginLeft="?dialogPreferredPadding"
tools:ignore="ContentDescription,RtlHardcoded"
tools:src="?attr/ic_hot"/>
<TextView
android:id="@+id/tabName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="?dialogPreferredPadding"
android:layout_marginRight="?dialogPreferredPadding"
android:layout_toRightOf="@+id/tabIcon"
android:ellipsize="end"
android:maxLines="2"
android:textAppearance="?textAppearanceListItem"
tools:ignore="RtlHardcoded"
tools:text="Lorem ipsum dolor sit amet"/>
</RelativeLayout>

View File

@@ -9,7 +9,6 @@
tools:layout_height="84dp"
tools:layout_width="@dimen/popup_minimum_width">
<com.google.android.exoplayer2.ui.AspectRatioFrameLayout
android:id="@+id/aspectRatioLayout"
android:layout_width="match_parent"
@@ -290,4 +289,11 @@
android:visibility="gone"
tools:ignore="RtlHardcoded"
tools:visibility="gone"/>
<View
android:id="@+id/closingOverlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#AAFF0000"
android:visibility="gone"/>
</FrameLayout>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.FloatingActionButton
android:id="@+id/closeButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:layout_marginBottom="24dp"
android:src="@drawable/ic_close_white_24dp"
app:backgroundTint="@color/light_youtube_primary_color"
app:borderWidth="0dp"
app:fabSize="normal"/>
</FrameLayout>

View File

@@ -2,13 +2,11 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group
android:id="@+id/menu_services_group">
<item
android:id="@+id/menu_service_youtube"
android:icon="@drawable/place_holder_youtube"
android:title="@string/youtube"/>
<item
android:id="@+id/menu_service_soundcloud"
android:icon="@drawable/place_holder_circle"
android:title="@string/soundcloud"/>
</group>
<group
android:id="@+id/menu_tabs_group">
</group>
<group
android:id="@+id/menu_options_about_group">
</group>
</menu>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_show_downloads"
@@ -19,9 +19,4 @@
android:orderInCategory="990"
android:title="@string/settings"
app:showAsAction="never"/>
<item
android:id="@+id/action_about"
android:orderInCategory="1000"
android:title="@string/action_about"/>
</menu>

View File

@@ -25,7 +25,6 @@
<string name="kore_not_found">تطبيق Kore غير موجود. هل تريد تثبيته ؟</string>
<string name="light_theme_title">مضيء</string>
<string name="list_thumbnail_view_description">صور معاينة الفيديو</string>
<string name="m4a_description">جودة أفضل — M4A</string>
<string name="network_error">خطأ في الشبكة</string>
<string name="next_video_title">الفيديو التالي</string>
<string name="no_player_found">لا يوجد مشغل فيديو. هل تريد تثبيت VLC ؟</string>
@@ -51,12 +50,11 @@
<string name="use_external_audio_player_title">استخدام مشغل صوت خارجي</string>
<string name="use_external_video_player_title">استخدام مشغل فيديو خارجي</string>
<string name="use_tor_summary">(إختبارية) إجراء التنزيلات من خلال استخدام بروكسي Tor لزيادة الخصوصية ( تشغيل الفيديو المباشر غير مدعوم حتى الأن ).</string>
<string name="use_tor_title">استخدام Tor</string>
<string name="use_tor_title">استخدام تور</string>
<string name="view_count_text">مشاهدات %1$s</string>
<string name="webm_description">تنسيق حر — WebM</string>
<string name="blocked_by_gema">تم حجبه بواسطة GEMA</string>
<string name="content_not_available">المحتوى غير متاح</string>
<string name="could_not_load_thumbnails">لم يتمكن من تحميل كل صور المعاينة</string>
<string name="could_not_load_thumbnails">تعذرت عملية تحميل كافة صور المعاينة</string>
<string name="general_error">خطأ</string>
<string name="parsing_error">تعذرت عملية تحليل الموقع</string>
<string name="youtube_signature_decryption_error">تعذر فك تشفير توقيع رابط الفيديو</string>
@@ -138,11 +136,11 @@
<string name="unknown_content">[غير معروف]</string>
<string name="light_parsing_error">لايمكن تحليل الموقع بشكل كلي</string>
<string name="could_not_setup_download_menu">يتعذر إعداد قائمة التنزيل</string>
<string name="live_streams_not_supported">هذا هو بث مباشر ، وهو غير معتمد حتى الآن.</string>
<string name="could_not_get_stream">يتعذر الحصول على أي بث</string>
<string name="could_not_load_image">تعذر تحميل الصورة</string>
<string name="light_parsing_error">لا يمكن تحليل الموقع بشكل كلي</string>
<string name="could_not_setup_download_menu">تعذرت عملية إعداد قائمة التنزيل</string>
<string name="live_streams_not_supported">هذا بث مباشر، وهو غير معتمد الآن.</string>
<string name="could_not_get_stream">تعذر الحصول على أي بث</string>
<string name="could_not_load_image">تعذرت عملية تحميل الصورة</string>
<string name="app_ui_crash">تعطل التطبيق / واجهة المستخدم</string>
<string name="player_stream_failure">لا يمكن تشغيل هذا البث</string>
<string name="player_unrecoverable_failure">حدث خطأ للمشغل غير قابل للاسترداد</string>
@@ -156,7 +154,7 @@
<string name="what_happened_headline">ماذا حدث:</string>
<string name="info_labels">ماذا:\\nطلب:\\nيحتوى اللغة:\\nSالخدمات:\\nتوقيت غرينتش:\\nحزمة:\\nالإصدار:\\nOS إصدار نظام التشغيل:</string>
<string name="your_comment">تعليقك (باللغة الإنجليزية):</string>
<string name="error_details_headline">تفاصيل:</string>
<string name="error_details_headline">التفاصيل:</string>
<string name="report_error">الإبلاغ عن خطأ</string>
@@ -170,22 +168,22 @@
<string name="use_old_player_title">استخدام المشغل القديم</string>
<string name="use_old_player_summary">المشغل القديم المدمج في إطار Mediaframework</string>
<string name="short_thousand">الف</string>
<string name="short_thousand">ألف</string>
<string name="short_million">مليون</string>
<string name="short_billion">بليون</string>
<string name="no_subscribers">صفر لا تقم با الإختيار (في بعض اللغات) لأنها ليست \"حالة خاصة\" للأندرويد</string>
<string name="no_subscribers">ليس هناك مشترِكون</string>
<plurals name="subscribers">
<item quantity="zero">صفر</item>
<item quantity="one">%s مشترك</item>
<item quantity="two">اثنان</item>
<item quantity="few">قليل</item>
<item quantity="many">كثير</item>
<item quantity="zero">صفر مشترِك</item>
<item quantity="one">%s مشترِك</item>
<item quantity="two">مشترِكان</item>
<item quantity="few">%s مشترك</item>
<item quantity="many">%s مشترك</item>
<item quantity="other">%s مشتركون</item>
</plurals>
<string name="no_views">لاتوجد مشاهدات</string>
<string name="no_videos">لاتوجد فديوهات</string>
<string name="no_views">دون مشاهدات</string>
<string name="no_videos">لاتوجد فيديوهات</string>
<string name="start">تشغيل</string>
<string name="pause">إيقاف</string>
<string name="view">شغل</string>
@@ -196,15 +194,15 @@
<string name="finish">حسناً</string>
<string name="msg_name">اسم الملف</string>
<string name="msg_threads">العمليات</string>
<string name="msg_threads">الخيوط</string>
<string name="msg_error">الخطأ</string>
<string name="msg_server_unsupported">الخادم غير معتمد</string>
<string name="msg_exists">الملف موجود مسبقا</string>
<string name="msg_url_malform">العنوان غير صحيح أو ان الإنترنت غير متوفر</string>
<string name="msg_running">NewPipe يقوم بالتحميل</string>
<string name="msg_url_malform">العنوان غير صحيح أو الإنترنت غير متوفر</string>
<string name="msg_running">يقوم نيوبايب بالتنزيل</string>
<string name="msg_running_detail">انقر للحصول على التفاصيل</string>
<string name="msg_wait">أرجو الإنتظار…</string>
<string name="msg_copied">نسخ إلى الحافظة</string>
<string name="msg_wait">يُرجى الإنتظار…</string>
<string name="msg_copied">تم نسخه إلى الحافظة</string>
<string name="no_available_dir">الرجاء تحديد مجلد لحفظ التنزيلات</string>
<string name="msg_popup_permission">هذا الإذن مطلوب
\nللفتح في وضع النافذة المنبثقة</string>
@@ -217,28 +215,28 @@
<string name="charset_letters_and_digits">الحروف والأرقام</string>
<string name="charset_most_special_characters">معظم الأحرف الخاصة</string>
<string name="title_activity_about">معلومات عن NewPipe</string>
<string name="title_activity_about">عن تطبيق نيوپايپ</string>
<string name="action_settings">الإعدادات</string>
<string name="title_licenses">تراخيص الجهات الخارجية</string>
<string name="error_unable_to_load_license">تعذر تحميل الترخيص</string>
<string name="action_open_website">فتح الموقع</string>
<string name="tab_contributors">المساهمون</string>
<string name="tab_licenses">التراخيص</string>
<string name="app_description">بث مباشر على يوتيوب مجاني خفيف الوزن لنظام أندرويد.</string>
<string name="app_description">تطبيق حُر و خفيف لالتقاط البث على نظام الأندرويد.</string>
<string name="contribution_title">ساهم</string>
<string name="contribution_encouragement">إذا كانت لديك أفكار؛ أو ترجمة، أو تغييرات تخص التصميم، أو تنظيف و تحسين الشفرة البرمجية ، أو تعديلات عميقة عليها، فتذكر أنّ مساعدتك دائما موضع ترحيب. وكلما أتممنا شيئا كلما كان ذلك أفضل !</string>
<string name="view_on_github">عرض على GitHub</string>
<string name="donation_title">تبرع</string>
<string name="donation_encouragement">"يتم تطوير NewPipe من قبل المتطوعين الذين يقضون وقت فراغهم لتقديم أفضل تجربة لك. حان الوقت لرد المساعدة مع المطورين وجعل NewPipe أكثر و أفضل أثناء الاستمتاع بفنجان من القهوة "</string>
<string name="donation_encouragement">يتم تطوير NewPipe من قبل متطوعين يقضون وقت فراغهم لتقديم أفضل تجربة لك. حان الوقت لرد المساعدة مع المطورين وجعل NewPipe أكثر و أفضل بينما تستمتع بفنجان من القهوة.</string>
<string name="give_back">تبرع</string>
<string name="website_title">الموقع</string>
<string name="website_encouragement">قم بزيارة موقع NewPipe لمزيد من المعلومات والأخبار.</string>
<string name="website_title">موقع الويب</string>
<string name="website_encouragement">قم بزيارة موقع NewPipe لمزيد من المعلومات والمستجدات.</string>
<string name="app_license_title">تراخيص NewPipe</string>
<string name="read_full_license">قراءة الترخيص</string>
<string name="read_full_license">قراءة الرخصة</string>
<string name="title_history_search">البحث</string>
<string name="title_history_view">تمت مشاهدته</string>
<string name="title_history_search">التي تم البحث عنها</string>
<string name="title_history_view">تمت مشاهدتها</string>
<string name="history_disabled">تم تعطيل السجل</string>
<string name="history_empty">التاريخ فارغ</string>
<string name="history_cleared">تم مسح التاريخ</string>
@@ -250,20 +248,20 @@
<string name="subscription_page_summary">صفحة الاشتراك</string>
<string name="feed_page_summary">صفحة الخلاصة</string>
<string name="channel_page_summary">صفحة القناة</string>
<string name="select_a_channel">حدد قناة</string>
<string name="no_channel_subscribed_yet">لم يتم الاشتراك في اي قناة بعد</string>
<string name="trending">الترند</string>
<string name="select_a_channel">اختر قناة</string>
<string name="no_channel_subscribed_yet">لم يتم الاشتراك في أي قناة بعد</string>
<string name="trending">الشائعة</string>
<string name="top_50">أفضل 50</string>
<string name="new_and_hot">جديد &amp; وساخن</string>
<string name="new_and_hot">جديد وساخن</string>
<string name="title_activity_background_player">مشغل الخلفية</string>
<string name="title_activity_popup_player">المشغل المنبثق</string>
<string name="play_queue_remove">حذف</string>
<string name="play_queue_stream_detail">تفاصيل</string>
<string name="play_queue_audio_settings">إعدادات الصوت</string>
<string name="start_here_on_main">بدء التشغيل هنا</string>
<string name="play_queue_stream_detail">التفاصيل</string>
<string name="play_queue_audio_settings">الإعدادات الصوتية</string>
<string name="start_here_on_main">ابدء التشغيل هنا</string>
<string name="start_here_on_popup">تشغيل هنا في وضع النافذة المنبثقة</string>
<string name="reCaptcha_title">تحدي ريكابتشا</string>
<string name="hold_to_append">اضغط للإدراج بقائمة الانتظار</string>
<string name="hold_to_append">اضغط للإدراج في قائمة الانتظار</string>
<plurals name="views">
<item quantity="zero">لاتوجد مشاهدة</item>
<item quantity="one">%s مشاهدة</item>
@@ -274,12 +272,12 @@
</plurals>
<plurals name="videos">
<item quantity="zero">صفر</item>
<item quantity="one">واحد</item>
<item quantity="two">%s اثنان</item>
<item quantity="few">%s قليل</item>
<item quantity="many">%s عدد كثير</item>
<item quantity="other">"%s أخرى "</item>
<item quantity="zero">%s فيديو</item>
<item quantity="one">%s فيديو</item>
<item quantity="two">%s فيديوهات</item>
<item quantity="few">%s فيديوهات</item>
<item quantity="many">%s فيديوهات</item>
<item quantity="other">%s فيديوهات</item>
</plurals>
<string name="recaptcha_request_toast">إعادة طلب كلمة التحقق</string>
@@ -288,10 +286,10 @@
<string name="kiosk_page_summary">صفحة الكشك</string>
<string name="select_a_kiosk">حدد كشك</string>
<string name="kiosk">كشك</string>
<string name="enqueue_on_background">إدراج بقائمة الانتظار في مشغل الخلفية</string>
<string name="enqueue_on_popup">إدراج بقائمة الانتظار على المنبثقة</string>
<string name="start_here_on_background">ابدأ هنا على خلفية المصدر</string>
<string name="kiosk">الكشك</string>
<string name="enqueue_on_background">إدراج في قائمة الانتظار في الخلفية</string>
<string name="enqueue_on_popup">إدراج في قائمة الانتظار على المنبثقة</string>
<string name="start_here_on_background">ابدأ هنا في الخلفية</string>
<string name="default_content_country_title">المحتوى الإفتراضي حسب البلد</string>
<string name="toggle_orientation">تغيير الإتجاه</string>
<string name="switch_to_background">الإنتقال إلى التشغيل في الخلفية</string>
@@ -309,14 +307,14 @@
<string name="import_data_title">استيراد قاعدة البيانات</string>
<string name="export_data_title">تصدير قاعدة البيانات</string>
<string name="import_data_summary">"سيقوم بالكتابة على سجل التاريخ والاشتراكات الحالية "</string>
<string name="export_data_summary">تصدير سجل, الاشتراكات وقوائم التشغيل.</string>
<string name="export_data_summary">تصدير السجل، الإشتراكات وقوائم التشغيل</string>
<string name="show_info">عرض المعلومات</string>
<string name="controls_add_to_playlist_title">إضافة إلى</string>
<string name="settings_category_debug_title">تحليل</string>
<string name="live">مٌباشِر</string>
<string name="video_streams_empty">لم يتم العثور على أي بث</string>
<string name="video_streams_empty">لم يتم العثور على أي بث مرئي</string>
<string name="audio_streams_empty">لم يتم العثور على أي بث صوتي</string>
<string name="detail_drag_description">إسحب للقيام بالترتيب</string>
@@ -333,7 +331,7 @@
<string name="drawer_header_action_paceholder_text">سوف يظهر شيء هنا قريبا ;D</string>
<string name="video_player">مشغل الفديو</string>
<string name="video_player">مشغل الفيديو</string>
<string name="always_ask_open_action">السؤال دائماً</string>
<string name="preferred_player_fetcher_notification_title">الحصول على المعلومات …</string>
@@ -342,10 +340,10 @@
<string name="create_playlist">إنشاء قائمة تشغيل جديدة</string>
<string name="delete_playlist">حذف قائمة التشغيل</string>
<string name="rename_playlist">"إعادة تسمية قائمة التشغيل "</string>
<string name="playlist_name_input">الأسم</string>
<string name="playlist_name_input">التسمية</string>
<string name="append_playlist">إضافة إلى قائمة تشغيل</string>
<string name="delete_playlist_prompt">هل تريد حذف قائمة التشغيل هذه ؟</string>
<string name="playlist_creation_success">قائمة التشغيل التي تم إنشاؤها</string>
<string name="playlist_creation_success">تم إنشاء قائمة التشغيل</string>
<string name="playlist_add_stream_success">تمت إضافتها إلى قائمة التشغيل</string>
<string name="playlist_delete_failure">لا يمكن حذف قائمة التشغيل</string>
@@ -370,14 +368,14 @@
<string name="invalid_directory">مجلد غير صالح</string>
<string name="file_name_empty_error">لا يمكن ترك إسم الملف فارغا</string>
<string name="error_occurred_detail">طرأ هناك خطأ : %1$s</string>
<string name="no_valid_zip_file">ملف مضغوط غير صالح</string>
<string name="unbookmark_playlist">إزالة الإشارات المرجعية</string>
<string name="error_occurred_detail">طرأ هناك خطأ: %1$s</string>
<string name="no_valid_zip_file">ملف مضغوط ZIP غير صالح</string>
<string name="unbookmark_playlist">إزالة الفواصل المرجعية</string>
<string name="resize_fit">تناسب مع الشاشة</string>
<string name="caption_auto_generated">توليد تلقائي</string>
<string name="import_export_title">إستيراد و تصدير</string>
<string name="import_export_title">إستيراد وتصدير</string>
<string name="import_title">إستيراد</string>
<string name="import_from">إستعادة مِن</string>
<string name="export_to">تصدير إلى</string>
@@ -389,20 +387,20 @@
<string name="import_soundcloud_instructions_hint">"معرفك , soundcloud.com/ الخاص بك "</string>
<string name="playback_default">إفتراضي</string>
<string name="download_thumbnail_summary">تعطيل إيقاف جميع الصور المصغرة من تحميل البيانات واستخدام الذاكرة وحفظها. سيؤدي التغيير هذا إلى محو-ذاكرة التخزين المؤقت في الذاكرة-وذاكرة على القرص.</string>
<string name="download_thumbnail_summary">تعطيل إيقاف جميع الصور المصغرة من تحميل البيانات واستخدام الذاكرة وحفظها. سيؤدي التغيير هذا إلى محو-ذاكرة التخزين المؤقت في الذاكرة-وذاكرة على القرص</string>
<string name="metadata_cache_wipe_title">امسح البيانات الوصفية المخزنة مؤقتًا</string>
<string name="metadata_cache_wipe_summary">إزالة جميع بيانات صفحات الويب المخزنة مؤقتًا</string>
<string name="metadata_cache_wipe_complete_notice">تم محو ذاكرة التخزين المؤقت للبيانات الوصفية</string>
<string name="auto_queue_title">وضع البث القادم تلقائيا في قائمة الإنتظار</string>
<string name="auto_queue_summary">رفض البث المشابه في حال كون البث السابق يعمل في حالة عدم التكرار.</string>
<string name="auto_queue_summary">رفض البث المشابه في حال كون البث السابق يعمل في حالة عدم التكرار</string>
<string name="set_as_playlist_thumbnail">إضافة صورة مصغرة إلى قائمة التشغيل</string>
<string name="bookmark_playlist">قائمة التشغيل المخزنة</string>
<string name="bookmark_playlist">تفضيل قائمة التشغيل</string>
<string name="playlist_thumbnail_change_success">تم تغيير الصورة المصغرة لقائمة التشغيل</string>
<string name="caption_none">بدون تسميات توضيحية</string>
<string name="caption_setting_title">تسميات توضيحية</string>
<string name="caption_setting_description">تعديل مشغل نص التسمية التوضيحية وأنماط الخلفية. يتطلب إعادة تشغيل التطبيق لتصبح التغييرات سارية المفعول.</string>
<string name="caption_setting_description">تعديل مشغل نص التسمية التوضيحية وأنماط الخلفية. يتطلب إعادة تشغيل التطبيق لتصبح التغييرات سارية المفعول</string>
<string name="enable_leak_canary_title">تمكين LeakCanary</string>
<string name="enable_leak_canary_summary">قد تتسبب مراقبة تسرب الذاكرة في عدم استجابة التطبيق عند تفريغ السجلات</string>
@@ -411,29 +409,29 @@
<string name="enable_disposed_exceptions_summary">فرض الإبلاغ عن استثناءات Rx غير القابلة للتسليم خارج دورة حياة الجزء أو النشاط بعد التخلص منها</string>
<string name="clear_views_history_title">محو سجل المشاهدة</string>
<string name="clear_views_history_summary">احذف محفوظات الفديوهات التي تم تشغيلها.</string>
<string name="clear_views_history_summary">احذف سِجل الفيديوهات التي تم تشغيلها</string>
<string name="delete_view_history_alert">حذف سجل المشاهدة بالكامل.</string>
<string name="view_history_deleted">سجل المشاهدة محذوف.</string>
<string name="view_history_deleted">تم حذف سجل المشاهدة.</string>
<string name="clear_search_history_title">محو سجل البحث</string>
<string name="clear_search_history_summary">يحذف تاريخ البحث عن الكلمات الرئيسية.</string>
<string name="delete_search_history_alert">حذف محفوظات البحث بالكامل.</string>
<string name="search_history_deleted">سجل البحث المحذوف.</string>
<string name="clear_search_history_summary">يحذف تاريخ البحث عن الكلمات الرئيسية</string>
<string name="delete_search_history_alert">حذف سِجل البحث بالكامل.</string>
<string name="search_history_deleted">تم حذف سجل البحث.</string>
<string name="external_player_unsupported_link_type">المشغل الخارجي لا يدعم هذه الأنواع من الروابط</string>
<string name="invalid_source">مصدر ملف / مصدر غير صالح</string>
<string name="invalid_file">الملف غير موجود أو ليس لديك الإذن الكافي للقراءة أو الكتابة إليه</string>
<string name="invalid_source">ملف غير صالح / محتوى غير صالح</string>
<string name="invalid_file">الملف غير موجود أو ليس لديك الإذن الكافي للقراءة أو الكتابة فيه</string>
<string name="no_streams_available_download">لا توجد تدفقات متاحة للتنزيل</string>
<string name="one_item_deleted">تم حذف عنصر واحد.</string>
<string name="toast_no_player">لم يتم تثبيت أي تطبيق لتشغيل هذا الملف</string>
<string name="app_license">NewPipe هو برنامج مفتوح المصدر حقوق متروكة: يمكنك استخدام الكود ودراسته وتحسينه بإرادتك. و على وجه التحديد يمكنك إعادة توزيعه / أو تعديله تحت شروط رخصة GNU العمومية والتي نشرتها مؤسسة البرمجيات الحرة، سواء الإصدار 3 من الرخصة، أو (باختيارك) أي إصدار لاحق.</string>
<string name="app_license">NewPipe هو برنامج مفتوح المصدر حقوق متروكة: يمكنك استخدام الكود ودراسته وتحسينه كما شئت. و على وجه التحديد يمكنك إعادة توزيعه / أو تعديله تحت شروط رخصة GNU العمومية والتي نشرتها مؤسسة البرمجيات الحرة، سواء الإصدار 3 من الرخصة، أو (باختيارك) أي إصدار جديد.</string>
<string name="delete_stream_history_prompt">هل تريد حذف هذا العنصر من سجل المشاهدة؟</string>
<string name="delete_all_history_prompt">هل أنت متأكد من أنك تريد حذف كل العناصر من السجل؟</string>
<string name="title_last_played">آخر ما تم تشغيله</string>
<string name="title_most_played">الأكثر تشغيلا</string>
<string name="override_current_data">هذا سوف يتجاوز الإعدادت الحالية الخاصة بك.</string>
<string name="override_current_data">هذا سوف يُزيل إعداداتك الحالية.</string>
<string name="preferred_open_action_settings_title">طريقة \'التشغيل\' المفضلة</string>
<string name="preferred_open_action_settings_summary">"الإجراء الافتراضي عند فتح المحتوى — %s"</string>
@@ -470,7 +468,7 @@
<string name="privacy_policy_title">"سياسة الخصوصية في NewPipe "</string>
<string name="privacy_policy_encouragement">يأخذ مشروع NewPipe خصوصيتك على محمل الجد. لذلك ، لا يجمع التطبيق أي بيانات دون موافقتك.
\nتوضح سياسة خصوصية NewPipe بالتفصيل البيانات التي يتم إرسالها وتخزينها عند إرسال تقرير الأعطال.</string>
<string name="read_privacy_policy">قراءة سياسة الخصوصية</string>
<string name="read_privacy_policy">الإطلاع على سياسة الخصوصية</string>
<string name="start_accept_privacy_policy">من أجل الامتثال للائحة الأوروبية العامة لحماية البيانات (GDPR) ، فإننا نلفت انتباهك إلى سياسة خصوصية NewPipe. يرجى قراءتها بعناية.
\nو يجب عليك قبولها لإرسال تقرير الأخطاء إلينا.</string>
<string name="accept">قبول</string>
@@ -488,4 +486,8 @@
<string name="minimize_on_exit_background_description">تصغير إلى مشغل الخلفية</string>
<string name="minimize_on_exit_popup_description">تصغير إلى مشغل منبثق</string>
</resources>
<string name="channels">القنوات</string>
<string name="playlists">قوائم التشغيل</string>
<string name="tracks">المسارات</string>
<string name="users">المستخدمين</string>
</resources>

View File

@@ -48,7 +48,6 @@
<string name="play_audio">Audio</string>
<string name="default_audio_format_title">Defolt audio formatı</string>
<string name="default_video_format_title">Defolt video formatı</string>
<string name="m4a_description">M4A — daha yaxşı keyfiyyət</string>
<string name="theme_title">Mövzu</string>
<string name="light_theme_title">ıq</string>
<string name="dark_theme_title">Qaranlıq</string>

View File

@@ -33,8 +33,6 @@
<string name="show_play_with_kodi_summary">Amuesa una opción pa reproducir un videu per Kodi</string>
<string name="play_audio">Audiu</string>
<string name="default_audio_format_title">Formatu por defeutu d\'audiu</string>
<string name="webm_description">WebM — formatu llibre</string>
<string name="m4a_description">M4A — calidá meyor</string>
<string name="theme_title">Tema</string>
<string name="dark_theme_title">Escuru</string>
<string name="light_theme_title">Claru</string>

View File

@@ -0,0 +1,466 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources><string name="main_bg_subtitle">Націсніце пошук, каб пачаць</string>
<string name="view_count_text">%1$s праглядаў</string>
<string name="upload_date_text">Апублікавана %1$s</string>
<string name="no_player_found">Патокавы плэер не знойдзены. Усталяваць VLC?</string>
<string name="no_player_found_toast">Патокавы плэер не знойдзены (вы можаце ўсталяваць VLC)</string>
<string name="install">Усталяваць</string>
<string name="cancel">Скасаваць</string>
<string name="open_in_browser">Адкрыць у браўзэры</string>
<string name="open_in_popup_mode">Адкрыць у асобным акне</string>
<string name="share">Падзяліцца</string>
<string name="download">Спампаваць</string>
<string name="controls_download_desc">Загрузка файла прамой трансляцыі.</string>
<string name="search">Пошук</string>
<string name="settings">Налады</string>
<string name="did_you_mean">Магчыма, вы мелі на ўвазе: %1$s?</string>
<string name="share_dialog_title">Падзяліцца з дапамогай</string>
<string name="choose_browser">Выбраць браўзэр</string>
<string name="screen_rotation">паварот</string>
<string name="use_external_video_player_title">Знешні відэаплэер</string>
<string name="use_external_video_player_summary">У некаторых разрозненнях НЕ будзе гуку, калі гэтая опцыя абраная</string>
<string name="use_external_audio_player_title">Знешні аўдыяплэер</string>
<string name="popup_mode_share_menu_title">NewPipe ў акне</string>
<string name="subscribe_button_title">Падпісацца</string>
<string name="subscribed_button_title">Вы падпісаныя</string>
<string name="channel_unsubscribed">Падпіска адменена</string>
<string name="subscription_change_failed">Не атрымалася змяніць падпіску</string>
<string name="subscription_update_failed">Не атрымалася абнавіць падпіску</string>
<string name="show_info">Паказаць звесткі</string>
<string name="tab_main">Галоўная</string>
<string name="tab_subscriptions">Падпіскі</string>
<string name="tab_bookmarks">Закладкі</string>
<string name="fragment_whats_new">Што новага</string>
<string name="controls_background_title">У фоне</string>
<string name="controls_popup_title">У акне</string>
<string name="controls_add_to_playlist_title">Дадаць да</string>
<string name="download_path_title">Шлях загрузкі відэа</string>
<string name="download_path_summary">Папка для захоўвання загружаных відэа</string>
<string name="download_path_dialog_title">Увядзіце шлях да папкі для загрузкі відэа</string>
<string name="download_path_audio_title">Шлях загрузкі аўдыё</string>
<string name="download_path_audio_summary">Папка для захоўвання загружаных аўдыё</string>
<string name="download_path_audio_dialog_title">Увядзіце шлях да папкі для загрузкі аўдыё</string>
<string name="autoplay_by_calling_app_title">Аўтапрайграванне</string>
<string name="autoplay_by_calling_app_summary">Прайграваць відэа пры выкліку NewPipe з іншага прыкладання</string>
<string name="default_resolution_title">Разрознянне па змаўчанні</string>
<string name="default_popup_resolution_title">Разрозненне усплываючага акна</string>
<string name="show_higher_resolutions_title">Высокія разрозненні</string>
<string name="show_higher_resolutions_summary">Толькі некаторыя прылады могуць прайграваць відэа ў 2K/4K</string>
<string name="play_with_kodi_title">Прайграць у Kodi</string>
<string name="kore_not_found">Дадатак Kore не знойдзены. Усталяваць яго?</string>
<string name="show_play_with_kodi_title">\"Прайграць у Kodi\"</string>
<string name="show_play_with_kodi_summary">Паказаць опцыю прайгравання відэа праз медыяцэнтр Kodi</string>
<string name="play_audio">Аўдыё</string>
<string name="default_audio_format_title">Фармат аўдыё па змаўчанні</string>
<string name="default_video_format_title">Фармат відэа па змаўчанні</string>
<string name="webm_description">WebM - свабодны</string>
<string name="m4a_description">M4A - вышэй якасць</string>
<string name="theme_title">Тэма</string>
<string name="light_theme_title">Светлая</string>
<string name="dark_theme_title">Цёмная</string>
<string name="black_theme_title">Чорная</string>
<string name="popup_remember_size_pos_title">Аднавіць акно</string>
<string name="popup_remember_size_pos_summary">Запамінаць памер і становішча ўсплываючага акна</string>
<string name="use_inexact_seek_title">Хуткі пошук пазіцыі</string>
<string name="use_inexact_seek_summary">Недакладны пошук дазваляе плэеру шукаць пазіцыю хутчэй, але менш дакладна</string>
<string name="download_thumbnail_title">Загружаць мініяцюры</string>
<string name="download_thumbnail_summary">Адключыце, каб не загружаць мініяцюры і зэканоміць трафік і памяць. Змена налады ачысьціць кэш малюнкаў у памяці і носьбіце</string>
<string name="thumbnail_cache_wipe_complete_notice">Кэш малюнкаў ачышчаны</string>
<string name="metadata_cache_wipe_title">Ачысціць кэш метададзеных</string>
<string name="metadata_cache_wipe_summary">Выдаліць усе загружаныя дадзеныя вэб-старонак</string>
<string name="metadata_cache_wipe_complete_notice">Кэш метададзеных ачышчаны</string>
<string name="auto_queue_title">Аўтадапаўненне чаргі</string>
<string name="auto_queue_summary">Дадаваць падобныя патокі ў чаргу пры прайграванні апошняга, калі не ўключаны паўтор</string>
<string name="player_gesture_controls_title">Кіраванне жэстамі</string>
<string name="player_gesture_controls_summary">Змяняць яркасць і гучнасць жэстамі</string>
<string name="show_search_suggestions_title">Варыянты пошуку</string>
<string name="show_search_suggestions_summary">Адлюстроўваць падказкі пры пошуку</string>
<string name="enable_search_history_title">Гісторыя пошуку</string>
<string name="enable_search_history_summary">Захоўваць пошукавыя запыты лакальна</string>
<string name="enable_watch_history_title">Гісторыя і кэш</string>
<string name="enable_watch_history_summary">Запамінаць прагледжаныя відэа</string>
<string name="resume_on_audio_focus_gain_title">Аднавіць пры фокусе</string>
<string name="resume_on_audio_focus_gain_summary">Аднаўляць прайграванне пасля перапынкаў (напрыклад, тэлефонных званкоў)</string>
<string name="download_dialog_title">Спампаваць</string>
<string name="next_video_title">Наступнае відэа</string>
<string name="show_next_and_similar_title">\"Наступнае\" и \"Прапанаваныя\"</string>
<string name="show_hold_to_append_title">\"Зацісніце, каб дадаць\"</string>
<string name="show_hold_to_append_summary">Паказаць падказку пры націсканні \"У акне\" ці \"У фоне\" на старонцы звестак аб відэа</string>
<string name="url_not_supported_toast">URL не падтрымліваецца</string>
<string name="default_content_country_title">Краіна кантэнту па змаўчанні</string>
<string name="service_title">Сэрвіс</string>
<string name="search_language_title">Мова кантэнту па змаўчанні</string>
<string name="settings_category_player_title">Плэер</string>
<string name="settings_category_player_behavior_title">Павядзенне</string>
<string name="settings_category_video_audio_title">Відэа і аўдыё</string>
<string name="settings_category_history_title">Гісторыя і кэш</string>
<string name="settings_category_popup_title">Усплываючае акно</string>
<string name="settings_category_appearance_title">Знешні выгляд</string>
<string name="settings_category_other_title">Іншае</string>
<string name="settings_category_debug_title">Адладка</string>
<string name="background_player_playing_toast">Прайграванне ў фонавым рэжыме</string>
<string name="popup_playing_toast">Прайграванне ва ўсплываючым акне</string>
<string name="background_player_append">Дададзена ў чаргу ў фоне</string>
<string name="popup_playing_append">Дададзена ў чаргу ў акне</string>
<string name="play_btn_text">Прайграць</string>
<string name="content">Кантэнт</string>
<string name="show_age_restricted_content_title">Кантэнт 18+</string>
<string name="video_is_age_restricted">Відэа з узроставымі абмежаваннямі. Дазволіць падобны кантэнт можна ў наладах.</string>
<string name="duration_live">ужывую</string>
<string name="downloads">Загрузкі</string>
<string name="downloads_title">Загрузкі</string>
<string name="error_report_title">Справаздача пра памылку</string>
<string name="all">Усё</string>
<string name="channel">Канал</string>
<string name="channels">Каналы</string>
<string name="playlist">Плэйліст</string>
<string name="playlists">Плэйлісты</string>
<string name="tracks">Дарожкі</string>
<string name="users">Карыстальнікі</string>
<string name="yes">Так</string>
<string name="later">Пазней</string>
<string name="disabled">Адключана</string>
<string name="filter">Фільтр</string>
<string name="refresh">Абнавіць</string>
<string name="clear">Ачысціць</string>
<string name="popup_resizing_indicator_title">Змена памеру</string>
<string name="best_resolution">Лепшае разрозненне</string>
<string name="undo">Скасаваць</string>
<string name="play_all">Прайграць усё</string>
<string name="always">Заўсёды</string>
<string name="just_once">Толькі цяпер</string>
<string name="file">Файл</string>
<string name="notification_channel_name">Апавяшчэнне NewPipe</string>
<string name="notification_channel_description">Апавяшчэнні для NewPipe ў фоне і ва ўсплываючым акне</string>
<string name="unknown_content">[Невядома]</string>
<string name="toggle_orientation">Пераключыць арыентацыю</string>
<string name="switch_to_background">Перайсці ў фон</string>
<string name="switch_to_popup">Перайсці ў акно</string>
<string name="switch_to_main">Перайсці ў галоўнае акно</string>
<string name="import_data_title">Імпарт дадзеных</string>
<string name="export_data_title">Экспарт дадзеных</string>
<string name="import_data_summary">Ваша бягучая гісторыя і падпіскі перазапішуцца</string>
<string name="export_data_summary">Экспарт гісторыі, падпісак і плэйлістоў</string>
<string name="clear_views_history_title">Ачысціць гісторыю праглядаў</string>
<string name="clear_views_history_summary">Выдаліць гісторыю прайграных патокаў</string>
<string name="delete_view_history_alert">Выдаліць усю гісторыю праглядаў.</string>
<string name="view_history_deleted">Гісторыя праглядаў выдалена.</string>
<string name="clear_search_history_title">Ачысціць гісторыю пошуку</string>
<string name="clear_search_history_summary">Выдаліць гісторыю пошукавых запытаў</string>
<string name="delete_search_history_alert">Выдаліць усю гісторыю пошуку.</string>
<string name="search_history_deleted">Гісторыя пошуку выдалена.</string>
<string name="general_error">Памылка</string>
<string name="network_error">Памылка сеткі</string>
<string name="could_not_load_thumbnails">Не атрымалася загрузіць усе мініяцюры</string>
<string name="youtube_signature_decryption_error">Не атрымалася расшыфраваць подпіс URL у відэа</string>
<string name="parsing_error">Не атрымалася разабраць вэб-сайт</string>
<string name="light_parsing_error">Не атрымалася цалкам разабраць вэб-сайт</string>
<string name="content_not_available">Кантэнт недаступны</string>
<string name="blocked_by_gema">Заблакавана GEMA</string>
<string name="could_not_setup_download_menu">Не атрымалася стварыць меню загрузкі</string>
<string name="live_streams_not_supported">Гэта прамая трансляцыя, яны пакуль не падтрымліваюцца.</string>
<string name="could_not_get_stream">Не атрымалася знайсці ні аднаго патока</string>
<string name="could_not_load_image">Не атрымалася загрузіць малюнак</string>
<string name="app_ui_crash">Падзенне прыкладання/UI</string>
<string name="player_stream_failure">Не атрымалася прайграць гэты паток</string>
<string name="player_unrecoverable_failure">Памылка плэера без магчымасці аднаўлення</string>
<string name="player_recoverable_failure">Аднаўленне пасля памылкі плэера</string>
<string name="external_player_unsupported_link_type">Знешнія плэеры не падтрымліваюць спасылкі гэтых тыпаў</string>
<string name="invalid_url_toast">Няправільная спасылка</string>
<string name="video_streams_empty">Патокі відэа не знойдзены</string>
<string name="audio_streams_empty">Патокі аўдыё не знойдзены</string>
<string name="invalid_directory">Няправільная папка</string>
<string name="invalid_source">Няправільны файл або крыніца кантэнту</string>
<string name="invalid_file">Файл не існуе або няма дазволу на яго чытанне або запіс</string>
<string name="file_name_empty_error">Імя файла не можа быць пустым</string>
<string name="error_occurred_detail">Адбылася памылка: %1$s</string>
<string name="no_streams_available_download">Няма патокаў, даступных для загрузкі</string>
<string name="sorry_string">Прабачце, гэта не павінна было адбыцца.</string>
<string name="error_report_button_text">Адправіць справаздачу па e-mail</string>
<string name="error_snackbar_message">Прабачце, адбыліся памылкі.</string>
<string name="error_snackbar_action">СПРАВАЗДАЧА</string>
<string name="what_device_headline">Інфармацыя:</string>
<string name="what_happened_headline">Што адбылося:</string>
<string name="info_labels">Што:\\nЗапыт:\\nМова кантэнту:\\nСэрвіс:\\nЧас па Грынвічы:\\nПакет:\\nВерсія:\\nВерсія АС:</string>
<string name="your_comment">Ваш каментар (English):</string>
<string name="error_details_headline">Падрабязнасці:</string>
<string name="list_thumbnail_view_description">Мініяцюра відэа-прэв\'ю</string>
<string name="detail_thumbnail_view_description">Мініяцюра відэа-прэв\'ю</string>
<string name="detail_uploader_thumbnail_view_description">Мініяцюра аватара карыстальніка</string>
<string name="detail_likes_img_view_description">Спадабалася</string>
<string name="detail_dislikes_img_view_description">Не спадабалася</string>
<string name="use_tor_title">Выкарыстоўваць Tor</string>
<string name="use_tor_summary">(Эксперыментальна) Загружаць праз Tor для павышэння прыватнасці (прамыя трансляцыі пакуль не падтрымліваюцца).</string>
<string name="report_error">Паведаміць пра памылку</string>
<string name="user_report">Паведаміць аб парушэнні</string>
<string name="search_no_results">Няма вынікаў</string>
<string name="empty_subscription_feed_subtitle">Нічога няма</string>
<string name="detail_drag_description">Перацягніце, каб змяніць парадак</string>
<string name="err_dir_create">Не атрымалася стварыць папку для загрузкі \"%1$s\"</string>
<string name="info_dir_created">Створана папка для загрузак \"%1$s\"</string>
<string name="video">Відэа</string>
<string name="audio">Аўдыё</string>
<string name="retry">Паспрабаваць зноў</string>
<string name="storage_permission_denied">Няма доступу да носьбіта</string>
<string name="use_old_player_title">Стары плэер</string>
<string name="use_old_player_summary">Стары убудаваны плэер на Mediaframework</string>
<string name="short_thousand">тыс.</string>
<string name="short_million">млн.</string>
<string name="short_billion">млрд.</string>
<string name="no_subscribers">Няма падпісчыкаў</string>
<plurals name="subscribers">
<item quantity="one">%s падпісчык</item>
<item quantity="few">%s падпісчыка</item>
<item quantity="many">%s падпісчыкаў</item>
</plurals>
<string name="no_views">Няма праглядаў</string>
<plurals name="views">
<item quantity="one">%s прагляд</item>
<item quantity="few">%s прагляда</item>
<item quantity="many">%s праглядаў</item>
</plurals>
<string name="no_videos">Няма відэа</string>
<plurals name="videos">
<item quantity="one">%s відэа</item>
<item quantity="few">%s відэа</item>
<item quantity="many">%s відэа</item>
</plurals>
<string name="start">Пачаць</string>
<string name="pause">Паўза</string>
<string name="view">Прайграць</string>
<string name="create">Стварыць</string>
<string name="delete">Выдаліць</string>
<string name="delete_one">Выдаліць адно</string>
<string name="delete_all">Выдаліць усё</string>
<string name="checksum">Кантрольная сума</string>
<string name="dismiss">Адхіліць</string>
<string name="rename">Перайменаваць</string>
<string name="add">Новая мэта</string>
<string name="finish">ОК</string>
<string name="msg_name">Імя файла</string>
<string name="msg_threads">Патокі</string>
<string name="msg_error">Памылка</string>
<string name="msg_server_unsupported">Сервер не падтрымліваецца</string>
<string name="msg_exists">Файл ужо існуе</string>
<string name="msg_url_malform">Няправільны URL або няма доступу да інтэрнэту</string>
<string name="msg_running">NewPipe спампоўвае</string>
<string name="msg_running_detail">Падрабязнасці</string>
<string name="msg_wait">Пачакайце…</string>
<string name="msg_copied">Скапіявана ў буфер абмену</string>
<string name="no_available_dir">Выберыце даступную папку для загрузкі</string>
<string name="msg_popup_permission">Гэтае разрозненне трэба для
\nпрайгравання ў акне</string>
<string name="one_item_deleted">1 элемент выдалены.</string>
<string name="reCaptchaActivity">reCAPTCHA</string>
<string name="reCaptcha_title">Запыт reCAPTCHA</string>
<string name="recaptcha_request_toast">Запытаны ўвод reCAPTCHA</string>
<string name="settings_category_downloads_title">Загрузкі</string>
<string name="settings_file_charset_title">Дапушчальныя сімвалы назвы файлаў</string>
<string name="settings_file_replacement_character_summary">Недапушчальныя сімвалы замяняюцца на гэтыя</string>
<string name="settings_file_replacement_character_title">Сімвал для замены</string>
<string name="charset_letters_and_digits">Літары і лічбы</string>
<string name="charset_most_special_characters">Большасць спецзнакаў</string>
<string name="toast_no_player">Прыкладанне для прайгравання гэтага файла не ўстаноўлена</string>
<string name="title_activity_about">Аб NewPipe</string>
<string name="action_settings">Налады</string>
<string name="action_about">Аб дадатку</string>
<string name="title_licenses">Іншыя ліцэнзіі</string>
<string name="copyright" formatted="true">© %1$s %2$s пад ліцэнзіяй %3$s</string>
<string name="error_unable_to_load_license">Не атрымалася загрузіць ліцэнзію</string>
<string name="action_open_website">Адкрыць вэб-сайт</string>
<string name="tab_about">Аб дадатку</string>
<string name="tab_contributors">Удзельнікі</string>
<string name="tab_licenses">Ліцэнзіі</string>
<string name="app_description">Свабоднае легкавагавае патокавае прайграванне на Android.</string>
<string name="contribution_title">Дапамога праекту</string>
<string name="contribution_encouragement">Вітаецца ўсё - ідэі, пераклад, змены дызайну, чыстка кода або велізарныя змены ў кодзе. Чым больш зроблена, тым лепш!</string>
<string name="view_on_github">Адкрыць на GitHub</string>
<string name="donation_title">Ахвяраваць</string>
<string name="donation_encouragement">Распрацоўшчыкі NewPipe цаной свайго вольнага часу робяць ваша жыццё крышачку зручней. Адплаціце ім тым жа - атрымліваючы асалоду ад кубачка кавы, яны змогуць зрабіць NewPipe яшчэ лепей.</string>
<string name="give_back">Аддаць належнае</string>
<string name="website_title">Вэб-сайт</string>
<string name="website_encouragement">Дзеля атрымання больш падрабязнай інфармацыі і апошніх навін аб NewPipe наведайце наш вэб-сайт.</string>
<string name="privacy_policy_title">Палітыка прыватнасці NewPipe</string>
<string name="privacy_policy_encouragement">Праект NewPipe вельмі сур\'ёзна ставіцца да вашай прыватнасці. Таму прыкладанне не збірае ніякіх дадзеных без вашай згоды.
\nПалітыка прыватнасці NewPipe падрабязна тлумачыць, якія дадзеныя адпраўляюцца і захоўваюцца пры адпраўцы справаздачы аб збоях.</string>
<string name="read_privacy_policy">Прачытаць палітыку</string>
<string name="app_license_title">Ліцэнзія NewPipe</string>
<string name="app_license">NewPipe - свабоднае праграмнае забеспячэнне: вы можаце выкарыстоўваць, вывучаць і паляпшаць яго па сваім меркаванні. У прыватнасці, вы можаце распаўсюджваць і / або змяняць яго ў адпаведнасці з умовамі GNU General Public License, апублікаванай Free Software Foundation, альбо версіі 3, альбо (па вашаму выбару) любой больш позняй версіі.</string>
<string name="read_full_license">Прачытаць ліцэнзію</string>
<string name="title_activity_history">Гісторыя</string>
<string name="title_history_search">Гісторыя пошуку</string>
<string name="title_history_view">Прагледжана</string>
<string name="history_disabled">Гісторыя адключаная</string>
<string name="action_history">Гісторыя</string>
<string name="history_empty">Гісторыя пустая</string>
<string name="history_cleared">Гісторыя ачышчана</string>
<string name="item_deleted">Элемент выдалены</string>
<string name="delete_item_search_history">Выдаліць гэты элемент з гісторыі пошуку?</string>
<string name="delete_stream_history_prompt">Выдаліць гэты элемент з гісторыі пошуку?</string>
<string name="delete_all_history_prompt">Выдаліць усе элементы з гісторыі?</string>
<string name="title_last_played">Нядаўна прайграныя</string>
<string name="title_most_played">Часта прайграваемыя</string>
<string name="main_page_content">Кантэнт галоўнай старонкі</string>
<string name="blank_page_summary">Пустая старонка</string>
<string name="kiosk_page_summary">Старонка кіёска</string>
<string name="subscription_page_summary">Старонка падпіскі</string>
<string name="feed_page_summary">Старонка стужкі</string>
<string name="channel_page_summary">Старонка канала</string>
<string name="select_a_channel">Выберыце канал</string>
<string name="no_channel_subscribed_yet">Пакуль няма падпісак на каналы</string>
<string name="select_a_kiosk">Выберыце кіёск</string>
<string name="export_complete_toast">Экспарт завершаны</string>
<string name="import_complete_toast">Імпарт завершаны</string>
<string name="no_valid_zip_file">Няма правільнага Zip-файла</string>
<string name="could_not_import_all_files">Увага: не ўсе файлы былі імпартаваныя.</string>
<string name="override_current_data">Бягучыя дадзеныя будуць замененыя.</string>
<string name="import_settings">Хочаце імпартаваць налады?</string>
<string name="kiosk">Кіёск</string>
<string name="trending">Трэнды</string>
<string name="top_50">Топ 50</string>
<string name="new_and_hot">Новае і гарачае</string>
<string name="title_activity_background_player">У фоне</string>
<string name="title_activity_popup_player">У акне</string>
<string name="play_queue_remove">Выдаліць</string>
<string name="play_queue_stream_detail">Падрабязнасці</string>
<string name="play_queue_audio_settings">Налады аўдыё</string>
<string name="hold_to_append">Зацісніце, каб дадаць у чаргу</string>
<string name="enqueue_on_background">У чаргу \"У фоне\"</string>
<string name="enqueue_on_popup">У чаргу \"У фоне\"</string>
<string name="start_here_on_main">Відэаплэер</string>
<string name="start_here_on_background">Фонавы плэер</string>
<string name="start_here_on_popup">Плэер у акне</string>
<string name="drawer_open">Адкрыць бакавую панэль</string>
<string name="drawer_close">Зачыніць бакавую панэль</string>
<string name="drawer_header_action_paceholder_text">Хутка тут сёе-тое з\'явіцца ;D</string>
<string name="preferred_open_action_settings_title">Пры адкрыцці кантэнту</string>
<string name="preferred_open_action_settings_summary">Пры адкрыцці спасылкі на кантэнт — %s</string>
<string name="video_player">Відэаплэер</string>
<string name="background_player">Фонавы плэер</string>
<string name="popup_player">Плэер у акне</string>
<string name="always_ask_open_action">Заўсёды пытацца</string>
<string name="preferred_player_fetcher_notification_title">Атрыманне звестак…</string>
<string name="preferred_player_fetcher_notification_message">Загрузка запытанага кантэнту</string>
<string name="create_playlist">Стварыць плэйліст</string>
<string name="delete_playlist">Выдаліць плэйліст</string>
<string name="rename_playlist">Перайменаваць плэйліст</string>
<string name="playlist_name_input">Імя</string>
<string name="append_playlist">Дадаць у плэйліст</string>
<string name="set_as_playlist_thumbnail">На мініяцюру плэйліста</string>
<string name="bookmark_playlist">Дадаць плэйліст у закладкі</string>
<string name="unbookmark_playlist">Выдаліць закладку</string>
<string name="delete_playlist_prompt">Выдаліць гэты плэйліст?</string>
<string name="playlist_creation_success">Плэйліст створаны</string>
<string name="playlist_add_stream_success">Дададзена ў плэйліст</string>
<string name="playlist_thumbnail_change_success">Мініяцюра плэйліста зменена</string>
<string name="playlist_delete_failure">Не атрымалася выдаліць плэйліст</string>
<string name="caption_none">Без тытраў</string>
<string name="resize_fit">Падагнаць</string>
<string name="resize_fill">Запоўніць</string>
<string name="resize_zoom">Наблізіць</string>
<string name="caption_auto_generated">Створаны аўтаматычна</string>
<string name="caption_setting_title">Тытры</string>
<string name="caption_setting_description">Змяніць памер і фон тытраў. Змены ўступяць у сілу пасля перазапуску</string>
<string name="enable_leak_canary_title">Уключыць LeakCanary</string>
<string name="enable_leak_canary_summary">Маніторынг уцечкі памяці можа прывесці да завісання прыкладання</string>
<string name="enable_disposed_exceptions_title">Паведамляць пра памылкі жыццёвага цыклу</string>
<string name="enable_disposed_exceptions_summary">Прымусова паведамляць пра недастаўляемыя Rx-выключэнні па-за фрагментам або жыццёвым цыкле пасля выдалення</string>
<string name="import_export_title">Імпарт/Экспарт</string>
<string name="import_title">Імпарт</string>
<string name="import_from">Імпарт з</string>
<string name="export_to">Экспарт у</string>
<string name="import_ongoing">Імпарт…</string>
<string name="export_ongoing">Экспарт…</string>
<string name="import_file_title">Імпарт файла</string>
<string name="previous_export">Папярэдні экспарт</string>
<string name="subscriptions_import_unsuccessful">Не атрымалася імпартаваць падпіскі</string>
<string name="subscriptions_export_unsuccessful">Не атрымалася экспартаваць падпіскі</string>
<string name="import_youtube_instructions">Імпарт падпісак з YouTube загрузкай файла экспарту:
\n
\n1. Перайдзіце на: %1$s
\n2. Увайдзіце, калі неабходна
\n3. Павінна пачацца загрузка (гэта будзе файл экспарту)</string>
<string name="import_soundcloud_instructions">Імпарт падпісак з SoundCloud набраўшы альбо URL, альбо ваш ID:
\n
\n1. Уключыце \"рэжым працоўнага стала\" у браўзэры (сайт недаступны на тэлефоне)
\n2. Перайдзіце на: %1$s
\n3. Увайдзіце, калі неабходна
\n4. Скапіруйце адрас з адраснага радка.</string>
<string name="import_soundcloud_instructions_hint">вашID, soundcloud.com/вашID</string>
<string name="import_network_expensive_warning">Гэтае дзеянне можа выклікаць вялікі расход трафіку.
\n
\nПрацягнуць?</string>
<string name="playback_speed_control">Кіраванне хуткасцю прайгравання</string>
<string name="playback_tempo">Тэмп</string>
<string name="playback_pitch">Тон</string>
<string name="unhook_checkbox">Незалежна (скажэнні)</string>
<string name="skip_silence_checkbox">Прапускаць цішыню</string>
<string name="playback_step">Крок</string>
<string name="playback_reset">Скід</string>
<string name="start_accept_privacy_policy">У адпаведнасці з Агульным рэгламентам па абароне дадзеных ЕС (GDPR), звяртаем вашу ўвагу на палітыку прыватнасці NewPipe. Калі ласка, уважліва азнаёмцеся з ёй.
\nВам неабходна прыняць яе ўмовы, каб адправіць нам справаздачу пра памылку.</string>
<string name="accept">Прыняць</string>
<string name="decline">Адмовіцца</string>
<string name="limit_data_usage_none_description">Без абмежаванняў</string>
<string name="limit_mobile_data_usage_title">Ліміт разрознення ў мабільнай сетцы</string>
<string name="minimize_on_exit_title">Пры згортванні плэера</string>
<string name="minimize_on_exit_summary">Пры пераключэнні са стандартнага плэера на іншае прыкладанне — %s</string>
<string name="minimize_on_exit_none_description">Нічога не рабіць</string>
<string name="minimize_on_exit_background_description">Фонавы плэер</string>
<string name="minimize_on_exit_popup_description">Плэер у акне</string>
</resources>

View File

@@ -53,8 +53,6 @@
<string name="play_audio">Аудио</string>
<string name="default_audio_format_title">Аудио формат по подразбиране</string>
<string name="default_video_format_title">Видео формат по подразбиране</string>
<string name="webm_description">WebM — свободен формат</string>
<string name="m4a_description">M4A — по-добро качество</string>
<string name="theme_title">Тема на външния вид</string>
<string name="light_theme_title">Светла</string>
<string name="dark_theme_title">Тъмна</string>
@@ -72,7 +70,7 @@
<string name="resume_on_audio_focus_gain_title">Възобнови при връщане на фокус</string>
<string name="resume_on_audio_focus_gain_summary">Продължавай възпроизвеждането след прекъсване (например телефонно обаждане)</string>
<string name="download_dialog_title">Изтегли</string>
<string name="next_video_title">Следващо видео</string>
<string name="next_video_title">Следващ клип</string>
<string name="show_next_and_similar_title">Показвай „следващ“ и „подобни“</string>
<string name="show_hold_to_append_title">Показвай съвет „задръж за добавяне“</string>
<string name="url_not_supported_toast">Непознат URL</string>
@@ -157,7 +155,7 @@
<string name="video">Видео</string>
<string name="audio">Аудио</string>
<string name="retry">Опитай отново</string>
<string name="storage_permission_denied">Отказан достъп до хранилището (провери правата)</string>
<string name="storage_permission_denied">Достъпа до хранилището е отказан</string>
<string name="use_old_player_title">Използвай стария плейър</string>
<string name="use_old_player_summary">Използваю стария вграден Mediaframewoek плейър</string>
@@ -174,8 +172,8 @@
<string name="no_videos">Няма клипове</string>
<plurals name="videos">
<item quantity="one">%s видео</item>
<item quantity="other">%s видеота</item>
<item quantity="one">%s клип</item>
<item quantity="other">%s клипове</item>
</plurals>
<string name="pause">Пауза</string>
@@ -227,7 +225,7 @@
<string name="metadata_cache_wipe_summary">Премахни всички метаданни за уебстраници от кеш-паметта</string>
<string name="metadata_cache_wipe_complete_notice">Кеш-паметта с метаданни бе изтрита</string>
<string name="auto_queue_title">Автоматично нареди на опашка следващия</string>
<string name="auto_queue_summary">"Автоматично прибавяне на сродно съдържание при неповтарящ се преглед . "</string>
<string name="auto_queue_summary">"Автоматично прибавяне на сродно съдържание при неповтарящ се преглед "</string>
<string name="default_content_country_title">Държава, за която да бъде показвано съдържание</string>
<string name="service_title">Услуга</string>
<string name="settings_category_debug_title">Отстраняване на грешки</string>
@@ -248,10 +246,10 @@
<string name="delete_view_history_alert">Изтрий цялата история с изгледани.</string>
<string name="view_history_deleted">Историята с изгледани е изтрита.</string>
<string name="clear_search_history_title">Изтрий историята на търсенията</string>
<string name="clear_search_history_summary">Изтрива историята с въвежданите за търсене ключови думи.</string>
<string name="clear_search_history_summary">Изтрива историята с въвежданите за търсене ключови думи</string>
<string name="delete_search_history_alert">Изтрий цялата история на търсенията.</string>
<string name="search_history_deleted">Историята на търсенията е изтрита.</string>
<string name="youtube_signature_decryption_error">URL подписът на видеото не можа да бъде дешифрован</string>
<string name="youtube_signature_decryption_error">URL подписът на клипа не можа да бъде дешифрован</string>
<string name="external_player_unsupported_link_type">Външните плейъри не поддържат този вид линкове</string>
<string name="invalid_url_toast">Невалиден URL</string>
<string name="invalid_directory">Невалидна директория</string>
@@ -285,9 +283,9 @@
<string name="read_privacy_policy">Прочетете нашата политика за поверителност</string>
<string name="app_license_title">Лицензът на NewPipe</string>
<string name="no_player_found_toast">Липсва стрийм плейър (можете да изтеглите VLC, за да пуснете стрийма)</string>
<string name="download_thumbnail_summary">Изключете, за да спрете зареждането на всички миниатюри, спестявайки трафик и памет. При промяна на тази настройка, текущата кеш-памет на изображенията ще бъде изтрита.</string>
<string name="show_hold_to_append_summary">Показвай подсказка, когато е избран фонов режим или режим в прозорец на страницата с детайли на съответното видео</string>
<string name="clear_views_history_summary">Изтрива историята на възпроизвежданите стриймове.</string>
<string name="download_thumbnail_summary">Изключете, за да спрете зареждането на всички миниатюри, спестявайки трафик и памет. При промяна на тази настройка, текущата кеш-памет на изображенията ще бъде изтрита</string>
<string name="show_hold_to_append_summary">Показвай подсказка, когато е избран фонов режим или режим в прозорец на страницата с детайли на съответния клип</string>
<string name="clear_views_history_summary">Изтрива историята на възпроизвежданите стриймове</string>
<string name="video_streams_empty">Не са намерени видео стриймове</string>
<string name="audio_streams_empty">Не са намерени аудио стриймове</string>
<string name="info_labels">"Какво:\\nЗаявка:\\nЕзик на съдържанието:\\nУслуга:\\nВреме по GMT:\\nПакет:\\nВерсия:\\nОС версия: "</string>
@@ -403,7 +401,7 @@
<string name="caption_auto_generated">Авто-генерирани</string>
<string name="caption_setting_title">Надписи</string>
<string name="caption_setting_description">Модифицирай мащаба на текста и фона на надписите. Изисква рестарт на приложението, за да се приложат промените.</string>
<string name="caption_setting_description">Модифицирай мащаба на текста и фона на надписите. Изисква рестарт на приложението, за да се приложат промените</string>
<string name="enable_leak_canary_title">Включи LeakCanary</string>
<string name="enable_leak_canary_summary">Следенето за пропускане на памет може да направи приложението нестабилно</string>
@@ -457,4 +455,10 @@
<string name="minimize_on_exit_background_description">Минимизирай във фонов режим</string>
<string name="minimize_on_exit_popup_description">Минимизирай в прозорец</string>
</resources>
<string name="channels">Канали</string>
<string name="playlists">Плейлисти</string>
<string name="tracks">Песни</string>
<string name="users">Потребители</string>
<string name="playback_reset">Възстанови</string>
</resources>

View File

@@ -46,8 +46,6 @@
<string name="play_audio">অডিও</string>
<string name="default_audio_format_title">ডিফল্ট অডিও ফরম্যাট</string>
<string name="default_video_format_title">পছন্দসই ভিডিও ফরম্যাট</string>
<string name="webm_description">WebM — বিনামূল্য/স্বাধীন ফরম্যাট</string>
<string name="m4a_description">m4a — ভালো মানের</string>
<string name="theme_title">থিম</string>
<string name="light_theme_title">উজ্জ্বল</string>
<string name="dark_theme_title">অন্ধকার</string>

View File

@@ -23,8 +23,6 @@
<string name="play_audio">Àudio</string>
<string name="default_audio_format_title">Format d\'àudio per defecte</string>
<string name="default_video_format_title">Format de vídeo per defecte</string>
<string name="webm_description">WebM — format lliure</string>
<string name="m4a_description">M4A — millor qualitat</string>
<string name="theme_title">Tema</string>
<string name="light_theme_title">Clar</string>
<string name="dark_theme_title">Fosc</string>
@@ -61,7 +59,7 @@
<string name="import_data_title">Importa una base de dades</string>
<string name="export_data_title">Exporta una base de dades</string>
<string name="export_data_summary">Exporta l\'historial, les subscripcions i les llistes de reproducció.</string>
<string name="export_data_summary">Exporta l\'historial, les subscripcions i les llistes de reproducció</string>
<string name="general_error">Error</string>
<string name="network_error">Error de xarxa</string>
<string name="video">Vídeo</string>
@@ -310,7 +308,7 @@
<string name="main_bg_subtitle">Toqueu el botó de cerca per començar</string>
<string name="use_external_video_player_summary">En algunes resolucions NO hi haurà àudio quan aquesta opció estigui activada</string>
<string name="use_external_audio_player_title">Reproductor d\'àudio extern</string>
<string name="download_thumbnail_summary">Desactiveu-ho per evitar que es carreguin les miniatures i estalviar dades i memòria. Si canvieu aquesta opció, s\'esborrarà la memòria cau d\'imatges tant de la memòria com de l\'emmagatzematge.</string>
<string name="download_thumbnail_summary">Desactiveu-ho per evitar que es carreguin les miniatures i estalviar dades i memòria. Si canvieu aquesta opció, s\'esborrarà la memòria cau d\'imatges tant de la memòria com de l\'emmagatzematge</string>
<string name="enable_search_history_summary">Emmagatzema les cerques localment</string>
<string name="enable_watch_history_summary">Registra els vídeos visualitzats</string>
<string name="resume_on_audio_focus_gain_title">Reprèn automàticament</string>
@@ -390,7 +388,7 @@
<string name="unhook_checkbox">Desvincula (pot provocar distorsió)</string>
<string name="playback_nightcore">Nightcore</string>
<string name="metadata_cache_wipe_summary">Elimina totes les dades de llocs web de la memòria cau</string>
<string name="auto_queue_summary">Afegeix a la cua un vídeo relacionat quan es reprodueix l\'últim vídeo en una cua sense repetició.</string>
<string name="auto_queue_summary">Afegeix a la cua un vídeo relacionat quan es reprodueix l\'últim vídeo en una cua sense repetició</string>
<string name="show_hold_to_append_title">Mostra els missatges d\'ajuda</string>
<string name="show_hold_to_append_summary">Mostra un missatge d\'ajuda quan el botó de mode en segon pla o emergent estigui premut a la pàgina de detalls d\'un vídeo</string>
<string name="info_labels">Què ha passat:\\nPetició:\\nIdioma del contingut:\\nServei:\\nHora GMT:\\nPaquet:\\nVersió:\\nVersió del SO:</string>
@@ -425,16 +423,16 @@
<string name="no_streams_available_download">No hi ha vídeos que es puguin baixar</string>
<string name="caption_setting_title">Subtítols</string>
<string name="caption_setting_description">Modifica la mida del text i el fons dels subtítols. Cal reiniciar l\'aplicació per aplicar els canvis.</string>
<string name="caption_setting_description">Modifica la mida del text i el fons dels subtítols. Cal reiniciar l\'aplicació per aplicar els canvis</string>
<string name="toast_no_player">No s\'ha trobat cap aplicació que pugui reproduir aquest fitxer</string>
<string name="clear_views_history_title">Esborra l\'historial de reproduccions</string>
<string name="clear_views_history_summary">Esborra l\'historial dels vídeos que s\'han reproduït.</string>
<string name="clear_views_history_summary">Esborra l\'historial dels vídeos que s\'han reproduït</string>
<string name="delete_view_history_alert">Esborra tot l\'historial de reproduccions.</string>
<string name="view_history_deleted">S\'ha esborrat l\'historial de reproduccions.</string>
<string name="clear_search_history_title">Esborra l\'historial de cerca</string>
<string name="clear_search_history_summary">Esborra l\'historial de paraules cercades.</string>
<string name="clear_search_history_summary">Esborra l\'historial de paraules cercades</string>
<string name="delete_search_history_alert">Esborra tot l\'historial de cerca.</string>
<string name="search_history_deleted">S\'ha esborrat l\'historial de cerca.</string>
<string name="one_item_deleted">S\'ha esborrat 1 element.</string>

View File

@@ -64,9 +64,60 @@
<string name="dark_theme_title">酷黑</string>
<string name="black_theme_title">黑色</string>
<string name="popup_remember_size_pos_title">记住弹出窗口的尺寸与位置</string>
<string name="m4a_description">M4A — 更好的音质</string>
<string name="popup_remember_size_pos_summary">记住上一次弹出窗口的位置以及大小</string>
<string name="thumbnail_cache_wipe_complete_notice">清理照片内存</string>
<string name="minimize_on_exit_popup_description">最小化弹出播放器</string>
</resources>
<string name="clear_views_history_title">清除观看历史</string>
<string name="search_history_deleted">已删除搜索历史</string>
<string name="general_error">错误</string>
<string name="network_error">网络错误</string>
<string name="report_error">举报错误</string>
<string name="search_no_results">没有结果</string>
<string name="start">开始</string>
<string name="pause">暂停</string>
<string name="view">播放</string>
<string name="create">创建</string>
<string name="delete">删除</string>
<string name="delete_all">删除所有</string>
<string name="add">新任务</string>
<string name="finish">
\n</string>
<string name="msg_error">错误
\n</string>
<string name="msg_server_unsupported">服务器无法支持</string>
<string name="msg_exists">文件已存在</string>
<string name="msg_running">NewPipe 下载中</string>
<string name="msg_wait">请稍等…</string>
<string name="charset_letters_and_digits">字母与数字</string>
<string name="charset_most_special_characters">最特别的字符</string>
<string name="toast_no_player">这个文件里没有已下载应用程式</string>
<string name="title_activity_about">关于NewPipe</string>
<string name="action_settings">设置</string>
<string name="action_about">关于</string>
<string name="title_licenses">第三方执照</string>
<string name="action_open_website">打开网页</string>
<string name="unbookmark_playlist">删除书签</string>
<string name="delete_playlist_prompt">请问你要删除这个列表吗?</string>
<string name="playlist_creation_success">已创建播放列表</string>
<string name="playlist_add_stream_success">已加入播放列表</string>
<string name="playback_step">步骤</string>
<string name="playback_reset">重置</string>
<string name="start_accept_privacy_policy">为了遵守欧洲通用数据保护法规GDPR,我们请你注意NewPipe的隐私政策.请仔细阅读.
\n你必须接受它才能将错误报告发送给我们.</string>
<string name="accept">接受</string>
<string name="decline">拒绝</string>
<string name="limit_data_usage_none_description">没有限制</string>
<string name="limit_mobile_data_usage_title">使用移动数据时的解析度限制</string>
<string name="minimize_on_exit_title">最小化应用程序切换</string>
<string name="minimize_on_exit_summary">从主视频播放器切换到其他应用程序时的操作 - %s</string>
<string name="minimize_on_exit_none_description">没有</string>
<string name="minimize_on_exit_background_description">最小化背景播放器</string>
</resources>

View File

@@ -32,8 +32,6 @@
<string name="show_play_with_kodi_summary">Zobrazit možnost přehrání videa pomocí multimediálního centra Kodi</string>
<string name="play_audio">Zvuk</string>
<string name="default_audio_format_title">Výchozí formát zvuku</string>
<string name="webm_description">WebM — volný formát</string>
<string name="m4a_description">M4A — lepší kvalita</string>
<string name="theme_title">Téma</string>
<string name="dark_theme_title">Tmavé</string>
<string name="light_theme_title">Světlé</string>
@@ -314,7 +312,7 @@ otevření ve vyskakovacím okně</string>
<string name="import_data_title">Importovat databázi</string>
<string name="export_data_title">Exportovat databázi</string>
<string name="import_data_summary">Přepíše vaši dosavadní historii a odběry</string>
<string name="export_data_summary">Exportuje historii, odběry a playlisty.</string>
<string name="export_data_summary">Exportuje historii, odběry a playlisty</string>
<string name="external_player_unsupported_link_type">Externí přehrávače nepodporují tyto druhy odkazů</string>
<string name="invalid_url_toast">Neplatná URL</string>
<string name="video_streams_empty">Nenalezeny žádné video streamy</string>
@@ -399,13 +397,13 @@ otevření ve vyskakovacím okně</string>
<string name="use_inexact_seek_title">Použít rychlé nepřesné hledání</string>
<string name="use_inexact_seek_summary">Nepřesné hledání umožní přehrávači posouvat se rychleji, ale se sníženou přesností</string>
<string name="download_thumbnail_title">Načítat náhledy</string>
<string name="download_thumbnail_summary">Zakažte, chcete-li zabránit načítání všech náhledů a tím šetřit data a využití paměti. Změnou tohoto nastavení dojde k vyčištění mezipaměti obrázků.</string>
<string name="download_thumbnail_summary">Zakažte, chcete-li zabránit načítání všech náhledů a tím šetřit data a využití paměti. Změnou tohoto nastavení dojde k vyčištění mezipaměti obrázků</string>
<string name="thumbnail_cache_wipe_complete_notice">Mezipaměť obrázků vymazána</string>
<string name="metadata_cache_wipe_title">Vymazat metadata v mezipaměti</string>
<string name="metadata_cache_wipe_summary">Odebrat všechna data uložená v mezipaměti</string>
<string name="metadata_cache_wipe_complete_notice">Mezipaměť metadat vymazána</string>
<string name="auto_queue_title">Automatická fronta dalšího streamu</string>
<string name="auto_queue_summary">Automaticky připojí související stream při přehrávání posledního streamu v neopakující se frontě.</string>
<string name="auto_queue_summary">Automaticky připojí související stream při přehrávání posledního streamu v neopakující se frontě</string>
<string name="file">Soubor</string>
<string name="invalid_directory">Neplatný adresář</string>
@@ -460,16 +458,16 @@ otevření ve vyskakovacím okně</string>
<string name="preferred_open_action_settings_summary">Výchozí chování při otevírání obsahu — %s</string>
<string name="caption_setting_title">Poznámky</string>
<string name="caption_setting_description">Upravuje velikost textu poznámek a styly pozadí. Změny se projeví po restartu aplikace.</string>
<string name="caption_setting_description">Upravuje velikost textu poznámek a styly pozadí. Změny se projeví po restartu aplikace</string>
<string name="toast_no_player">K přehrání tohoto souboru chybí vhodná aplikace</string>
<string name="clear_views_history_title">Vymazat historii sledování</string>
<string name="clear_views_history_summary">Vymaže historii přehrávaných streamů.</string>
<string name="clear_views_history_summary">Vymaže historii přehrávaných streamů</string>
<string name="delete_view_history_alert">Vymazat celkovou historii sledování.</string>
<string name="view_history_deleted">Historie sledování smazána.</string>
<string name="clear_search_history_title">Vymazat historii vyhledávání</string>
<string name="clear_search_history_summary">Vymaže historii vyhledávaných klíčových slov.</string>
<string name="clear_search_history_summary">Vymaže historii vyhledávaných klíčových slov</string>
<string name="delete_search_history_alert">Vymazat celkovou historii vyhledávání.</string>
<string name="search_history_deleted">Historie vyhledávání smazána.</string>
<string name="one_item_deleted">Jedna položka smazána.</string>

View File

@@ -24,8 +24,6 @@
<string name="show_play_with_kodi_summary">Zeigt eine Option an, über die man Videos mit Kodi abspielen kann</string>
<string name="play_audio">Audio</string>
<string name="default_audio_format_title">Bevorzugtes Audioformat</string>
<string name="webm_description">WebM — freies Format</string>
<string name="m4a_description">M4A — bessere Qualität</string>
<string name="download_dialog_title">Herunterladen</string>
<string name="next_video_title">Nächstes Video</string>
<string name="show_next_and_similar_title">\'Nächste\' und \'ähnliche\' Videos anzeigen</string>
@@ -329,7 +327,7 @@
<string name="import_data_title">Datenbank importieren</string>
<string name="export_data_title">Datenbank exportieren</string>
<string name="import_data_summary">Wird deinen Verlauf und deine Abos überschreiben</string>
<string name="export_data_summary">Verlauf, Abos und Wiedergabelisten exportieren.</string>
<string name="export_data_summary">Verlauf, Abos und Wiedergabelisten exportieren</string>
<string name="no_valid_zip_file">Keine gültige ZIP-Datei</string>
<string name="could_not_import_all_files">Warnung: Nicht alle Dateien konnten importiert werden.</string>
<string name="override_current_data">Dies wird deine aktuellen Einstellungen überschreiben.</string>
@@ -421,9 +419,9 @@
<string name="unhook_checkbox">Verknüpfung aufheben (kann zu Verzerrungen führen)</string>
<string name="playback_nightcore">Nightcore</string>
<string name="playback_default">Standard</string>
<string name="download_thumbnail_summary">Deaktiviere diese Option, um das Laden aller Miniaturansichten zu stoppen und Daten und Speicherverbrauch zu sparen. Wenn du dies änderst, wird sowohl der In-Memory- als auch der On-Disk-Image-Cache gelöscht.</string>
<string name="download_thumbnail_summary">Deaktiviere diese Option, um das Laden aller Miniaturansichten zu stoppen und Daten und Speicherverbrauch zu sparen. Wenn du dies änderst, wird sowohl der In-Memory- als auch der On-Disk-Image-Cache gelöscht</string>
<string name="auto_queue_title">Nächsten Stream automatisch einreihen</string>
<string name="auto_queue_summary">Automatisches Anhängen eines verwandten Streams beim Abspielen des letzten Streams in einer nicht wiederholten Warteschlange.</string>
<string name="auto_queue_summary">Automatisches Anhängen eines verwandten Streams beim Abspielen des letzten Streams in einer nicht wiederholten Warteschlange</string>
<string name="drawer_header_action_paceholder_text">Hier wird bald etwas stehen ;D</string>
@@ -456,16 +454,17 @@
<string name="preferred_open_action_settings_summary">Standardaktion beim Öffnen von Inhalten - %s</string>
<string name="caption_setting_title">Untertitel</string>
<string name="caption_setting_description">Ändere Textgröße und Hintergrundstil des Untertitels im Player. Wird erst nach Neustart des App wirksam.</string>
<string name="caption_setting_description">Ändere Textgröße und Hintergrundstil des Untertitels im Player. Wird erst nach Neustart des App wirksam</string>
<string name="toast_no_player">Keine App zum Abspielen dieser Datei installiert</string>
<string name="clear_views_history_title">Verlauf leeren</string>
<string name="clear_views_history_summary">Löscht den Verlauf der abgespielten Streams.</string>
<string name="clear_views_history_summary">Löscht den Verlauf der abgespielten Streams</string>
<string name="delete_view_history_alert">Löscht den ganzen Verlauf.</string>
<string name="view_history_deleted">Verlauf gelöscht.</string>
<string name="clear_search_history_title">Suchverlauf löschen</string>
<string name="clear_search_history_summary">Lösche Verlauf der Suchschlüsselwörter.</string>
<string name="clear_search_history_summary">Lösche Verlauf der Suchschlüsselwörter</string>
<string name="delete_search_history_alert">Lösche gesamten Suchverlauf.</string>
<string name="search_history_deleted">Suchverlauf gelöscht.</string>
<string name="one_item_deleted">1 Element gelöscht.</string>
@@ -493,4 +492,8 @@
<string name="playback_step">Schritt</string>
<string name="playback_reset">Zurücksetzen</string>
<string name="channels">Kanäle</string>
<string name="playlists">Wiedergabelisten</string>
<string name="tracks">Titel</string>
<string name="users">Nutzer</string>
</resources>

View File

@@ -2,7 +2,7 @@
<resources>
<string name="view_count_text">%1$s προβολές</string>
<string name="upload_date_text">Ανέβηκε στις %1$s</string>
<string name="no_player_found">Δεν βρέθηκε πρόγραμμα αναπαραγωγής. Εγκατάσταση του VLC;</string>
<string name="no_player_found">Δεν βρέθηκε πρόγραμμα αναπαραγωγής. Θα θέλατε να εγκαταστήσετε το VLC;</string>
<string name="install">Εγκατάσταση</string>
<string name="cancel">Ακύρωση</string>
<string name="open_in_browser">Άνοιγμα στον browser</string>
@@ -14,49 +14,49 @@
<string name="share_dialog_title">Κοινοποίηση με</string>
<string name="choose_browser">Επιλέξτε browser</string>
<string name="screen_rotation">περιστροφή</string>
<string name="use_external_video_player_title">Χρήση εξωτερικού video player</string>
<string name="use_external_audio_player_title">Χρήση εξωτερικού audio player</string>
<string name="use_external_video_player_title">Χρήση εξωτερικής εφαρμογής αναπαραγωγής βίντεο</string>
<string name="use_external_audio_player_title">Χρήση εξωτερικής συσκευής αναπαραγωγής ήχου</string>
<string name="download_path_title">Διαδρομή λήψης video</string>
<string name="download_path_summary">Διαδρομή για αποθήκευση των video.</string>
<string name="download_path_dialog_title">Εισάγετε διαδρομή για λήψη των video</string>
<string name="download_path_title">Διαδρομή λήψης βίντεο</string>
<string name="download_path_summary">Διαδρομή για αποθήκευση των βίντεο</string>
<string name="download_path_dialog_title">Εισάγετε διαδρομή για λήψη των βίντεο</string>
<string name="download_path_audio_title">Διαδρομή λήψης αρχείων ήχου</string>
<string name="download_path_audio_summary">Διαδρομή για αποθήκευση αρχείων ήχου</string>
<string name="download_path_audio_dialog_title">Εισάγετε διαδρομή για λήψη αρχείων ήχου.</string>
<string name="download_path_audio_dialog_title">Εισάγετε διαδρομή για λήψη αρχείων ήχου</string>
<string name="default_resolution_title">Προεπιλεγμένη ανάλυση</string>
<string name="play_with_kodi_title">Αναπαραγωγή με το Kodi</string>
<string name="kore_not_found">Η εφαρμογή Kore δεν βρέθηκε. Εγκατάσταση;</string>
<string name="kore_not_found">Η εφαρμογή Kore δεν βρέθηκε. Εγκατάσταση της;</string>
<string name="show_play_with_kodi_title">Εμφάνιση της επιλογής \"Αναπαραγωγή με το Kodi\"</string>
<string name="show_play_with_kodi_summary">Προβολή μιας επιλογής για αναπαραγωγή με το Kodi media center.</string>
<string name="show_play_with_kodi_summary">Προβολή μιας επιλογής για αναπαραγωγή με το Kodi media center</string>
<string name="play_audio">Ήχος</string>
<string name="default_audio_format_title">Προεπιλεγμένη μορφή ήχου</string>
<string name="webm_description">WebM — δωρεάν format</string>
<string name="m4a_description">m4a — καλύτερη ποιότητα</string>
<string name="webm_description">WebM — δωρεάν μορφή</string>
<string name="m4a_description">Μ4Α — καλύτερη ποιότητα</string>
<string name="theme_title">Θέμα</string>
<string name="dark_theme_title">Σκοτεινό</string>
<string name="light_theme_title">Φωτεινό</string>
<string name="download_dialog_title">Λήψη</string>
<string name="next_video_title">Επόμενο video</string>
<string name="show_next_and_similar_title">Προβολή επόμενου και σχετικών video</string>
<string name="next_video_title">Επόμενο βίντεο</string>
<string name="show_next_and_similar_title">Εμφάνιση \"επόμενου\" και \"σχετικών\" βίντεο</string>
<string name="url_not_supported_toast">Δεν υποστηρίζεται η διεύθυνση URL</string>
<string name="search_language_title">Προτιμώμενη γλώσσα περιεχομένου</string>
<string name="settings_category_video_audio_title">Video &amp; Ήχος</string>
<string name="search_language_title">Προεπιλεγμένη γλώσσα περιεχομένου</string>
<string name="settings_category_video_audio_title">Βίντεο &amp; Ήχος</string>
<string name="settings_category_appearance_title">Εμφάνιση</string>
<string name="settings_category_other_title">Άλλα</string>
<string name="background_player_playing_toast">Αναπαραγωγή στο υπόβαθρο</string>
<string name="play_btn_text">Αναπαραγωγή</string>
<string name="network_error">Σφάλμα δικτύου</string>
<string name="list_thumbnail_view_description">Εικόνα προεπισκόπησης video</string>
<string name="detail_thumbnail_view_description">Εικόνα προεπισκόπησης video</string>
<string name="detail_uploader_thumbnail_view_description">Εικόνα προφίλ του uploader</string>
<string name="list_thumbnail_view_description">Μικρογραφία προεπισκόπισης βίντεο</string>
<string name="detail_thumbnail_view_description">Μικρογραφία προεπισκόπησης βίντεο</string>
<string name="detail_uploader_thumbnail_view_description">Μικρογραφία εικόνας προφίλ του χρήστη</string>
<string name="detail_likes_img_view_description">Like</string>
<string name="detail_dislikes_img_view_description">Dislike</string>
<string name="use_tor_title">Χρήση του Tor</string>
<string name="use_tor_summary">Αναγκάζει την κίνηση λήψης μέσω Tor για αυξημένη ανωνυμία (η αναπαραγωγή δεν υποστηρίζεται ακόμη)</string>
<string name="use_tor_summary">(Πειραματικό) Αναγκάζει την κίνηση λήψης μέσω Tor για αυξημένη προστασία προσωπικών δεδομένων (η αναπαραγωγή δεν υποστηρίζεται ακόμη).</string>
<string name="err_dir_create">Δεν μπόρεσε να δημιουργηθεί ο φάκελος \'%1$s\'</string>
<string name="info_dir_created">Δημιουργήθηκε ο φάκελος \'%1$s\'</string>
@@ -101,4 +101,367 @@
<string name="action_history">Ιστορικό</string>
<string name="show_info">Εμφάνιση πληροφοριών</string>
</resources>
<string name="main_bg_subtitle">Πατήστε στην αναζήτηση για να ξεκινήσετε</string>
<string name="no_player_found_toast">Δε βρέθηκε πρόγραμμα αναπαραγωγής ροής δεδομένων (μπορείτε να εγκαταστήσετε το VLC για να κάνετε αναπαραγωγή)</string>
<string name="controls_download_desc">Κατέβασμα του αρχείου ροής.</string>
<string name="use_external_video_player_summary">Κάποιες αναλύσεις ΔΕΝ θα περιλαμβάνουν ήχο όταν επιλεχθεί αυτή η επιλογή</string>
<string name="popup_mode_share_menu_title">NewPipe σε λειτουργία αναδυόμενου παραθύρου</string>
<string name="channel_unsubscribed">Έγινε διαγραφή από το κανάλι</string>
<string name="subscription_change_failed">Αδύνατη η αλλαγή της εγγραφής</string>
<string name="subscription_update_failed">Αδύνατη η ενημέρωση της εγγραφής</string>
<string name="tab_main">Κύριο</string>
<string name="tab_subscriptions">Εγγραφές</string>
<string name="tab_bookmarks">Σελιδοδείκτες</string>
<string name="fragment_whats_new">Νέα</string>
<string name="controls_background_title">Στο παρασκήνιο</string>
<string name="controls_popup_title">Αναδυόμενο παράθυρο</string>
<string name="controls_add_to_playlist_title">Προσθήκη σε</string>
<string name="autoplay_by_calling_app_title">Αυτόματη αναπαραγωγή</string>
<string name="autoplay_by_calling_app_summary">Αναπαραγωγή του βίντεο όταν το NewPipe καλείται από άλλη εφαρμογή</string>
<string name="default_popup_resolution_title">Προεπιλεγμένη ανάλυση αναδυόμενου παραθύρου</string>
<string name="show_higher_resolutions_title">Εμφάνιση υψηλότερων αναλύσεων</string>
<string name="default_video_format_title">Προεπιλεγμένη μορφή βίντεο</string>
<string name="popup_remember_size_pos_title">Ενθύμιση του μεγέθους και της θέσης του αναδυόμενου παραθύρου</string>
<string name="popup_remember_size_pos_summary">Ενθύμιση του τελευταίου μεγέθους και θέσης του παραθύρου</string>
<string name="use_inexact_seek_title">Χρήση γρήγορης μη-ακριβούς αναζήτησης</string>
<string name="use_inexact_seek_summary">Η μη-ακριβής αναζήτηση επιτρέπει στην εφαρμογή να αναζητεί θέσεις στο βίντεο γρηγορότερα με μειωμένη ακρίβεια</string>
<string name="download_thumbnail_title">Φόρτωση thumbnails</string>
<string name="download_thumbnail_summary">Απενεργοποιήστε για να σταματήσετε τη φόρτωση των μικρογραφιών και να εξοικονομήσετε δεδομένα και χρήση μνήμης. Αλλάζοντας αυτή τη ρύθμιση θα εκκαθαρίσει τα προσωρινά αποθηκευμένα δεδομένα στη μνήμη και τον αποθηκευτικό χώρο</string>
<string name="thumbnail_cache_wipe_complete_notice">Εκκαθαρίστηκε η προσωρινή μνήμη εικονών</string>
<string name="metadata_cache_wipe_title">Εκκαθάριση προσωρινά αποθηκευμένων μεταδεδομένων</string>
<string name="metadata_cache_wipe_summary">Αφαίρεση όλων των προσωρινά αποθηκευμένων δεδομένων ιστοσελίδων</string>
<string name="metadata_cache_wipe_complete_notice">Η προσωρινή μνήμη μεταδεδομένων εκκαθαρίστηκε</string>
<string name="auto_queue_title">Αυτόματη πρόσθεση της επόμενης ροής στην ουρά</string>
<string name="auto_queue_summary">Αυτόματη πρόσθεση μιας σχετικής ροής όταν αναπαράγεται η προηγούμενη ροή σε μια μη-επαναλαμβανόμενη ουρά</string>
<string name="player_gesture_controls_title">Έλεγχος αναπαραγωγής με χειρονομίες</string>
<string name="player_gesture_controls_summary">Χρήση χειρονομιών για τον έλεγχο της φωτεινότητας και της έντασης ήχου της εφαρμογής</string>
<string name="show_search_suggestions_summary">Εμφάνιση υποδείξεων ενώ κάνετε αναζήτηση</string>
<string name="enable_search_history_summary">Αποθήκευση αναζητήσεων στη συσκευή</string>
<string name="enable_watch_history_title">Ιστορικό &amp; Προσωρινή Αποθήκευση</string>
<string name="enable_watch_history_summary">Κρατήστε ιστορικό των βίντεο που έχετε δει</string>
<string name="resume_on_audio_focus_gain_title">Συνέχεια όταν η εφαρμογή έρθει σε πρώτο πλάνο</string>
<string name="resume_on_audio_focus_gain_summary">Συνέχιση της αναπαραγωγής έπειτα από διακοπές (π.χ.: κλήσεις)</string>
<string name="show_hold_to_append_title">Εμφάνιση της βοήθειας \"πιέστε παρατεταμένα για πρόσθεση\"</string>
<string name="show_hold_to_append_summary">Εμφάνιση της βοήθειας όταν έχει πατηθεί το κουμπί Παρασκηνίου ή Αναδυόμενου παραθύρου στη σελίδα λεπτομερειών του βίντεο</string>
<string name="default_content_country_title">Προεπιλεγμένη χώρα περιεχομένου</string>
<string name="service_title">Υπηρεσία</string>
<string name="settings_category_player_title">Συσκευή Αναπαραγωγής</string>
<string name="settings_category_player_behavior_title">Συμπεριφορά</string>
<string name="settings_category_history_title">Ιστορικό &amp; Προσωρινή Αποθήκευση</string>
<string name="settings_category_popup_title">Αναδυόμενο παράθυρο</string>
<string name="settings_category_debug_title">Απασφαλμάτωση</string>
<string name="popup_playing_toast">Αναπαραγωγή σε αναδυόμενο παράθυρο</string>
<string name="background_player_append">Προστέθηκε στη λίστα αναπαραγωγής παρασκηνίου</string>
<string name="popup_playing_append">Προστέθηκε στη λίστα αναπαραγωγής αναδυόμενου παραθύρου</string>
<string name="content">Περιεχόμενο</string>
<string name="show_age_restricted_content_title">Εμφάνιση περιεχομένου περιορισμένης πρόσβασης</string>
<string name="video_is_age_restricted">Βίντεο περιορισμένης πρόσβασης. Για να επιτρέπετε περιερχόμενο τέτοιου είδους, ενεργοποιήστε το στις Ρυθμίσεις.</string>
<string name="duration_live">Ζωντανά</string>
<string name="error_report_title">Αναφορά σφαλμάτων</string>
<string name="channels">Κανάλια</string>
<string name="playlist">Λίστα αναπαραγωγής</string>
<string name="playlists">Λίστες αναπαραγωγής</string>
<string name="tracks">Κομμάτια</string>
<string name="users">Χρήστες</string>
<string name="disabled">Απενεργοποιημένο</string>
<string name="refresh">Ανανέωση</string>
<string name="clear">Εκκαθάριση</string>
<string name="popup_resizing_indicator_title">Αλλαγή μεγέθους</string>
<string name="best_resolution">Βέλτιστη ανάλυση</string>
<string name="undo">Αναίρεση</string>
<string name="play_all">Αναπαραγωγή όλων</string>
<string name="always">Πάντα</string>
<string name="just_once">Μόνο μία φορά</string>
<string name="file">Αρχείο</string>
<string name="notification_channel_name">Ειδοποίηση NewPipe</string>
<string name="notification_channel_description">Ειδοποιήσεις για την αναπαραγωγή Παρασκηνίου και Αναδυόμενου Παραθύρου</string>
<string name="unknown_content">[Άγνωστο]</string>
<string name="toggle_orientation">Αλλαγή προσανατολισμού</string>
<string name="switch_to_background">Αλλαγή σε Παρασκήνιο</string>
<string name="switch_to_popup">Αλλαγή σε Αναδυόμενο Παράθυρο</string>
<string name="switch_to_main">Αλλαγή σε Κύριο</string>
<string name="import_data_title">Εισαγωγή βάσης δεδομένων</string>
<string name="export_data_title">Εξαγωγή βάσης δεδομένων</string>
<string name="import_data_summary">Θα παρακάμψει το τρέχον ιστορικό και εγγραφές σας</string>
<string name="export_data_summary">Εξαγωγή ιστορικού, εγγραφών και λιστών αναπαραγωγής</string>
<string name="clear_views_history_title">Εκκαθάριση ιστορικού προβολής</string>
<string name="clear_views_history_summary">Διαγράφει το ιστορικό των ροών που έχουν αναπαραχθεί</string>
<string name="delete_view_history_alert">Διαγραφή ολόκληρου του ιστορικού προβολής.</string>
<string name="view_history_deleted">Το στορικό προβολής διαγράφηκε.</string>
<string name="clear_search_history_title">Διαγραφή ιστορικού αναζητήσεων</string>
<string name="clear_search_history_summary">Διαγράφει το ιστορικό αναζητήσεών σας</string>
<string name="delete_search_history_alert">Διαγραφή ολόκληρου του ιστορικού αναζητήσεων.</string>
<string name="search_history_deleted">Το ιστορικό αναζητήσεων διαγράφηκε.</string>
<string name="could_not_load_thumbnails">Δεν ήταν δυνατή η φόρτωση όλων των εικονιδίων</string>
<string name="youtube_signature_decryption_error">Δεν ήταν δυνατή η αποκρυπτογράφηση της υπογραφής του URL του βίντεο</string>
<string name="parsing_error">Δεν ήταν δυνατή η ανάλυση του ιστοτόπου</string>
<string name="light_parsing_error">Δεν ήταν δυνατή η ανάλυση ολόκληρου του ιστοτόπου</string>
<string name="content_not_available">Το περιεχόμενο δεν είναι διαθέσιμο</string>
<string name="blocked_by_gema">"Έχει αποκλειστεί από την GEMA"</string>
<string name="could_not_setup_download_menu">Δεν ήταν δυνατή η ρύθμιση του μενού λήψεων</string>
<string name="live_streams_not_supported">Αυτή είναι μια Ζωντανή Ροή, που δεν υποστηρίζεται ακόμα.</string>
<string name="could_not_get_stream">Δεν ήταν δυνατή η λήψη καμίας ροής</string>
<string name="could_not_load_image">Δεν ήταν δυνατή η φόρτωση της εικόνας</string>
<string name="app_ui_crash">Η εφαρμογή κράσαρε</string>
<string name="player_stream_failure">Δεν ήταν δυνατή η αναπαραγωγή αυτής της ροής</string>
<string name="player_unrecoverable_failure">Συνέβη ένα μη ανακτήσιμο σφάλμα στη συσκευή αναπαραγωγής</string>
<string name="player_recoverable_failure">Ανάκτηση από σφάλμα της συσκευής αναπαραγωγής</string>
<string name="external_player_unsupported_link_type">Οι εξωτερικές συσκευές αναπαραγωγής δεν υποστηρίζουν αυτού του είδους συνδέσμους</string>
<string name="invalid_url_toast">Μη έγκυρο URL</string>
<string name="video_streams_empty">Δε βρέθηκαν ροές βίντεο</string>
<string name="audio_streams_empty">Δε βρέθηκαν ροές ήχου</string>
<string name="invalid_directory">Μη έγκυρη τοποθεσία</string>
<string name="invalid_source">Μη έγκυρο αρχείο ή πηγή περιεχομένου</string>
<string name="invalid_file">Το αρχείο δεν υπάρχει ή δεν έχουμε επαρκή εξουσιοδότηση για να διαβάσουμε ή να γράψουμε σε αυτό</string>
<string name="file_name_empty_error">Το όνομα αρχείου δεν μπορεί να είναι κενό</string>
<string name="error_occurred_detail">Προέκυψε ένα σφάλμα: %1$s</string>
<string name="no_streams_available_download">Δεν υπάρχουν διαθέσιμες ροές για λήψη</string>
<string name="sorry_string">Λυπούμαστε, αυτό δεν έπρεπε να έχει συμβεί.</string>
<string name="error_report_button_text">Αναφορά σφάλματος με ηλεκτρονικό ταχυδρομίο</string>
<string name="error_snackbar_message">Λυπούμαστε, συνέβησαν κάποια σφάλματα.</string>
<string name="info_labels">What:\\nΑίτημα:\\nΓλώσσα περιεχομένου:\\nΥπηρεσία:\\nΏρα GMT:\\nΠακέτο:\\nΈκδοση:\\nΈκδοση λειτουργικού:</string>
<string name="user_report">Αναφορά χρήστη</string>
<string name="search_no_results">Κανένα αποτέλεσμα</string>
<string name="empty_subscription_feed_subtitle">Τρεις κι ο κούκος</string>
<string name="detail_drag_description">Σύρετε για ταξινόμηση</string>
<string name="retry">Προσπάθεια εκ νέου</string>
<string name="storage_permission_denied">Δεν δώθηκε άδεια εγγραφής στην εσωτερική μνήμη</string>
<string name="use_old_player_title">Χρήση παλαιάς συσκευής αναπαραγωγής</string>
<string name="use_old_player_summary">Παλαιά συσκευή αναπαραγωγής Mediaframework</string>
<string name="short_thousand">Κ</string>
<string name="short_million">Μ</string>
<string name="no_subscribers">Κανένας εγγεγραμένος χρήστης</string>
<plurals name="subscribers">
<item quantity="one">%s εγγεγραμένος χρήστης</item>
<item quantity="other">%s εγγεγραμένοι χρήστες</item>
</plurals>
<string name="no_views">Καμία προβολή</string>
<plurals name="views">
<item quantity="one">%s προβολή</item>
<item quantity="other">%s προβολές</item>
</plurals>
<string name="no_videos">Κανένα βίντεο</string>
<plurals name="videos">
<item quantity="one">%s βίντεο</item>
<item quantity="other">%s βίντεο</item>
</plurals>
<string name="start">Εκκίνηση</string>
<string name="view">Αναπαραγωγή</string>
<string name="create">Δημιουργία</string>
<string name="delete_one">Διαγραφή ενός</string>
<string name="delete_all">Διαγραφή όλων</string>
<string name="checksum">Checksum</string>
<string name="dismiss">Αγνόηση</string>
<string name="rename">Μετονομασία</string>
<string name="add">Νέα αποστολη</string>
<string name="finish">ΟΚ</string>
<string name="msg_name">Όνομα αρχείου</string>
<string name="msg_threads">Νήματα</string>
<string name="msg_server_unsupported">Ο εξυπηρετητής δεν υποστηρίζεται</string>
<string name="msg_url_malform">Λάθος μορφή URL ή δεν υπάρχει σύνδεση στο διαδίκτυο</string>
<string name="msg_running">Λήψη NewPipe</string>
<string name="msg_wait">Παρακαλώ περιμένετε…</string>
<string name="msg_copied">Αντιγράφηκε στο πρόχειρο</string>
<string name="no_available_dir">Παρακαλώ επιλέξτε έναν διαθέσιμο φάκελο λήψεων</string>
<string name="msg_popup_permission">Αυτή η άδεια είναι απαραίτητη για
\nτο άνοιγμα αναδυόμενων παραθύρων</string>
<string name="one_item_deleted">1 αντικείμενο διαγράφηκε.</string>
<string name="reCaptchaActivity">reCAPTCHA</string>
<string name="reCaptcha_title">Πρόκληση reCAPTCHA</string>
<string name="recaptcha_request_toast">Ζητήθηκε πρόκληση reCAPTCHA</string>
<string name="settings_file_charset_title">Επιτρεπτοί χαρακτήρες σε ονόματα αρχείων</string>
<string name="settings_file_replacement_character_summary">Οι μη έγκυροι χαρακτήρες αντικαθίστανται με αυτήν την τιμή</string>
<string name="settings_file_replacement_character_title">Αντικαταστάτης χαρακτήρας</string>
<string name="charset_most_special_characters">Οι περισσότεροι ειδικοί χαρακτήρες</string>
<string name="toast_no_player">Δεν υπάρχει εφαρμογή εγκατεστημένη για την αναπαραγωγή αυτού του αρχείου</string>
<string name="title_activity_about">Σχετικά με το NewPipe</string>
<string name="action_about">Περί</string>
<string name="title_licenses">Άδειες Τρίτων</string>
<string name="copyright" formatted="true">© %1$s από %2$s υπό %3$s</string>
<string name="error_unable_to_load_license">Δεν ήταν δυνατή η φόρτωση της άδειας</string>
<string name="tab_about">Περί</string>
<string name="tab_contributors">Συνεισφέροντες</string>
<string name="app_description">Ελέυθερη και ελαφριά εφαρμογή αναπαραγωγής ροών στο Android.</string>
<string name="contribution_title">Συνεισφέρετε</string>
<string name="contribution_encouragement">Αν έχετε ιδέες για μετάφραση, αλλαγή σχεδιασμού, εκκαθάριση ή ριζικές αλλαγές κώδικα της εφαρμογής—η βοήθεια σας είναι πάντα ευπρόσδεκτη. Όσο περισσότερη έχουμε, τόσο καλύτεροι γινόμαστε!</string>
<string name="view_on_github">Δείτε το στο GitHub</string>
<string name="donation_title">Κάντε μια δωρεά</string>
<string name="donation_encouragement">Το NewPipe αναπτύσσεται από εθελοντές που δαπανούν χρόνο για να σας προσφέρουν τη βέλτιστη δυνατή εμπειρία της εφαρμογής. Μπορείτε να συνεισφέρετε στους προγραμματιστές του NewPipe να το κάνουν ακόμα καλύτερο, προσφέροντάς τους έναν καφέ.</string>
<string name="give_back">Προσφέρτε</string>
<string name="website_title">Ιστότοπος</string>
<string name="website_encouragement">Επισκευτείτε τον ιστότοπο του NewPipe για περισσότερες πληροφορίες και νέα.</string>
<string name="privacy_policy_title">Η πολιτική ιδιωτικού απόρρητου του NewPipe</string>
<string name="privacy_policy_encouragement">Το NewPipe παίρνει πολύ σοβαρά το ιδιωτικό σας απόρρητο. Έτσι, η εφαρμογή αυτή δεν συλλέγει δεδομένα από εσάς χωρίς τη συγκατάθεσή σας.
\nΗ πολιτική ιδιωτικού απόρρητου του NewPipe εξηγεί λεπτομερώς ποια δεδομένα αποστέλλονται και αποθηκεύονται όταν επιλέγετε να στείλετε μια αναφορά σφαλμάτων.</string>
<string name="read_privacy_policy">Διαβάστε την πολιτική ιδιωτικού απόρρητου</string>
<string name="app_license_title">Η άδεια του NewPipe</string>
<string name="app_license">Το NewPipe είναι copylelft ελεύθερο λογισμικό: Μπορείτε να το χρησιμοποιήσετε, να το μελετήσετε, να το μοιραστείτε και να το βελτιώσετε κατά βούληση. Ειδικότερα, μπορείτε να το αναδιανείμετε ή/και να το τροποποιήσετε υπό την άδεια GNU General Public Licence όπως αυτή εκδόθηκε από το Free Software Foundation, είτε υπό την έκδοση 3 της Άδειας είτε (προεραιτικά) υπό οποιαδήποτε μεταγενέστερη άδεια.</string>
<string name="read_full_license">Διαβάστε την άδεια</string>
<string name="title_history_search">Αναζητημένα</string>
<string name="title_history_view">Έχει γίνει προβολή</string>
<string name="history_disabled">Το ιστορικό έχει απενεργοποιηθεί</string>
<string name="history_empty">Το ιστορικό είναι κενό</string>
<string name="history_cleared">Το ιστορικό εκκαθαρίστηκε</string>
<string name="item_deleted">Το αντικείμενο διαγράφηκε</string>
<string name="delete_item_search_history">Θέλετε να σβήσετε αυτό το αντικείμενο από το ιστορικό αναζήτησης;</string>
<string name="delete_stream_history_prompt">Θέλετε να σβήσετε αυτό το αντικείμενο από το ιστορικό προβολής;</string>
<string name="delete_all_history_prompt">Είστε σίγουροι ότι θέλετε να σβήσετε όλα τα αντικείμενα από το ιστορικό;</string>
<string name="title_last_played">Τελευταία αναπαραγωγή</string>
<string name="title_most_played">Αναπαράχθηκε περισσότερο</string>
<string name="main_page_content">Περιεχόμενο της κεντρικής σελίδας</string>
<string name="blank_page_summary">Κενή σελίδα</string>
<string name="kiosk_page_summary">Σελίδα περιπτέρου</string>
<string name="subscription_page_summary">Σελίδα εγγραφών</string>
<string name="channel_page_summary">Σελίδα καναλιών</string>
<string name="select_a_channel">Επιλέξτε ένα κανάλι</string>
<string name="no_channel_subscribed_yet">Δεν έχει γίνει εγγραφή σε κάποιο κανάλι ακόμα</string>
<string name="select_a_kiosk">Επιλέξτε ένα περίπτερο</string>
<string name="export_complete_toast">Η εξαγωγή ολοκληρώθηκε</string>
<string name="import_complete_toast">Η εισαγωγή ολοκληρώθηκε</string>
<string name="no_valid_zip_file">Μη έγκυρο αρχείο ZIP</string>
<string name="could_not_import_all_files">Προσοχή: Δεν ήταν δυνατή η εισαγωγή όλων των αρχείων.</string>
<string name="override_current_data">Αυτό θα παρακάμψει τις τρέχουσες ρυθμίσεις σας.</string>
<string name="import_settings">Θέλετε επίσης να εισάγετε ρυθμίσεις;</string>
<string name="kiosk">Περίπτερο</string>
<string name="title_activity_background_player">Συσκευή αναπαραγωγής Παρασκηνίου</string>
<string name="title_activity_popup_player">Συσκευή αναπαραγωγής Αναδυόμενου παραθύρου</string>
<string name="play_queue_remove">Αφαίρεση</string>
<string name="play_queue_stream_detail">Λεπτομέρειες</string>
<string name="play_queue_audio_settings">Ρυθμίσεις ήχου</string>
<string name="hold_to_append">Πιέστε παρατεταμένα για να προστεθεί στην ουρά</string>
<string name="enqueue_on_background">Προσθήκη στην ουρά Παρασκηνίου</string>
<string name="enqueue_on_popup">Προσθήκη στην ουρά Αναδυόμενου παραθύρου</string>
<string name="start_here_on_main">Εκκίνηση Αναπαραγωγής εδώ</string>
<string name="start_here_on_background">Εκκίνηση εδώ στο Παρασκήνιο</string>
<string name="start_here_on_popup">Εκκίνηση εδώ στο Αναδυόμενο Παράθυρο</string>
<string name="drawer_open">Άνοιγμα Συρταριού</string>
<string name="drawer_close">Κλείσιμο Συρταριού</string>
<string name="drawer_header_action_paceholder_text">Κάτι θα παιχτεί εδω σύντομα ;D</string>
<string name="top_50">Τοπ 50</string>
<string name="new_and_hot">Καινούρια &amp; δημοφιλή</string>
<string name="preferred_open_action_settings_title">Προτιμώμενη ενέργεια ανοίγματος</string>
<string name="preferred_open_action_settings_summary">Προεπιλεγμένη ενέργεια για το άνοιγμα περιεχομένου — %s</string>
<string name="video_player">Συσκευή αναπαραγωγής βίντεο</string>
<string name="background_player">Αναπαραγωγή Παρασκηνίου</string>
<string name="popup_player">Αναπαραγωγή σε Αναδυόμενο Παράθυρο</string>
<string name="always_ask_open_action">Πάντα ερώτηση</string>
<string name="preferred_player_fetcher_notification_title">Γίνεται λήψη πληροφοριών…</string>
<string name="preferred_player_fetcher_notification_message">Γίνεται φόρτωση του ζητούμενου περιεχομένου</string>
<string name="create_playlist">Δημιουργία νέας Λίστας Αναπαραγωγής</string>
<string name="delete_playlist">Διαγραφή Λίστας</string>
<string name="rename_playlist">Μετονομασία Λίστας</string>
<string name="playlist_name_input">Όνομα</string>
<string name="append_playlist">Προσθήκη στη Λίστα</string>
<string name="set_as_playlist_thumbnail">Ορισμός ως μικρογραφία λίστας αναπαραγωγής</string>
<string name="bookmark_playlist">Προσθήκη Σελιδοδείκτη στη Λίστα</string>
<string name="unbookmark_playlist">Διαγραφή Σελιδοδείκτη</string>
<string name="delete_playlist_prompt">Επιθυμείτε να διαγράψετε αυτή τη λίστα αναπαραγωγής;</string>
<string name="playlist_creation_success">Η λίστα αναπαραγωγής δημιουργήθηκε</string>
<string name="playlist_add_stream_success">Προστέθηκε στη λίστα</string>
<string name="playlist_thumbnail_change_success">Η μικρογραφία της λίστας αναπαραγωγής άλλαξε</string>
<string name="playlist_delete_failure">Δεν ήταν δυνατή η διαγραφή της λίστας</string>
<string name="caption_none">Δεν υπάρχουν υπότιτλοι</string>
<string name="resize_fit">Προσαρμογή</string>
<string name="resize_fill">Γέμισμα</string>
<string name="resize_zoom">Μεγέθυνση</string>
<string name="caption_auto_generated">Αυτόματοι</string>
<string name="caption_setting_title">Υπότιτλοι</string>
<string name="caption_setting_description">Τροποποίηση του μεγέθους και του φόντου των υπότιτλων. Απαιτεί επανεκκίνηση της εφαρμογής</string>
<string name="enable_leak_canary_title">Ενεργοποίηση του LeakCanary</string>
<string name="enable_leak_canary_summary">Η παρακολούθηση των διαρροών μνήμης μπορεί να κάνει την εφαρμογή να μην αποκρίνεται κατά το heap dumping</string>
<string name="enable_disposed_exceptions_summary"/>
<string name="import_export_title">Εισαγωγή/Εξαγωγή</string>
<string name="import_title">Εισαγωγή</string>
<string name="import_from">Εισαγωγή από</string>
<string name="export_to">Εξαγωγή σε</string>
<string name="import_ongoing">Γίνεται εισαγωγή…</string>
<string name="export_ongoing">Γίνεται εξαγωγή…</string>
<string name="import_file_title">Εισαγωγή αρχείου</string>
<string name="previous_export">Προηγούμενη εξαγωγή</string>
<string name="subscriptions_import_unsuccessful">Δεν ήταν δυνατή η εισαγωγή των εγγραφών</string>
<string name="subscriptions_export_unsuccessful">Δεν ήταν δυνατή η εισαγωγή των εγγραφών</string>
<string name="import_youtube_instructions">Κάντε εισαγωγή των εγγραφών σας στο YouTube κατεβάζοντας το εξής αρχείο:
\n
\n1. Πλοηγηθήτε στο: %1$s
\n2. Εισέλθετε στο λογαριασμό σας, όταν σας ζητηθεί
\n3. Η λήψη του αρχείου των εγγραφών σας θα ξεκινήσει</string>
<string name="import_soundcloud_instructions">Για να εισάγετε τον λογαριασμό SoundCloud σας, πληκτρολογήστε τον σύνδεσμο ή το ID σας:
\n
\n1. Ενεργοποιήστε τη λειτουργία \"Desktop mode\" στον φυλλομετρητή σας (καθώς η ιστοσελίδα δεν είναι διαθέσιμη για κινητά)
\n2. Πλοηθηθείτε στο %1$s
\n3. Εισέλθετε στο λογαριασμό σας, όταν σας ζητηθεί
\n4. Αντιγράψτε τον σύνδεσμο του λογαριαμού στον οποίο ανακατευθυνθήκατε.</string>
<string name="import_network_expensive_warning">Αυτή η διαδικασία μπορεί να χρησιμοποιήσει μεγάλο όγκο δεδομένων.
\n
\nΕπιθυμείτε να συνεχίσετε;</string>
<string name="playback_speed_control">Έλεγχος ταχύτητας αναπαραγωγής</string>
<string name="playback_tempo">Τέμπο</string>
<string name="playback_pitch">Τόνος</string>
<string name="minimize_on_exit_summary">Ενέργεια κατά τη μετάβαση σε άλλη εφαρμογή — %s</string>
<string name="feed_page_summary">Σελίδαρ Ροής</string>
<string name="trending">Δημοφιλή</string>
<string name="enable_disposed_exceptions_title">Αναφορά σφαλμάτων εκτός κύκλου ζωής</string>
<string name="import_soundcloud_instructions_hint">Το όνομα χρήστη σας, soundcloud.com/όνομαχρήστη</string>
<string name="unhook_checkbox">Αποσύνδεση (μπορεί να προκαλέσει παραμόρφωση)</string>
<string name="skip_silence_checkbox">Επιτάχυνση αναπαραγωγής κατά τη διάρκεια σιωπής</string>
<string name="playback_step">Βήμα</string>
<string name="playback_reset">Μηδενισμός</string>
<string name="start_accept_privacy_policy">Προς συμμόρφωση με τον Ευρωπαϊκό Γενικό Κανονισμό για την Προστασία Δεδομένων (GDPR), σας επιστούμε την προσοχή στην πολιτική προστασίας προσωπικών δεδομένων του NewPipe. Παραλούμε, διαβάστε την προσεκτικά.
\nΘα πρέπει να την αποδεχτέιτε προκειμένου να μας αποστείλετε την αναφορά σφάλματος.</string>
<string name="accept">Αποδοχή</string>
<string name="decline">Απόρριψη</string>
<string name="limit_data_usage_none_description">Χωρίς όριο</string>
<string name="limit_mobile_data_usage_title">Περιορισμός της ανάλυσης όταν γίνεται χρήση δεδομένων</string>
<string name="minimize_on_exit_title">Ελαχιστοποίηση κατά την εναλλαγή εφαρμογών</string>
<string name="minimize_on_exit_none_description">Καμία</string>
<string name="minimize_on_exit_background_description">Ελαχιστοποίηση στο παρασκήνιο</string>
<string name="minimize_on_exit_popup_description">Ελαχιστοποίηση σε αναδυόμενο παράθυρο</string>
</resources>

View File

@@ -21,8 +21,6 @@
<string name="show_play_with_kodi_title">Montri \"Ludi per Kodi\"-opcion</string>
<string name="play_audio">Sono</string>
<string name="default_audio_format_title">Defaŭlta sondosierformo</string>
<string name="webm_description">WebM — libera dosierformo</string>
<string name="m4a_description">m4a — pli bona kvalito</string>
<string name="theme_title">Etoso</string>
<string name="dark_theme_title">Malluma</string>
<string name="light_theme_title">Luma</string>
@@ -38,8 +36,8 @@
<string name="play_btn_text">Ludi</string>
<string name="general_error">Eraro</string>
<string name="network_error">Reteraro</string>
<string name="content_not_available">Enhavo ne estas disponebla.</string>
<string name="blocked_by_gema">Blokita de GEMA.</string>
<string name="content_not_available">Enhavo ne estas disponebla</string>
<string name="blocked_by_gema">Blokita de GEMA</string>
<string name="detail_likes_img_view_description">Ŝatoj</string>
<string name="detail_dislikes_img_view_description">Malŝatoj</string>
@@ -48,8 +46,8 @@
<string name="kore_not_found">La aplikaĵo Kore ne estas trovita. Ĉu instali ĝin?</string>
<string name="show_next_and_similar_title">Montri la sekvan videon kaj similajn videojn</string>
<string name="could_not_load_thumbnails">Ĉiuj miniaturoj ne ŝargeblas</string>
<string name="youtube_signature_decryption_error">La subskribo de la ligilo de la video ne malĉifreblas.</string>
<string name="parsing_error">La retejo ne analizeblas.</string>
<string name="youtube_signature_decryption_error">La subskribo de la ligilo de la video ne malĉifreblas</string>
<string name="parsing_error">La retejo ne analizeblas</string>
<string name="list_thumbnail_view_description">Miniaturo de la antaŭrigardo de la video</string>
<string name="detail_thumbnail_view_description">Miniaturo de la antaŭrigardo de la video</string>
<string name="detail_uploader_thumbnail_view_description">Miniaturo de la bildo de la alŝutinto</string>

View File

@@ -24,8 +24,6 @@
<string name="show_play_with_kodi_summary">Mostrar una opción para reproducir vídeo con Kodi Media Center</string>
<string name="play_audio">Audio</string>
<string name="default_audio_format_title">Formato de audio por defecto</string>
<string name="webm_description">WebM — formato libre</string>
<string name="m4a_description">M4A — mejor calidad</string>
<string name="download_dialog_title">Descargar</string>
<string name="next_video_title">Siguiente vídeo</string>
<string name="url_not_supported_toast">URL no soportada</string>
@@ -330,7 +328,7 @@ abrir en modo popup</string>
<string name="import_data_title">Importar base de datos</string>
<string name="export_data_title">Exportar base de datos</string>
<string name="import_data_summary">Reemplazará su historial actual y sus suscripciones</string>
<string name="export_data_summary">Exportar historial, suscripciones y listas de reproducción.</string>
<string name="export_data_summary">Exportar historial, suscripciones y listas de reproducción</string>
<string name="export_complete_toast">Exportación completa</string>
<string name="import_complete_toast">Importación completa</string>
<string name="no_valid_zip_file">Archivo ZIP no válido</string>
@@ -403,7 +401,7 @@ abrir en modo popup</string>
<string name="use_inexact_seek_title">Usar búsqueda rápida inexacta</string>
<string name="use_inexact_seek_summary">La búsqueda inexacta permite al reproductor buscar posiciones más rápido con menor precisión</string>
<string name="auto_queue_title">Auto-encolar la siguiente transmisión</string>
<string name="auto_queue_summary">Auto-añadir un vídeo relacionado al reproducir el último vídeo en una cola no repetitiva.</string>
<string name="auto_queue_summary">Auto-añadir un vídeo relacionado al reproducir el último vídeo en una cola no repetitiva</string>
<string name="live">DIRECTO</string>
<string name="live_sync">SINCRONIZAR</string>
<string name="file">Archivo</string>
@@ -445,7 +443,7 @@ abrir en modo popup</string>
\n
\n¿Desea continuar?</string>
<string name="download_thumbnail_title">Cargar Miniaturas</string>
<string name="download_thumbnail_summary">Descativar todas las miniaturas para evitar que se carguen, guarden datos y usen memoria. Al cambiar esto se borrarán tanto la caché de imágenes en la memoria como en el disco.</string>
<string name="download_thumbnail_summary">Descativar todas las miniaturas para evitar que se carguen, guarden datos y usen memoria. Al cambiar esto se borrarán tanto la caché de imágenes en la memoria como en el disco</string>
<string name="thumbnail_cache_wipe_complete_notice">Caché de imagen limpiado</string>
<string name="metadata_cache_wipe_title">Metadatos eliminados del caché</string>
<string name="metadata_cache_wipe_summary">Eliminar todos los datos de la página web en caché</string>
@@ -464,14 +462,14 @@ abrir en modo popup</string>
<string name="toast_no_player">No hay ninguna app instalada para reproducir este archivo</string>
<string name="caption_setting_title">Subtítulos</string>
<string name="caption_setting_description">Modificar la escala de texto de los subtítulos y los estilos de fondo del reproductor. Requiere el reinicio de la app para que surta efecto.</string>
<string name="caption_setting_description">Modificar la escala de texto de los subtítulos y los estilos de fondo del reproductor. Requiere el reinicio de la app para que surta efecto</string>
<string name="clear_views_history_title">Borrar historial de reproducciones</string>
<string name="clear_views_history_summary">Elimina el historial de las transmisiones reproducidas.</string>
<string name="clear_views_history_summary">Elimina el historial de las transmisiones reproducidas</string>
<string name="delete_view_history_alert">Elimina todo el historial de reproducciones.</string>
<string name="view_history_deleted">Historial de reproducciones eliminado.</string>
<string name="clear_search_history_title">Borrar historial de búsqueda</string>
<string name="clear_search_history_summary">Elimina el historial de palabras clave de búsqueda.</string>
<string name="clear_search_history_summary">Elimina el historial de palabras clave de búsqueda</string>
<string name="delete_search_history_alert">Elimina todo el historial de búsqueda.</string>
<string name="search_history_deleted">Historial de búsquedas eliminado.</string>
<string name="one_item_deleted">1 elemento eliminado.</string>
@@ -498,4 +496,8 @@ abrir en modo popup</string>
<string name="playback_step">Paso</string>
<string name="playback_reset">Reiniciar</string>
<string name="channels">Canales</string>
<string name="users">Usuarios</string>
<string name="playlists">Listas de reproducción</string>
<string name="tracks">Pistas</string>
</resources>

View File

@@ -9,7 +9,7 @@
<string name="open_in_browser">Ava veebilehitsejas</string>
<string name="open_in_popup_mode">Ava hüpikaknas</string>
<string name="share">Jaga</string>
<string name="download">Laadi alla</string>
<string name="download">Allalaadimine</string>
<string name="controls_download_desc">Laadi voog alla.</string>
<string name="search">Otsi</string>
<string name="settings">Seaded</string>
@@ -58,8 +58,6 @@
<string name="play_audio">Heli</string>
<string name="default_audio_format_title">Heli vaikevorming</string>
<string name="default_video_format_title">Video vaikevorming</string>
<string name="webm_description">WebM — libre vorming</string>
<string name="m4a_description">M4A — parem kvaliteet</string>
<string name="theme_title">Teema</string>
<string name="light_theme_title">Hele</string>
<string name="dark_theme_title">Tume</string>
@@ -69,13 +67,13 @@
<string name="use_inexact_seek_title">Kasuta ebatäpset kerimist</string>
<string name="use_inexact_seek_summary">Ebatäpne kerimine lubab pleieril otsida asukohta kiiremini täpsuse arvel</string>
<string name="download_thumbnail_title">Laadi pisipildid</string>
<string name="download_thumbnail_summary">Keela, peatamaks pisipiltide laadimine ja vähenda andme- ja mälukasutust. Selle muutmine puhastab nii sisemälu kui piltide vahemälu andmekandjal.</string>
<string name="download_thumbnail_summary">Keela, peatamaks pisipiltide laadimine ja vähenda andme- ja mälukasutust. Selle muutmine puhastab nii sisemälu kui piltide vahemälu andmekandjal</string>
<string name="thumbnail_cache_wipe_complete_notice">Pildid kustutati vahemälust</string>
<string name="metadata_cache_wipe_title">Kustuta metaandmed vahemälust</string>
<string name="metadata_cache_wipe_summary">Kustuta veebilehtede andmed vahemälust</string>
<string name="metadata_cache_wipe_complete_notice">Metaandmed kustutati vahemälust</string>
<string name="auto_queue_title">Järgmine voog automaatselt järjekorda</string>
<string name="auto_queue_summary">Lisa seotud voog automaatselt, kui esitusel on viimane voog mittekorduvast järjekorrast.</string>
<string name="auto_queue_summary">Lisa seotud voog automaatselt, kui esitusel on viimane voog mittekorduvast järjekorrast</string>
<string name="player_gesture_controls_title">Pleieri juhtimise viiped</string>
<string name="player_gesture_controls_summary">Luba viiped helitugevuse ja ereduse juhtimiseks</string>
<string name="show_search_suggestions_title">Kuva soovitused</string>
@@ -95,7 +93,7 @@
<string name="default_content_country_title">Sisu vaikimisi riik</string>
<string name="service_title">Teenus</string>
<string name="search_language_title">Sisu vaikimisi keel</string>
<string name="settings_category_player_title">Mängija</string>
<string name="settings_category_player_title">Pleier</string>
<string name="settings_category_player_behavior_title">Käitumine</string>
<string name="settings_category_video_audio_title">Heli ja pilt</string>
<string name="settings_category_history_title">Ajalugu ja vahemälu</string>
@@ -143,13 +141,13 @@
<string name="import_data_title">Impordi andmebaas</string>
<string name="export_data_title">Ekspordi andmebaas</string>
<string name="import_data_summary">Alistab praeguse ajaloo ja tellimused</string>
<string name="export_data_summary">Ekspordi ajalugu, tellimused ja pleilistid.</string>
<string name="export_data_summary">Ekspordi ajalugu, tellimused ja pleilistid</string>
<string name="clear_views_history_title">Puhasta vaatamiste ajalugu</string>
<string name="clear_views_history_summary">Kustutab vaadatud voogude ajaloo.</string>
<string name="clear_views_history_summary">Kustutab vaadatud voogude ajaloo</string>
<string name="delete_view_history_alert">Kustuta kogu vaatamiste ajalugu.</string>
<string name="view_history_deleted">Vaatamiste ajalugu kustutati.</string>
<string name="clear_search_history_title">Kustuta otsinguajalugu</string>
<string name="clear_search_history_summary">Kustutab otsisõnade ajaloo.</string>
<string name="clear_search_history_summary">Kustutab otsisõnade ajaloo</string>
<string name="delete_search_history_alert">Kustuta kogu otsinguajalugu.</string>
<string name="search_history_deleted">Otsinguajalugu kustutati.</string>
<string name="general_error">Viga</string>
@@ -210,7 +208,7 @@
<string name="audio">Audio</string>
<string name="retry">Proovi uuesti</string>
<string name="storage_permission_denied">Pääsuõigused salvestile puuduvad</string>
<string name="use_old_player_title">Kasuta vana mängijat</string>
<string name="use_old_player_title">Kasuta vana pleierit</string>
<string name="use_old_player_summary">Vana sisseehitatud mediaframework pleier</string>
<string name="short_thousand">K</string>
@@ -346,7 +344,7 @@
<string name="drawer_header_action_paceholder_text">Siia ilmub varsti midagi ;D</string>
<string name="preferred_open_action_settings_title">Eelistatud \'ava\' toiming</string>
<string name="preferred_open_action_settings_title">Lingi avamine</string>
<string name="preferred_open_action_settings_summary">Vaikimisi tegevus sisu avamisel — %s</string>
<string name="video_player">Videopleier</string>
@@ -382,7 +380,7 @@
<string name="caption_auto_generated">Automaatselt loodud</string>
<string name="caption_setting_title">Subtiitrid</string>
<string name="caption_setting_description">Kohanda pleieri subtiitrite teksti suurust ja tausta. Jõustamiseks tuleb rakendus taaskäivitada.</string>
<string name="caption_setting_description">Kohanda pleieri subtiitrite teksti suurust ja tausta. Jõustamiseks tuleb rakendus taaskäivitada</string>
<string name="enable_leak_canary_summary">Mälulekke seire võib põhjustada rakenduse hangumise</string>
@@ -418,4 +416,46 @@
<string name="limit_data_usage_none_description">Piiranguta</string>
<string name="limit_mobile_data_usage_title">Piira lahutust mobiilse andmeside kasutamisel</string>
</resources>
<string name="tab_main">Peamenüü</string>
<string name="channels">Kanalid</string>
<string name="playlists">Pleilistid</string>
<string name="tracks">Lood</string>
<string name="users">Kasutajad</string>
<string name="switch_to_main">Lülitu peamisele</string>
<string name="reCaptcha_title">reCAPTCHA nõue</string>
<string name="recaptcha_request_toast">Nõutav on reCAPTCHA</string>
<string name="copyright" formatted="true">© %1$s %2$s %3$s alla</string>
<string name="app_description">Vaba kergekaaluline Androidi voogesitus.</string>
<string name="contribution_encouragement">Kui sul on ideid kujunduse muutmisest, koodi puhastamisest või suurtest koodi muudatustest - abi on alati teretulnud. Mida rohkem tehtud, seda paremaks läheb!</string>
<string name="donation_encouragement">NewPipe arendajad on vabatahtlikud, kes kulutavad oma vaba aega, toomaks sulle parimat kogemust. On aeg anda tagasi aidates arendajaid ja muuta NewPipe veel paremaks, nautides ise tassi kohvi.</string>
<string name="give_back">Anneta</string>
<string name="privacy_policy_encouragement">NewPipe võtab privaatsust väga tõsiselt. Seetõttu ei kogu rakendus ilma nõusolekuta mingeid andmeid.
\nNewPipe privaatsuspoliitika selgitab üksikasjalikult, milliseid andmeid saadetakse ja kogutakse veateate saatmisel.</string>
<string name="app_license">NewPipe vaba avatud koodiga tarkvara. Seada võib kasutada, uurida, jagada ja parandada. Täpsemalt - seda võib levitada ja/või muuta vastavalt Vaba Tarkvara Sihtasutuse avaldatud GNU Üldise Avaliku Litsentsi v.3 (või hilisem) tingimustele.</string>
<string name="enable_leak_canary_title">Luba LeakCanary</string>
<string name="enable_disposed_exceptions_title">Teavita elutsüklist väljas vigadest</string>
<string name="import_soundcloud_instructions">Impordi SoundCloudi profiil trükkides URL või oma ID:
\n
\n1. Luba \"töölaua režiim\" veebilehitsejas (lmobiilsete seadmete jaoks leht pole kättesaadav)
\n2. Ava URL: %1$s
\n3. Logi sisse
\n4. Kopeeri suunatud profiili URL.</string>
<string name="import_soundcloud_instructions_hint">sinu_ID, soundcloud.com/sinu_id</string>
<string name="playback_pitch">Toon</string>
<string name="unhook_checkbox">Tühista ühendus (võib põhjustada moonutusi)</string>
<string name="skip_silence_checkbox">Keri helitu koht edasi</string>
<string name="playback_step">Samm</string>
<string name="playback_reset">Lähtesta</string>
<string name="start_accept_privacy_policy">Selleks, et täita Euroopa Üldist Andmekaitse Määrust (GDPR), juhime tähelepanu NewPipe\'i privaatsuspoliitikale. Palun lugege seda hoolikalt.
\nMeile veateate saatmiseks tuleb sellega nõustuda.</string>
<string name="minimize_on_exit_title">Minimeeri, kui kasutad teisi rakendusi</string>
<string name="minimize_on_exit_summary">Tegevus lülitusel peamiselt videopleierilt teisele rakendusele — %s</string>
<string name="minimize_on_exit_none_description">Pole</string>
<string name="minimize_on_exit_background_description">Esita taustal</string>
<string name="minimize_on_exit_popup_description">Minimeeri hüpikpleierisse</string>
</resources>

View File

@@ -22,8 +22,6 @@
<string name="show_play_with_kodi_summary">Erakutsi bideoa Kodi multimedia zentroarekin erreproduzitzeko aukera</string>
<string name="play_audio">Audioa</string>
<string name="default_audio_format_title">Audio formatu lehenetsia</string>
<string name="webm_description">WebM — formatu librea</string>
<string name="m4a_description">M4A — kalitate hobea</string>
<string name="download_dialog_title">Deskargatu</string>
<string name="next_video_title">Hurrengo bideoa</string>
<string name="show_next_and_similar_title">Erakutsi \'hurrengo\' eta \'antzeko\' bideoak</string>
@@ -328,21 +326,22 @@
<string name="metadata_cache_wipe_summary">Kendu cache-ko wegbuneen datu guztiak</string>
<string name="metadata_cache_wipe_complete_notice">Metadatuen cache-a ezabatuta</string>
<string name="auto_queue_title">Gehitu ilarara hurrengo jarioa</string>
<string name="auto_queue_summary">Gehitu erlazionatutako jario bat azken jarioa jo bitartean errepikapenik gabeko ilara batean.</string>
<string name="auto_queue_summary">Gehitu erlazionatutako jario bat azken jarioa jo bitartean errepikapenik gabeko ilara batean</string>
<string name="settings_category_debug_title">Arazketa</string>
<string name="file">Fitxategia</string>
<string name="import_data_title">Inportatu datu-basea</string>
<string name="export_data_title">Esportatu datu-basea</string>
<string name="import_data_summary">Zure uneko historiala eta harpidetzak gainidatziko ditu</string>
<string name="export_data_summary">Esportatu historiala, harpidetzak eta erreprodukzio-zerrendak.</string>
<string name="export_data_summary">Esportatu historiala, harpidetzak eta erreprodukzio-zerrendak</string>
<string name="clear_views_history_title">Garbitu ikusitakoaren historiala</string>
<string name="clear_views_history_summary">Jotako jarioen historiala ezabatzen du.</string>
<string name="clear_views_history_summary">Jotako jarioen historiala ezabatzen du</string>
<string name="delete_view_history_alert">Ezabatu ikusitakoaren historial osoa.</string>
<string name="view_history_deleted">Ikusitakoaren historiala ezabatuta.</string>
<string name="clear_search_history_title">Garbitu bilaketa historiala</string>
<string name="clear_search_history_summary">Ezabatu bilaketa gakoen historiala.</string>
<string name="clear_search_history_summary">Ezabatu bilaketa gakoen historiala</string>
<string name="delete_search_history_alert">Ezabatu bilaketen historial osoa.</string>
<string name="search_history_deleted">Bilaketen historiala ezabatuta.</string>
<string name="invalid_directory">Direktorio baliogabea</string>
<string name="invalid_source">Fitxategi edo edukiaren iturri baliogabea</string>
@@ -407,7 +406,7 @@
<string name="caption_auto_generated">Automatikoki sortuak</string>
<string name="caption_setting_title">Azpitituluak</string>
<string name="caption_setting_description">Aldatu azpitituluen testuaren eskala eta atzealdeko estiloa. Aplikazioa berrabiarazi behar da aldaketak aplikatzeko.</string>
<string name="caption_setting_description">Aldatu azpitituluen testuaren eskala eta atzealdeko estiloa. Aplikazioa berrabiarazi behar da aldaketak aplikatzeko</string>
<string name="enable_leak_canary_title">Gaitu LeakCanary</string>
<string name="enable_leak_canary_summary">Memoria galeren monitorizazioa. Aplikazioak agian ez du erantzungo memoriaren aitortza egin bitartean</string>
@@ -453,7 +452,7 @@
<string name="import_settings">Ezarpenak ere inportatu nahi dituzu?</string>
<string name="use_inexact_seek_summary">Bilaketa ez zehatzak posizioak azkarrago baina prezisio gutxiagoz bilatzea ahalbidetzen du</string>
<string name="download_thumbnail_summary">"Desgaitu iruditxoak kargatzea gelditzeko eta datuak eta memoria aurrezteko. Hau aldatzean memoria eta diskoko irudien cache-ak garbituko dira."</string>
<string name="download_thumbnail_summary">"Desgaitu iruditxoak kargatzea gelditzeko eta datuak eta memoria aurrezteko. Hau aldatzean memoria eta diskoko irudien cache-ak garbituko dira"</string>
<string name="app_license">NewPipe Software Librea eta Copyleft da: Nahi eran erabili, ikertu, partekatu eta hobetu dezakezu. Zehazki, elkarbanatzea eta aldatzea Free Software Foundation-ek argitaratutako GNU General Public License-ren 3. bertsioa edo berriagoren baten terminoen arabera egiteko baimena duzu.</string>
<string name="enable_disposed_exceptions_summary">Behartu aktibitatearen bizitza ziklotik kanpo baztertu eta gero entregatu ezin diren Rx salbuespenen inguruko txostena</string>

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