Compare commits

...

1429 Commits

Author SHA1 Message Date
Christian Schabesberger
48f966e7db add changelog for 730 2019-03-14 09:24:02 +01:00
Christian Schabesberger
d8746dc592 hotfix decrypt function and move on to version v0.16.1 2019-03-14 09:20:37 +01:00
Christian Schabesberger
f24fab0fa2 fix brake when selecting a mediaccc channel form subscription page 2019-02-25 12:24:48 +01:00
Christian Schabesberger
261620fd13 Merge branch 'master' into release_v0.16.0 2019-02-24 23:06:06 +01:00
Christian Schabesberger
9af6effad9 move on to version 0.16.0 2019-02-24 22:52:07 +01:00
Christian Schabesberger
92602916dd merge weblate 2019-02-24 22:51:08 +01:00
Nathan Follens
6c7b90e1c3 Translated using Weblate (Flemish)
Currently translated at 100.0% (443 of 443 strings)
2019-02-24 06:18:27 +01:00
Nathan Follens
d8e57144f7 Translated using Weblate (Dutch)
Currently translated at 100.0% (443 of 443 strings)
2019-02-24 06:18:27 +01:00
Christian Schabesberger
e69c45a246 Merge pull request #1650 from yausername/dev
comments support
2019-02-21 15:50:21 +01:00
Ritvik Saraf
3f87a6d714 Merge remote-tracking branch 'origin/dev' into dev 2019-02-20 05:29:34 +05:30
Ritvik Saraf
841124b1f3 updated extractor, fixed settings padding 2019-02-20 05:24:33 +05:30
Hosted Weblate
257a878ef4 Merge branch 'origin/dev' into Weblate. 2019-02-19 19:18:29 +01:00
84436
07d3f82912 Translated using Weblate (Vietnamese)
Currently translated at 99.3% (440 of 443 strings)
2019-02-19 19:18:26 +01:00
Ali Demirtas
7d3eb4f5a6 Translated using Weblate (Turkish)
Currently translated at 100.0% (443 of 443 strings)
2019-02-19 19:18:12 +01:00
srkrishna
0efcc55373 Translated using Weblate (Tamil)
Currently translated at 34.5% (153 of 443 strings)
2019-02-19 19:18:12 +01:00
Christian Schabesberger
eafceb8a6c Merge branch 'dev' into dev 2019-02-19 17:35:49 +01:00
Christian Schabesberger
5ac8318e71 Merge pull request #2136 from TeamNewPipe/db_doc
add database doc in dia
2019-02-19 16:06:11 +01:00
Christian Schabesberger
7981f9dc91 add database doc in dia 2019-02-19 16:03:10 +01:00
Christian Schabesberger
a9bf04cbc6 Merge pull request #2135 from TeamNewPipe/firetv
add support for search in amazon firetv
2019-02-19 15:00:22 +01:00
Christian Schabesberger
4b5591d884 move firetv utils into utils package 2019-02-19 14:57:49 +01:00
Christian Schabesberger
c08197f025 Merge branch 'dev' into feature/amazonfiretv-search-support 2019-02-19 14:54:48 +01:00
Christian Schabesberger
5d22eabfa3 Merge pull request #2092 from connectety/updated-urls
support for youtube-nocookie.com
2019-02-19 14:30:56 +01:00
Christian Schabesberger
c3a38e384a Merge branch 'dev' into updated-urls 2019-02-19 14:30:34 +01:00
Christian Schabesberger
0d3dd94559 Merge pull request #2120 from Redirion/patch-1
Fix delayed ducking of Audio
2019-02-19 14:30:11 +01:00
Christian Schabesberger
9cdaa37519 Merge branch 'dev' into patch-1 2019-02-19 14:29:34 +01:00
Christian Schabesberger
202a319d69 Merge pull request #2129 from TeamNewPipe/remove-old-player
Remove old player from the manifest
2019-02-19 14:29:02 +01:00
Christian Schabesberger
198384b2ed Merge branch 'dev' into remove-old-player 2019-02-19 14:28:53 +01:00
Christian Schabesberger
9d8b070f1e Merge pull request #2133 from kapodamy/popup-player-fixup
Fix random Popup player crash
2019-02-19 14:28:04 +01:00
dimqua
28ba2d5008 Translated using Weblate (Russian)
Currently translated at 100.0% (443 of 443 strings)
2019-02-19 14:07:27 +01:00
Arthur
37ddd63d27 Translated using Weblate (Russian)
Currently translated at 100.0% (443 of 443 strings)
2019-02-19 14:07:26 +01:00
dimqua
d7f8b8c1e0 Translated using Weblate (Russian)
Currently translated at 97.5% (432 of 443 strings)
2019-02-18 13:28:20 +01:00
Arthur
9e70c5bbea Translated using Weblate (Russian)
Currently translated at 97.5% (432 of 443 strings)
2019-02-18 13:28:19 +01:00
kapodamy
4dd572063e fix crash while switching from popup to fullscreen player, or closing the popup player. 2019-02-17 16:59:35 -03:00
TobiGr
db9cf95648 Remove old player from the manifest
See https://github.com/TeamNewPipe/NewPipe/pull/1884 for more info
2019-02-17 09:52:05 +01:00
Ritvik Saraf
df6bae4712 merged upstream/dev 2019-02-16 02:06:18 +05:30
Ritvik Saraf
56cb8209b8 refactored comments capability 2019-02-16 01:23:26 +05:30
WaldiS
aba9c2e113 Translated using Weblate (Polish)
Currently translated at 100.0% (443 of 443 strings)
2019-02-15 17:24:20 +01:00
Ali Demirtas
3d25008739 Translated using Weblate (Turkish)
Currently translated at 97.5% (432 of 443 strings)
2019-02-14 10:11:03 +01:00
Redirion
9437f057d0 Fix delayed ducking of Audio
Scenario: listening to a video on NewPipe over Bluetooth and a Notification Sound causes audio focus event AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK.

Problem: With the current implementation animateAudio would cause the audio to reach the target volume AFTER the notification sound is played, which is irritating and annoying.

Solution: animateAudio should just be used on focusGain where it is sensible to increase audio gradually. On ducking event the reaction should be immediate.

This very simple fix does this. Please approve.
2019-02-14 09:52:46 +01:00
Emanuele Petriglia
917d6089a7 Translated using Weblate (Italian)
Currently translated at 97.5% (432 of 443 strings)
2019-02-13 19:10:46 +01:00
toanpv
f19e99e3ad Translated using Weblate (Vietnamese)
Currently translated at 74.9% (332 of 443 strings)
2019-02-13 12:11:48 +01:00
Allan Nordhøy
2d0fb05fa6 Translated using Weblate (Norwegian Bokmål)
Currently translated at 96.8% (429 of 443 strings)
2019-02-13 12:11:46 +01:00
Chris
c617f2dfad Translated using Weblate (Malay)
Currently translated at 100.0% (443 of 443 strings)
2019-02-13 12:11:46 +01:00
naofum
747c5fc89a Translated using Weblate (Japanese)
Currently translated at 96.6% (428 of 443 strings)
2019-02-13 12:11:14 +01:00
Chris
4f662ef203 Translated using Weblate (Indonesian)
Currently translated at 100.0% (443 of 443 strings)
2019-02-13 12:11:13 +01:00
Le Poisson Libre
eb274ad88f Translated using Weblate (French)
Currently translated at 91.9% (407 of 443 strings)
2019-02-13 12:11:09 +01:00
Chris
bd7b41be9b Translated using Weblate (English)
Currently translated at 99.1% (439 of 443 strings)
2019-02-13 12:11:07 +01:00
Chris
076720bfff Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (443 of 443 strings)
2019-02-13 12:11:06 +01:00
Osoitz
2aadae407e Translated using Weblate (Basque)
Currently translated at 100.0% (443 of 443 strings)
2019-02-13 12:11:05 +01:00
Rex_sa
b16bb07774 Translated using Weblate (Arabic)
Currently translated at 100.0% (443 of 443 strings)
2019-02-13 12:11:00 +01:00
naofum
81dd083388 Translated using Weblate (Japanese)
Currently translated at 98.4% (436 of 443 strings)
2019-02-12 16:20:23 +01:00
Chris
92d4cef1e2 Translated using Weblate (Japanese)
Currently translated at 98.4% (436 of 443 strings)
2019-02-12 16:20:22 +01:00
C. Rüdinger
d2cd79dcf5 Translated using Weblate (German)
Currently translated at 99.8% (442 of 443 strings)
2019-02-10 23:34:08 +01:00
Hosted Weblate
0ef8b391e4 Merge branch 'origin/dev' into Weblate. 2019-02-09 13:57:02 +01:00
Eduardo Caron
0fbd105977 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (443 of 443 strings)
2019-02-09 13:57:02 +01:00
ssantos
5c23af541e Translated using Weblate (Portuguese)
Currently translated at 100.0% (443 of 443 strings)
2019-02-09 13:56:57 +01:00
WaldiS
db021ff78b Translated using Weblate (Polish)
Currently translated at 100.0% (443 of 443 strings)
2019-02-09 13:56:57 +01:00
Allan Nordhøy
31294c6f54 Translated using Weblate (Norwegian Bokmål)
Currently translated at 96.8% (429 of 443 strings)
2019-02-09 13:56:56 +01:00
JS Ahn
03d3495759 Translated using Weblate (Korean)
Currently translated at 78.6% (348 of 443 strings)
2019-02-09 13:56:56 +01:00
ssantos
4644e105a8 Translated using Weblate (German)
Currently translated at 100.0% (443 of 443 strings)
2019-02-09 13:56:53 +01:00
Tobias Groza
af5002508d Translated using Weblate (German)
Currently translated at 100.0% (443 of 443 strings)
2019-02-09 13:56:52 +01:00
Nathan Follens
9416d2b9c7 Translated using Weblate (Flemish)
Currently translated at 100.0% (443 of 443 strings)
2019-02-09 13:56:52 +01:00
Nathan Follens
f60f5d5a6c Translated using Weblate (Dutch)
Currently translated at 100.0% (443 of 443 strings)
2019-02-09 13:56:46 +01:00
ezjerry liao
26d00f87a8 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (443 of 443 strings)
2019-02-09 13:56:44 +01:00
Nathan Follens
6c33ea423c Translated using Weblate (Norwegian Bokmål)
Currently translated at 96.6% (428 of 443 strings)
2019-02-09 13:56:40 +01:00
Connectety-W
ad354aca4e added support for youtube-nocookie.com 2019-02-09 12:30:14 +01:00
Christian Schabesberger
ce6a5ebf0a Merge pull request #2107 from chin123/fix_padding_settings
Fix padding in settings layout (Fixes #1866)
2019-02-09 09:08:20 +01:00
Yaron Shahrabani
d66ef38e8d Translated using Weblate (Hebrew)
Currently translated at 100.0% (443 of 443 strings)
2019-02-09 03:38:29 +01:00
Chinmaya Krishnan Mahesh
1b6c49f621 Fix padding in settings layout (Fixes #1866) 2019-02-08 17:33:35 -06:00
Allan Nordhøy
0262c83815 Translated using Weblate (English)
Currently translated at 98.9% (438 of 443 strings)
2019-02-08 21:38:01 +01:00
Allan Nordhøy
c212934130 Translated using Weblate (Norwegian Bokmål)
Currently translated at 96.8% (429 of 443 strings)
2019-02-07 15:12:26 +01:00
Yaron Shahrabani
ac30e47e59 Translated using Weblate (Hebrew)
Currently translated at 98.2% (435 of 443 strings)
2019-02-06 20:36:39 +01:00
Yehuda Levy
d640057453 Translated using Weblate (Hebrew)
Currently translated at 90.1% (399 of 443 strings)
2019-02-06 09:17:45 +01:00
Yaron Shahrabani
0cea136d4d Translated using Weblate (Hebrew)
Currently translated at 90.1% (399 of 443 strings)
2019-02-06 09:17:44 +01:00
Hosted Weblate
9b3d93e734 Merge branch 'origin/dev' into Weblate. 2019-02-06 08:40:24 +01:00
Emin Tufan Çetin
ca3782ac62 Translated using Weblate (Turkish)
Currently translated at 100.0% (441 of 441 strings)
2019-02-06 08:40:24 +01:00
dimqua
dc9ffb7fd5 Translated using Weblate (Russian)
Currently translated at 96.8% (427 of 441 strings)
2019-02-06 08:40:23 +01:00
WaldiS
b21b3a55fe Translated using Weblate (Polish)
Currently translated at 100.0% (441 of 441 strings)
2019-02-06 08:40:21 +01:00
Mostafa Ahangarha
e62bd85a7a Translated using Weblate (Persian)
Currently translated at 57.8% (255 of 441 strings)
2019-02-06 08:40:19 +01:00
Michael Moroni
fed221b008 Translated using Weblate (Italian)
Currently translated at 100.0% (441 of 441 strings)
2019-02-06 08:40:18 +01:00
Terry Louwers
935298d159 Translated using Weblate (Dutch)
Currently translated at 100.0% (441 of 441 strings)
2019-02-06 08:40:17 +01:00
Nathan Follens
909919250c Translated using Weblate (Dutch)
Currently translated at 100.0% (441 of 441 strings)
2019-02-06 08:40:14 +01:00
Emil Tang Kristensen
d24f278f7c Translated using Weblate (Danish)
Currently translated at 85.7% (378 of 441 strings)
2019-02-06 08:40:14 +01:00
赖文胜
ca408a495c Translated using Weblate (Chinese (Simplified))
Currently translated at 97.1% (428 of 441 strings)
2019-02-06 08:39:42 +01:00
Sérgio Marques
ee99719137 Translated using Weblate (Portuguese)
Currently translated at 100.0% (441 of 441 strings)
2019-02-06 08:39:36 +01:00
Christian Schabesberger
5fb0729eae Merge pull request #2083 from TeamNewPipe/ccc
Add MediaCCC support
2019-02-05 18:28:22 +01:00
Christian Schabesberger
a1db3187cd Merge branch 'dev' into ccc 2019-02-05 18:28:17 +01:00
Christian Schabesberger
488a3cf1ff Merge pull request #2084 from TeamNewPipe/splash
add splash screen
2019-02-05 18:27:15 +01:00
Christian Schabesberger
1253a620a4 Merge pull request #2094 from def14nt/add-instable-warning
Added a warning about instability
2019-02-05 18:27:02 +01:00
Christian Schabesberger
64547fc4a7 remove splash for older android 2019-02-05 18:24:34 +01:00
Emil Tang Kristensen
d643d140cf Added translation using Weblate (Danish) 2019-02-04 20:57:57 +01:00
Ali Demirtas
222e2e3242 Translated using Weblate (Turkish)
Currently translated at 100.0% (441 of 441 strings)
2019-02-04 12:37:19 +01:00
Ali Demirtas
ea7b6daf26 Translated using Weblate (Turkish)
Currently translated at 100.0% (441 of 441 strings)
2019-02-04 02:10:01 +01:00
DefiantDog66
14cfab495d Added a warning about instability
This is a follow up to TeamNewPipe#2069. I've removed the "WARNING" from the second one as it looks a bit silly to have the same thing twice, when the user is already attracted to the first one, which means they will most probably read the second warning.

I can revert this if you don't like it.
2019-02-03 19:47:51 +00:00
Alec Holmes
ed4b4a1a3c Updated search fragment to be amazon fire tv friendly 2019-02-01 13:02:28 +00:00
Hosted Weblate
511c552188 Merge branch 'origin/dev' into Weblate. 2019-02-01 09:37:22 +01:00
ssantos
8c650219eb Translated using Weblate (Portuguese)
Currently translated at 100.0% (441 of 441 strings)
2019-02-01 09:37:22 +01:00
WaldiS
189b779abe Translated using Weblate (Polish)
Currently translated at 100.0% (441 of 441 strings)
2019-02-01 09:37:18 +01:00
Michael Moroni
c26074b184 Translated using Weblate (Italian)
Currently translated at 100.0% (441 of 441 strings)
2019-02-01 09:37:16 +01:00
Emanuele Petriglia
6c70779ff1 Translated using Weblate (Italian)
Currently translated at 100.0% (441 of 441 strings)
2019-02-01 09:37:15 +01:00
Yaron Shahrabani
73897481ac Translated using Weblate (Hebrew)
Currently translated at 83.7% (369 of 441 strings)
2019-02-01 09:37:11 +01:00
C. Rüdinger
a62c523ed5 Translated using Weblate (German)
Currently translated at 100.0% (441 of 441 strings)
2019-02-01 09:37:08 +01:00
Florian
0f46f57b52 Translated using Weblate (French)
Currently translated at 89.8% (396 of 441 strings)
2019-02-01 09:37:08 +01:00
Nathan Follens
bc4d65a20c Translated using Weblate (Dutch)
Currently translated at 86.4% (381 of 441 strings)
2019-02-01 09:37:07 +01:00
Vojtěch Šamla
f1e9a44ad9 Translated using Weblate (Czech)
Currently translated at 88.4% (390 of 441 strings)
2019-02-01 09:37:06 +01:00
Marc Riera
61972c4719 Translated using Weblate (Catalan)
Currently translated at 92.1% (406 of 441 strings)
2019-02-01 09:37:05 +01:00
Almin Agic
9183c9c220 Translated using Weblate (Bosnian (latin))
Currently translated at 4.3% (19 of 441 strings)
2019-02-01 09:37:03 +01:00
Rex_sa
1ce59f13ff Translated using Weblate (Arabic)
Currently translated at 87.8% (387 of 441 strings)
2019-02-01 09:37:00 +01:00
Christian Schabesberger
1bac5db6d5 add splash 2019-01-31 18:13:00 +01:00
Christian Schabesberger
58f2c4d8e6 Merge branch 'dev' into ccc 2019-01-31 16:48:22 +01:00
Robin
2c2c61b2fc Add Artist and Duration to MediaDescription 2019-01-31 16:47:33 +01:00
Christian Schabesberger
98b04a4a83 Merge branch 'dev' into ccc 2019-01-31 14:28:10 +01:00
Christian Schabesberger
3ac64c44f4 Merge pull request #1900 from anoymouserver/patch-1
support for invidio.us links
2019-01-31 14:10:10 +01:00
Christian Schabesberger
3375847302 Merge branch 'dev' into patch-1 2019-01-31 14:02:24 +01:00
Christian Schabesberger
14043c86f5 fix backstack issue with mediaccc 2019-01-31 13:24:02 +01:00
Jeff Huang
030117780a Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (441 of 441 strings)
2019-01-31 08:57:07 +01:00
Benedikt Freisen
971c9fe5a1 Translated using Weblate (German)
Currently translated at 100.0% (441 of 441 strings)
2019-01-30 08:49:53 +01:00
Ritvik Saraf
77c6d3d576 merged upstream/dev 2019-01-29 22:32:58 +05:30
Christian Schabesberger
6edbfe2a6f add content filter to mediaccc 2019-01-29 17:20:30 +01:00
Christian Schabesberger
d0a3125df4 fox ogg 2019-01-29 16:13:46 +01:00
Christian Schabesberger
d8c76d4c21 add conferences 2019-01-29 15:39:18 +01:00
Christian Schabesberger
a1cc0897df make frontend not crash on scrolling on ccc search 2019-01-29 15:39:18 +01:00
Christian Schabesberger
e88a90f242 add theming to mediaccc 2019-01-29 15:39:18 +01:00
Christian Schabesberger
1ae54f6f8c further compatiblity fix for meadic.ccc 2019-01-29 15:39:18 +01:00
Hosted Weblate
8a4cb484fa Merge branch 'origin/dev' into Weblate. 2019-01-29 14:17:45 +01:00
Licaon Kter
93defbf341 Translated using Weblate (Romanian)
Currently translated at 84.1% (322 of 383 strings)
2019-01-29 14:17:45 +01:00
Allan Nordhøy
ca1089da9a Translated using Weblate (Norwegian Bokmål)
Currently translated at 97.7% (374 of 383 strings)
2019-01-29 14:17:44 +01:00
Rex_sa
dd07c7db0e Translated using Weblate (Arabic)
Currently translated at 100.0% (383 of 383 strings)
2019-01-29 14:17:42 +01:00
Almin Agic
dfe932e37a Added translation using Weblate (Bosnian (latin)) 2019-01-29 14:17:38 +01:00
Christian Schabesberger
568cf9c259 Merge pull request #2067 from TeamNewPipe/release_v0.15.1
release v0.15.1
2019-01-29 13:40:15 +01:00
Christian Schabesberger
b4ef20e785 fix failing tests 2019-01-29 13:38:38 +01:00
Christian Schabesberger
3a56dabf42 fix exception on viewing downloads 2019-01-29 13:23:39 +01:00
anoy
3ac4899b96 Merge branch 'dev' into patch-1 2019-01-28 21:19:21 +01:00
Christian Schabesberger
b7b228d9ce merge weblate 2019-01-27 22:00:00 +01:00
Laura Arjona Reina
45339fd6d2 Translated using Weblate (Spanish)
Currently translated at 100.0% (383 of 383 strings)
2019-01-27 17:26:10 +01:00
nautilusx
1856a5a82f Translated using Weblate (German)
Currently translated at 100.0% (383 of 383 strings)
2019-01-27 17:26:02 +01:00
Christian Schabesberger
b2aa703b62 add changelog for version v0.15.1 2019-01-27 16:57:56 +01:00
Christian Schabesberger
b31897d65d Merge pull request #2066 from TeamNewPipe/fix_icons
fix icon messup
2019-01-27 16:48:01 +01:00
Christian Schabesberger
a92f776ebe fix icon messup 2019-01-27 16:46:44 +01:00
Christian Schabesberger
b84ed675f5 Merge pull request #2062 from kapodamy/anoth-buggy-giga-fix
Downloads: add null checks
2019-01-25 22:21:28 +01:00
kapodamy
21eb98a52c add null check
add checks for null. This happens after rotating the screen while is turned off
2019-01-24 23:23:30 -03:00
qazaxtan
75dd8d492b Translated using Weblate (Romanian)
Currently translated at 84.1% (322 of 383 strings)
2019-01-23 18:05:27 +01:00
Christian Schabesberger
338893ded4 fix merge conflict 2019-01-23 16:12:21 +01:00
Christian Schabesberger
b459900040 Merge branch 'fix_download' into dev 2019-01-23 14:14:49 +01:00
Christian Schabesberger
d31300f4f9 Merge pull request #2048 from gkeegan/dev
Add updating section to README
2019-01-23 12:54:52 +01:00
kapodamy
f2285c0b19 MP4 muxer +misc modifications
* allow retry downloads with "post-processing failed" error in the new muxer
* MPEG-4 muxer  ¡¡ no DASH output!!
* keep the progress if download fails
* remove TODO in SecondaryStreamHelper.java
* misc clean-up
* delete TestAlgo.java
* delete ExtSDDownloadFailedActivity.java and remove it from AndroidManifest.xml
* use hardcored version for changing icon colors
2019-01-22 18:53:31 -03:00
kapodamy
684cb81974 Update MissionsFragment.java
work-around for reading the current theme icons
2019-01-22 18:53:31 -03:00
kapodamy
9db272f30e Update MissionsFragment.java
use another way to get the "Context"
2019-01-22 18:53:31 -03:00
kapodamy
c4a5e8dc86 misc fixes
* add null checks before resuming a download
* (MissionAdapter.java) reset percent after resuming a download. prevents the "Error" string get stuck, until the download start
2019-01-22 18:53:30 -03:00
kapodamy
df4dd0122f Update missions_header.xml
make the separator line """"""""simetric""""""""""
2019-01-22 18:53:30 -03:00
kapodamy
8fed18b2ac Update MissionAdapter.java
* check if the iterator is initialized
2019-01-22 18:53:30 -03:00
kapodamy
ecabbb57e6 Update download_menu.xml
* hide clear button by default
2019-01-22 18:53:30 -03:00
kapodamy
8d1d4092aa add missing icons in bright theme
* missing white icons
* update attrs.xml and styles.xml
2019-01-22 18:53:30 -03:00
kapodamy
6185c4ddcf delete list and grid icons
* delete all grid.png files
* delete all list.png files
2019-01-22 18:53:29 -03:00
SiD ViCiO
3ccbbccd10 Translated using Weblate (Spanish)
Currently translated at 91.9% (352 of 383 strings)
2019-01-22 02:53:19 +01:00
YONGLE
30e0ccc77b Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (383 of 383 strings)
2019-01-22 02:53:18 +01:00
thami simo
50f7b72b09 Translated using Weblate (Arabic)
Currently translated at 93.2% (357 of 383 strings)
2019-01-22 02:52:58 +01:00
qazaxtan
a5828c7949 Translated using Weblate (Romanian)
Currently translated at 72.1% (276 of 383 strings)
2019-01-22 02:52:55 +01:00
Tobias Groza
22a5e72470 Translated using Weblate (Romanian)
Currently translated at 72.1% (276 of 383 strings)
2019-01-22 02:52:54 +01:00
Keegan Given
5d14dca818 Add link to #1981 2019-01-21 22:21:24 +00:00
Keegan Given
f97cb821f8 Update README.md
Move Updates underneath Coming Features. Make it linkable.
2019-01-21 21:55:45 +00:00
Keegan Given
d02dce5562 Add updating section to README
This will help cull the number of "how to update?" Issues whenever we need to push out a fix, like how recently NewPipe got completely broken.

This attempts to be an all-inclusive guide to updating NewPipe. If there are any (clean/official) links to additional info, please comment them and I will add it.

Also let me know if this section should be relocated within the README. I figured it would be best to keep it up top, so people see it right away, and then don't make a new Issue.
2019-01-21 02:35:39 +00:00
Christian Schabesberger
c6bd42843b Merge pull request #2040 from TeamNewPipe/fix_livestream
Fix livestream
2019-01-20 14:24:53 +01:00
Christian Schabesberger
43e4fbfcd0 fix livestream issue, and move on to v0.15.1 2019-01-20 14:23:41 +01:00
Igor Nedoboy
ac8430cbba Translated using Weblate (Russian)
Currently translated at 100.0% (383 of 383 strings)
2019-01-20 00:22:24 +01:00
Ritvik Saraf
86d619be30 merged upstream/dev 2019-01-19 15:48:05 +05:30
Ritvik Saraf
fec7672598 updated extractor for url decryption fix 2019-01-19 15:26:31 +05:30
Roberto Palenga
44d2744a8c Translated using Weblate (Italian)
Currently translated at 100.0% (383 of 383 strings)
2019-01-19 01:20:51 +01:00
philSism
22bfbe96c8 Translated using Weblate (Greek)
Currently translated at 100.0% (383 of 383 strings)
2019-01-19 01:20:51 +01:00
Christian Schabesberger
c5d1271894 Merge pull request #2022 from TeamNewPipe/dev
release_v0.15.0
2019-01-18 21:18:10 +01:00
Christian Schabesberger
050d3058f9 Merge pull request #2021 from TeamNewPipe/release_v0.15.0
add changelog for version v0.15.0
2019-01-18 21:16:38 +01:00
Christian Schabesberger
2ae99afa21 add changelog for version v0.15.0 2019-01-18 21:13:04 +01:00
Christian Schabesberger
b094c9190f Merge pull request #2020 from TeamNewPipe/release_v0.15.0
Release v0.15.0
2019-01-18 20:54:52 +01:00
Christian Schabesberger
1a9e1e6f7c Merge branch 'dev' into release_v0.15.0 2019-01-18 20:50:46 +01:00
Christian Schabesberger
8c94926693 move on to version 0.15.0 2019-01-18 20:46:50 +01:00
Christian Schabesberger
880a176e65 move on to version 0.15.0 2019-01-18 20:34:43 +01:00
Christian Schabesberger
8de1b5f3d9 Merge pull request #2014 from TeamNewPipe/fix_decrypt
fix decrypt error once again
2019-01-18 17:23:38 +01:00
Christian Schabesberger
178f4546fc Merge branch 'dev' into fix_decrypt 2019-01-18 17:13:49 +01:00
Christian Schabesberger
7564a49344 fix decrypt error once again 2019-01-18 17:11:31 +01:00
Edwar Tikhonov
6c5c42c2b5 Translated using Weblate (Russian)
Currently translated at 100.0% (383 of 383 strings)
2019-01-15 19:20:55 +01:00
naofum
b1653b359e Translated using Weblate (Japanese)
Currently translated at 84.3% (323 of 383 strings)
2019-01-15 19:20:52 +01:00
DeadMetaler
11098afab5 Translated using Weblate (Belarusian)
Currently translated at 86.7% (332 of 383 strings)
2019-01-13 00:19:37 +01:00
AppSoft4
a64051e0f1 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (383 of 383 strings)
2019-01-12 21:06:58 +01:00
Nathan Follens
db8ac4a9ae Translated using Weblate (Flemish)
Currently translated at 100.0% (383 of 383 strings)
2019-01-12 21:06:57 +01:00
Nathan Follens
890474a635 Translated using Weblate (Dutch)
Currently translated at 100.0% (383 of 383 strings)
2019-01-12 21:06:54 +01:00
Tobias Groza
c7941a85ed Merge pull request #1999 from thisBrian/patch-1
Correct 'description' spelling
2019-01-11 17:15:07 +01:00
Brian
fab5f26e09 Correct 'description' spelling 2019-01-12 04:41:16 +13:00
zmni
a39b10eee9 Translated using Weblate (Indonesian)
Currently translated at 100.0% (383 of 383 strings)
2019-01-08 19:07:15 +01:00
srkrishna
97b933a990 Translated using Weblate (Tamil)
Currently translated at 40.5% (155 of 383 strings)
2019-01-07 15:06:58 +01:00
Sérgio Marques
4926e90514 Translated using Weblate (Portuguese)
Currently translated at 100.0% (383 of 383 strings)
2019-01-07 15:06:58 +01:00
zmni
6dca975844 Translated using Weblate (Indonesian)
Currently translated at 100.0% (383 of 383 strings)
2019-01-04 12:07:00 +01:00
zelos-h
e31743770a Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (383 of 383 strings)
2019-01-04 12:06:59 +01:00
Markel @wakutiteo
d8aab62d75 Translated using Weblate (Spanish)
Currently translated at 90.9% (348 of 383 strings)
2019-01-03 00:48:39 +01:00
Jeff Huang
f90603a18f Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (383 of 383 strings)
2019-01-03 00:48:38 +01:00
Marc Riera
5fce9facbe Translated using Weblate (Catalan)
Currently translated at 99.7% (382 of 383 strings)
2019-01-03 00:48:37 +01:00
zelos-h
32b48d5cdb Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (383 of 383 strings)
2019-01-03 00:48:36 +01:00
Jeff Huang
3e4f0d682b Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (383 of 383 strings)
2019-01-03 00:48:35 +01:00
Yaron Shahrabani
98449ddfe0 Translated using Weblate (Hebrew)
Currently translated at 88.5% (339 of 383 strings)
2019-01-01 10:09:41 +01:00
WaldiS
26040d4e9f Translated using Weblate (Polish)
Currently translated at 100.0% (383 of 383 strings)
2018-12-30 19:03:39 +01:00
srkrishna
83cea5e1ee Translated using Weblate (Tamil)
Currently translated at 39.7% (152 of 383 strings)
2018-12-29 00:09:42 +01:00
postsorino
3e73a5dbd3 Translated using Weblate (Greek)
Currently translated at 99.5% (381 of 383 strings)
2018-12-29 00:09:41 +01:00
Tobias Groza
dd424a4cb3 Translated using Weblate (Greek)
Currently translated at 99.5% (381 of 383 strings)
2018-12-29 00:09:38 +01:00
Éfrit
0c79f5cce3 Translated using Weblate (French)
Currently translated at 100.0% (383 of 383 strings)
2018-12-29 00:09:37 +01:00
krtkush
794c3703e5 Merge branch '1520_app_update_notif' of https://github.com/krtkush/NewPipe into 1520_app_update_notif 2018-12-28 18:09:05 +05:30
krtkush
b674006fcc Conflict resolution. 2018-12-28 18:07:54 +05:30
Ritvik Saraf
559c397b2f more NPE fix 2018-12-25 20:17:56 +05:30
Ritvik Saraf
646698f1ed fixed NPE in soundcloud 2018-12-25 19:59:03 +05:30
Ritvik Saraf
c9b938ae55 readded animations 2018-12-25 15:36:15 +05:30
Ritvik Saraf
990c220fa0 updated extractor 2018-12-23 09:44:42 +05:30
Yehuda Levy
38641d4edf Translated using Weblate (Hebrew)
Currently translated at 88.5% (339 of 383 strings)
2018-12-23 02:09:26 +01:00
srkrishna
450072bd23 Translated using Weblate (Tamil)
Currently translated at 39.4% (151 of 383 strings)
2018-12-21 22:09:14 +01:00
srkrishna
25eb93fae0 Translated using Weblate (Tamil)
Currently translated at 37.1% (142 of 383 strings)
2018-12-20 14:09:13 +01:00
Ben De Meester
bcf6f60571 Translated using Weblate (Flemish)
Currently translated at 85.4% (327 of 383 strings)
2018-12-20 14:09:11 +01:00
Ben De Meester
2076b8f1d7 Translated using Weblate (Dutch)
Currently translated at 100.0% (383 of 383 strings)
2018-12-20 14:09:10 +01:00
Ritvik Saraf
f19cfb75e6 merged upstream/dev 2018-12-20 08:51:44 +05:30
Ritvik Saraf
ceaacc771d removed jerky animations 2018-12-19 10:58:59 +05:30
Ritvik Saraf
48067e3285 up next text alignment 2018-12-17 10:03:04 +05:30
sXp
003855a94c Translated using Weblate (Hindi)
Currently translated at 85.4% (327 of 383 strings)
2018-12-15 19:03:36 +01:00
Christian Schabesberger
3599ab3caf Merge pull request #1759 from kapodamy/giga-postprocessing
Fucking travis
2018-12-11 16:37:28 +01:00
srkrishna
e7a26b436d Translated using Weblate (Tamil)
Currently translated at 32.9% (126 of 383 strings)
2018-12-10 19:42:55 +01:00
AB
ab94bc18dc Translated using Weblate (Ukrainian)
Currently translated at 100.0% (383 of 383 strings)
2018-12-10 13:08:54 +01:00
Eduardo Caron
36e91ea155 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (383 of 383 strings)
2018-12-10 13:08:53 +01:00
Christian Schabesberger
6035be9ce6 Merge branch 'dev' into giga-postprocessing 2018-12-10 12:12:38 +01:00
Kartikey Kushwaha
67499bdc65 Merge branch 'dev' into 1520_app_update_notif 2018-12-09 18:43:20 +05:30
Ritvik Saraf
222c8fdb62 tablet ui support for comments 2018-12-09 03:21:55 +05:30
srkrishna
9d648bad51 Translated using Weblate (Tamil)
Currently translated at 30.3% (116 of 383 strings)
2018-12-08 22:08:52 +01:00
Ritvik Saraf
1a62b9a161 removed dislike button, added comment published time 2018-12-08 20:32:28 +05:30
Echelon Arpa
33021e8fe0 Translated using Weblate (Turkish)
Currently translated at 100.0% (383 of 383 strings)
2018-12-07 21:09:01 +01:00
Emin Tufan Çetin
5a2ae4c3e3 Translated using Weblate (Turkish)
Currently translated at 100.0% (383 of 383 strings)
2018-12-07 21:09:01 +01:00
srkrishna
9eaef84cc8 Translated using Weblate (Tamil)
Currently translated at 29.8% (114 of 383 strings)
2018-12-07 21:09:00 +01:00
Rintaro matsuo
fcc4d655f5 Translated using Weblate (Japanese)
Currently translated at 81.5% (312 of 383 strings)
2018-12-07 21:08:59 +01:00
ScratchBuild
8d0ac4f5f0 Translated using Weblate (Japanese)
Currently translated at 81.5% (312 of 383 strings)
2018-12-07 21:08:58 +01:00
Tobias Groza
d56cf003f8 Translated using Weblate (German)
Currently translated at 100.0% (383 of 383 strings)
2018-12-07 21:08:58 +01:00
Kristjan Räts
34d0c0b1ba Translated using Weblate (Estonian)
Currently translated at 100.0% (383 of 383 strings)
2018-12-07 21:08:56 +01:00
Vojtěch Šamla
9bbbffbe7a Translated using Weblate (Czech)
Currently translated at 100.0% (383 of 383 strings)
2018-12-07 21:08:55 +01:00
Tobias Groza
ccd0f7d9cc Merge pull request #1878 from shivanju/auto_queue_logic
#1336 Fix for inserting new streams when auto queuing is enabled
2018-12-07 19:43:41 +01:00
Ritvik Saraf
ccb9bceecc removed unused imports 2018-12-07 08:42:05 +05:30
Ritvik Saraf
c1a67ff1f8 minor scrolling fix and ellipsize fix 2018-12-07 06:45:33 +05:30
Kartikey Kushwaha
bfda8dcc02 Merge branch 'dev' into 1520_app_update_notif 2018-12-05 13:04:24 +05:30
kapodamy
8746e7c9ad Merge branch 'dev' into giga-postprocessing 2018-12-05 01:15:39 -03:00
kapodamy
e2aa36d083 fast download pausing
* fast download pausing
* fix UI thread blocking when calling pause()
* check running threads before start the download
* fix null pointer exception in onDestroy in the download service, without calling onCreate method (android 8)
2018-12-05 01:07:59 -03:00
Ritvik Saraf
ff90f257cc removed useless log statement 2018-12-04 23:37:02 +05:30
Ritvik Saraf
9b84046865 merged upstream/dev 2018-12-04 23:19:57 +05:30
Shivanju Awasthi
51434a39f8 Merge branch 'dev' into auto_queue_logic 2018-12-04 16:22:18 +00:00
Christian Schabesberger
3e2031be7c Merge pull request #1925 from TeamNewPipe/stale
disable stalebot again
2018-12-03 23:30:58 +01:00
Christian Schabesberger
ea4e8805b7 disable stalebot again 2018-12-03 23:30:28 +01:00
Tobias Groza
f62bfeac08 Merge branch 'dev' into 1520_app_update_notif 2018-12-03 16:33:46 +01:00
Tobias Groza
b9042c37f9 Merge pull request #1915 from agarwalakash06/issue-1505
Issue 1505: Delete on right swipe
2018-12-03 16:06:27 +01:00
srkrishna
49a58dcab1 Translated using Weblate (Tamil)
Currently translated at 29.0% (111 of 383 strings)
2018-12-02 16:08:42 +01:00
notramo
e2acbeddc2 Translated using Weblate (Hungarian)
Currently translated at 84.9% (325 of 383 strings)
2018-12-02 16:08:41 +01:00
Kristjan Räts
449b17d830 Translated using Weblate (Estonian)
Currently translated at 86.4% (331 of 383 strings)
2018-12-02 16:08:39 +01:00
Kartikey Kushwaha
7ed460ce02 Merge branch 'dev' into 1520_app_update_notif 2018-12-02 18:07:08 +05:30
kapodamy
9f4a7e664f more of the same
* misc code clean-up
* fix weird download speed, before switching the list view
* fix CircularFile.java getting stuck on post-processing huge files >2GiB
* keep crashed post-processing downloads visible to the user
2018-12-01 22:14:56 -03:00
Akash Agarwal
38eb75b13f Merge branch 'dev' into issue-1505 2018-12-01 14:12:28 +05:30
Christian Schabesberger
0ad56874b4 Merge pull request #1918 from TeamNewPipe/stale
add stalebot confic
2018-12-01 09:36:45 +01:00
Christian Schabesberger
c1168693fa add stalebot confic 2018-12-01 09:35:42 +01:00
Christian Schabesberger
feb8c27f1f Merge branch 'dev' into giga-postprocessing 2018-12-01 09:30:38 +01:00
Christian Schabesberger
c20ebd66e5 Merge branch 'dev' into auto_queue_logic 2018-12-01 09:29:25 +01:00
Christian Schabesberger
4d78d530dc Merge pull request #1917 from TeamNewPipe/cont
update contribution guidelines
2018-12-01 09:23:58 +01:00
Christian Schabesberger
22b20c15de update contribution guidelines 2018-12-01 09:21:57 +01:00
Akash Agarwal
c69b224c65 Issue 1505: Delete on right swipe 2018-12-01 00:42:56 +05:30
Christian Schabesberger
805d328d6c Merge pull request #1902 from Ping20002015/master
Fix NPE for issue #1901
2018-11-30 12:08:59 +01:00
mahmut özcan
edcb692f78 Translated using Weblate (Turkish)
Currently translated at 97.1% (372 of 383 strings)
2018-11-29 22:08:34 +01:00
ask6155
f33586f062 Translated using Weblate (Hindi)
Currently translated at 81.5% (312 of 383 strings)
2018-11-29 22:08:30 +01:00
kapodamy
b8293f134d Update settings_keys.xml
* remane max_try -> maximum_try
2018-11-29 15:19:53 -03:00
kapodamy
7e9bcff0f3 Merge branch 'dev' into giga-postprocessing 2018-11-28 22:53:29 -03:00
kapodamy
eba3b32708 misc improvements
* don't show notifications while download activity
* proper icon in failed download notifications
* re-write list auto-refresh (MissionAdapter.java)
* improve I/O performance (CircularFile.java)
* fix implementation of "save thread position" on multi-thread downloads
2018-11-28 22:24:52 -03:00
Olexandr Nesterenko
d61bf26e17 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (383 of 383 strings)
2018-11-28 15:17:37 +01:00
AB
2e662b5745 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (383 of 383 strings)
2018-11-28 15:17:36 +01:00
Helios
9714fa369b Translated using Weblate (Tamil)
Currently translated at 28.7% (110 of 383 strings)
2018-11-28 15:17:34 +01:00
Markel @wakutiteo
0e8acba08e Translated using Weblate (Spanish)
Currently translated at 88.3% (338 of 383 strings)
2018-11-28 15:17:28 +01:00
Sérgio Marques
a18e588e55 Translated using Weblate (Portuguese)
Currently translated at 100.0% (383 of 383 strings)
2018-11-28 15:17:27 +01:00
ask6155
bf55e3c0cc Translated using Weblate (Hindi)
Currently translated at 79.6% (305 of 383 strings)
2018-11-28 15:17:20 +01:00
ButterflyOfFire
486d114e64 Translated using Weblate (Arabic)
Currently translated at 85.9% (329 of 383 strings)
2018-11-28 15:17:19 +01:00
Prabjot Singh
ac5c060a98 Translated using Weblate (Hindi)
Currently translated at 79.6% (305 of 383 strings)
2018-11-28 15:17:18 +01:00
ask6155
86a9d197cb Translated using Weblate (Hindi)
Currently translated at 79.6% (305 of 383 strings)
2018-11-28 15:17:17 +01:00
Shivanju Awasthi
e911dbb9d4 Merge branch 'dev' into auto_queue_logic 2018-11-26 22:56:01 +05:30
Ping20002015
e26d123f67 Merge branch 'dev' into master 2018-11-26 18:00:22 +01:00
MadderRagax
70aac81900 Translated using Weblate (Swedish)
Currently translated at 98.4% (377 of 383 strings)
2018-11-26 14:42:04 +01:00
Eduardo Caron
28e78d98f6 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (383 of 383 strings)
2018-11-26 14:42:02 +01:00
Mostafa Ahangarha
fabc5ae032 Translated using Weblate (Persian)
Currently translated at 62.4% (239 of 383 strings)
2018-11-26 14:42:01 +01:00
ditokp
e40882455d Translated using Weblate (Indonesian)
Currently translated at 99.7% (382 of 383 strings)
2018-11-26 14:41:54 +01:00
Whod
f1483e8c8e Translated using Weblate (Bulgarian)
Currently translated at 100.0% (383 of 383 strings)
2018-11-26 14:41:51 +01:00
Viktar Vauchkevich
bd40e2c3ff Translated using Weblate (Belarusian)
Currently translated at 86.7% (332 of 383 strings)
2018-11-26 14:41:49 +01:00
Gontzal Manuel Pujana Onaindia
9479498713 Translated using Weblate (Basque)
Currently translated at 100.0% (383 of 383 strings)
2018-11-26 14:41:49 +01:00
ssantos
6bd8523ec2 Translated using Weblate (Portuguese)
Currently translated at 100.0% (383 of 383 strings)
2018-11-26 14:41:46 +01:00
Christian Schabesberger
22e3cddd91 Merge pull request #1884 from TeamNewPipe/remove_4.x
Remove support for android 4.1 - 4.3
2018-11-25 22:49:13 +01:00
krtkush
c864b15c34 Test code revert. 2018-11-26 01:18:33 +05:30
krtkush
069654c5f9 vector -> png 2018-11-26 01:18:02 +05:30
kapodamy
f3d4d4747a and more fixes
* fix content length reading
* use float overflow. Expensive, double is used instead
* fix invalid cast after click the mission body
* use a list for maximum attemps (downloads)
* minor clean up (DownloadManager.java)
* dont pass SharedPreferences instace to DownloadManager
* use a switch instead of checkbox for cross_network_downloads
* notify media scanner after deleting a finished download
2018-11-24 20:13:18 -03:00
shivanju
5bbb0cd666 issue:1336 Remove auto queued stream if a new stream gets appended 2018-11-24 17:35:17 +05:30
Ping20002015
7b5ba3bdc2 Fix NPE for issue #1901 2018-11-23 19:38:01 +01:00
anoy
5ac17ee6b8 added support for invidio.us links 2018-11-23 19:18:34 +01:00
kapodamy
d647555e3a more fixes
* use bold style in status (mission_item_linear.xml)
* fix download attemps not begin updated
* dont stop the queue if a download fails
* implement partial wake-lock & wifi-lock
* show notifications for failed downloads
* ¿proper bitmap dispose? (DownloadManagerService.java)
* improve buffer filling (CircularFile.java)
* [Mp4Dash] increment reserved space from 2MiB to 15MiB. This is expensive but useful for devices with low ram
* [WebM] use 2MiB of reserved space
* fix debug warning if one thread is used
* fix wrong download speed when the activity is suspended
* Fix "Queue" menu item that appears in post-processing errors
* fix mission length dont being updated (missing commit)
2018-11-22 23:33:34 -03:00
krtkush
26e22f97ee Conflict resolution 2018-11-23 01:41:47 +05:30
Shivanju Awasthi
84976a65e0 Merge branch 'dev' into auto_queue_logic 2018-11-22 23:25:13 +05:30
kapodamy
fef9d541ed misc fixes
* use getPreferredLocalization() instead of getLocalization()
* use lastest commit in build.gradle
* fix missing cast in MissionAdapter.java
2018-11-19 15:50:15 -03:00
krtkush
ad5535af81 Code refactoring, PR changes. 2018-11-19 23:27:13 +05:30
Yaron Shahrabani
cdcfb4ffce Translated using Weblate (Hebrew)
Currently translated at 78.9% (302 of 383 strings)
2018-11-18 21:40:36 +01:00
b1a6b64a90
5403ac8893 Translated using Weblate (French)
Currently translated at 99.5% (381 of 383 strings)
2018-11-18 21:40:35 +01:00
Igor Nedoboy
9b26457781 Translated using Weblate (Russian)
Currently translated at 100.0% (383 of 383 strings)
2018-11-18 21:40:33 +01:00
krtkush
939cc56951 Pull request changes v2. 2018-11-18 19:18:16 +05:30
krtkush
23309e6fdf Pull request changes. 2018-11-18 19:15:50 +05:30
MadderRagax
3f60c961d9 Translated using Weblate (Swedish)
Currently translated at 97.9% (375 of 383 strings)
2018-11-18 13:43:46 +01:00
Ariel Shulman
9e7f07e196 Translated using Weblate (Hebrew)
Currently translated at 78.9% (302 of 383 strings)
2018-11-18 13:43:45 +01:00
ssantos
eda4439ee8 Translated using Weblate (German)
Currently translated at 100.0% (383 of 383 strings)
2018-11-18 13:43:43 +01:00
b1a6b64a90
ad02558ade Translated using Weblate (French)
Currently translated at 99.5% (381 of 383 strings)
2018-11-18 13:43:38 +01:00
WaldiS
0d1901cfe5 Translated using Weblate (Polish)
Currently translated at 100.0% (383 of 383 strings)
2018-11-17 14:08:01 +01:00
Emanuele Petriglia
ce493d1ae2 Translated using Weblate (Italian)
Currently translated at 100.0% (383 of 383 strings)
2018-11-17 14:07:59 +01:00
wellinkstein
09312ecd1d Translated using Weblate (French)
Currently translated at 98.2% (376 of 383 strings)
2018-11-17 14:07:58 +01:00
ezjerry liao
2f274d5f52 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (383 of 383 strings)
2018-11-17 14:07:56 +01:00
Marc Riera
6dbf226365 Translated using Weblate (Catalan)
Currently translated at 99.5% (381 of 383 strings)
2018-11-17 14:07:54 +01:00
Viktar Vauchkevich
6401b5f54d Translated using Weblate (Belarusian)
Currently translated at 85.1% (326 of 383 strings)
2018-11-17 14:07:50 +01:00
beriain
ac2dd81d39 Translated using Weblate (Basque)
Currently translated at 85.1% (326 of 383 strings)
2018-11-17 14:07:50 +01:00
kapodamy
6784522195 Merge branch 'dev' into giga-postprocessing 2018-11-15 20:53:30 -03:00
kapodamy
f42d077f30 misc utils
Also this include:
* Mp4 DASH reader/writter
* WebM reader/writter
* a subtitle converter for Timed Text Markup Language v1 and TranScript (v1, v2 and v3)
* SharpStream to wrap IntputStream and OutputStream in one interface
* custom implementation of DataInputStream
2018-11-15 20:17:22 -03:00
Christian Schabesberger
eaca47ebc6 Merge branch 'dev' into remove_4.x 2018-11-15 13:27:27 +01:00
Tobias Groza
4d57223847 Merge pull request #1886 from nicholasfagan/dev
Fix broken screenshots in README.md
2018-11-15 08:20:02 +01:00
Nick Fagan
d9ab96236b Fix broken screenshots in README.md 2018-11-14 15:28:44 -04:00
AB
44b2e62eef Translated using Weblate (Ukrainian)
Currently translated at 84.9% (325 of 383 strings)
2018-11-13 20:43:45 +01:00
Igor Nedoboy
7d5d4df761 Translated using Weblate (Russian)
Currently translated at 100.0% (383 of 383 strings)
2018-11-13 20:43:41 +01:00
Mostafa Ahangarha
da7716db60 Translated using Weblate (Persian)
Currently translated at 35.0% (134 of 383 strings)
2018-11-13 20:43:39 +01:00
postsorino
862c5d342b Translated using Weblate (Greek)
Currently translated at 84.6% (324 of 383 strings)
2018-11-13 20:43:34 +01:00
Igor Nedoboy
6167d5dbfc Translated using Weblate (English)
Currently translated at 100.0% (383 of 383 strings)
2018-11-13 20:43:32 +01:00
Shivanju Awasthi
2856fb029f Merge branch 'dev' into auto_queue_logic 2018-11-14 00:17:54 +05:30
Christian Schabesberger
8fb945312a put autoplay next stream setting into behaviour page 2018-11-13 17:30:04 +01:00
Christian Schabesberger
25d6806ebd set minSdk to 19 and deprecate old player 2018-11-13 17:27:47 +01:00
Christian Schabesberger
51070d9afd Merge pull request #1818 from TeamNewPipe/screenshot-order
Fix fastlane screenshot order
2018-11-13 16:58:37 +01:00
Christian Schabesberger
1048caa496 Merge branch 'dev' into screenshot-order 2018-11-13 16:58:18 +01:00
Christian Schabesberger
0cd7ac05aa Merge pull request #1879 from jludden/play_one_click
downloaded files can now be opened with one click
2018-11-13 13:43:28 +01:00
Christian Schabesberger
2793c42d91 Merge branch 'dev' into play_one_click 2018-11-13 13:18:57 +01:00
Christian Schabesberger
2eaa7288e4 Merge pull request #1876 from bboa/dev
Missed strings
2018-11-13 12:53:01 +01:00
ssantos
f9341bea79 Translated using Weblate (Portuguese)
Currently translated at 100.0% (383 of 383 strings)
2018-11-12 10:08:44 +01:00
jludden
0df8d13020 downloads can now be viewed with one click fixed 2018-11-11 22:25:37 +08:00
jludden
d27622de1e downloaded files can now be opened with one click
For consistency, I removed the view file option from the overflow menu as well
2018-11-11 19:54:35 +08:00
shivanju
47c3da131c issue:1336 Fix for inserting new streams when auto queuing is enabled 2018-11-11 16:24:49 +05:30
Igor Nedoboy
3a608bb582 Translated using Weblate (Russian)
Currently translated at 100.0% (383 of 383 strings)
2018-11-11 00:47:04 +01:00
Igor Nedoboy
64f9228ee3 Update strings.xml 2018-11-11 02:44:45 +03:00
Igor Nedoboy
c926482b3c Translated using Weblate (Russian)
Currently translated at 100.0% (383 of 383 strings)
2018-11-10 23:38:19 +01:00
Igor Nedoboy
9562972c42 Translated using Weblate (Russian)
Currently translated at 100.0% (383 of 383 strings)
2018-11-10 23:02:17 +01:00
Igor Nedoboy
b462b7fcc4 Update strings.xml 2018-11-10 17:08:04 +03:00
Igor Nedoboy
b34f9d7fd3 Update strings.xml 2018-11-10 03:06:55 +03:00
Igor Nedoboy
57732c3e5f Update strings.xml 2018-11-10 02:56:14 +03:00
kapodamy
eb1f56488f resbase (08/11/2018) 2018-11-08 22:00:13 -03:00
kapodamy
5825843f68 main commit
Post-processing infrastructure
* remove interfaces with one implementation
* fix download resources with unknow length
* marquee style for ProgressDrawable
* "view details" option in mission context menu
* notification for finished downloads
* postprocessing infrastructure: sub-missions, circular file, layers for layers of abstractions for Java IO streams
* Mp4 muxing (only DASH brand)
* WebM muxing
* Captions downloading
* alert dialog for overwrite existing downloads finished or not.

Misc changes
* delete SQLiteDownloadDataSource.java
* delete DownloadMissionSQLiteHelper.java
* implement Localization from #114

Misc fixes (this branch)
* restore old mission listeners variables. Prevents registered listeners get de-referenced on low-end devices
* DownloadManagerService.checkForRunningMission() now return false if the mission has a error.
* use Intent.FLAG_ACTIVITY_NEW_TASK when launching an activity from gigaget threads (apparently it is required in old versions of android)

More changes
* proper error handling "infrastructure"
* queue instead of multiple downloads
* move serialized pending downloads (.giga files) to app data
* stop downloads when swicthing to mobile network (never works, see 2nd point)
* save the thread count for next downloads
* a lot of incoherences fixed
* delete DownloadManagerTest.java (too many changes to keep this file updated)
2018-11-08 19:00:44 -03:00
Андрій
dd56a5d869 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (383 of 383 strings)
2018-11-08 17:07:53 +01:00
Nitin Khanna
86f82c0e61 pop-up player crash fixed 2018-11-07 22:01:39 +05:30
rimasx
8ae45240b2 Translated using Weblate (Estonian)
Currently translated at 99.7% (382 of 383 strings)
2018-11-06 15:02:39 +01:00
WaldiS
6d845af7f1 Translated using Weblate (Polish)
Currently translated at 100.0% (383 of 383 strings)
2018-11-02 08:23:47 +01:00
Piotr Kaczmarek
733ebb8caf Translated using Weblate (Polish)
Currently translated at 100.0% (383 of 383 strings)
2018-11-02 08:23:47 +01:00
Max Mathys
695a194467 Translated using Weblate (Finnish)
Currently translated at 100.0% (383 of 383 strings)
2018-11-02 08:23:46 +01:00
simo
8a56257ade Translated using Weblate (Arabic)
Currently translated at 100.0% (383 of 383 strings)
2018-11-02 08:23:45 +01:00
Christian Schabesberger
45fea983b9 Merge pull request #1835 from ritiek/fix-res-crash
Fix crash with default resolution set to best and limited mobile data resolution
2018-11-01 20:22:44 +01:00
Ritiek Malhotra
642b499e70 Fix crash with default resolution best on mobile data 2018-10-31 21:44:12 +05:30
Christian Schabesberger
5c5575bb72 Merge branch 'dev' into screenshot-order 2018-10-31 10:26:31 +01:00
Dual Natan
4d5dc0d39c Translated using Weblate (Macedonian)
Currently translated at 100.0% (383 of 383 strings)
2018-10-29 19:23:30 +01:00
Christian Schabesberger
fba0a8036b Merge pull request #1825 from TeamNewPipe/release_0.14.2
Release 0.14.2
2018-10-27 09:58:56 +02:00
Christian Schabesberger
d82274f5d4 fucking android bullshit 2018-10-27 09:57:11 +02:00
Christian Schabesberger
3cf81230b2 Merge branch 'dev' into release_0.14.2 2018-10-26 15:02:11 +02:00
Christian Schabesberger
17bab456e4 Merge pull request #1845 from AndhikaWB/patch-1
Fix spelling of country names in settings
2018-10-26 15:01:34 +02:00
Christian Schabesberger
e1cc006db7 fix race condition when returning to main player 2018-10-26 14:59:49 +02:00
Tobias Groza
7e95dd3c76 Change from "Côte d'Ivoire" to "Côte d\'Ivoire"
Co-Authored-By: AndhikaWB <karagasnoelan@gmail.com>
2018-10-26 16:22:35 +07:00
AndhikaWB
ff3ce46a26 Update settings_keys.xml 2018-10-26 00:09:04 +07:00
AndhikaWB
4d61c2c5e0 Fix spelling in of country names in settings
- [x] I carefully read the [contribution guidelines](https://github.com/TeamNewPipe/NewPipe/blob/HEAD/.github/CONTRIBUTING.md) and agree to them.

Closes TeamNewPipe/NewPipe#1504
2018-10-26 00:04:36 +07:00
Eduardo Caron
8589ee14ff Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (383 of 383 strings)
2018-10-25 17:26:00 +02:00
Stjepan
1727387c9c Translated using Weblate (Croatian)
Currently translated at 86.9% (333 of 383 strings)
2018-10-25 17:25:58 +02:00
ezjerry liao
e1146e4655 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (383 of 383 strings)
2018-10-25 17:25:54 +02:00
Christian Schabesberger
afaba7ccdf fix broken search result 2018-10-25 15:58:06 +02:00
Christian Schabesberger
ccb3ceae4f add release node 2018-10-25 10:58:37 +02:00
Christian Schabesberger
d0d93f6d2d Merge branch 'dev' into release_0.14.2 2018-10-25 10:57:39 +02:00
Christian Schabesberger
67f091c731 Merge pull request #1830 from boredomdenied/dev
Add foreground service permission for API 28+ devices
2018-10-25 10:54:52 +02:00
krtkush
c29b0645bd Merge branch '1520_app_update_notif' of https://github.com/krtkush/NewPipe into 1520_app_update_notif 2018-10-22 23:12:51 +05:30
krtkush
96dac0f979 Code review suggested changes. 2018-10-22 23:12:25 +05:30
Tobias Groza
fda9b59129 Code review changes.
Co-Authored-By: krtkush <kartikey92@gmail.com>
2018-10-22 23:08:46 +05:30
Christian Schabesberger
fa3aebb7b1 localisation to localization 2018-10-22 12:25:50 +02:00
Christian Schabesberger
988251deb6 fix weblate collision 2018-10-22 12:15:16 +02:00
Christian Schabesberger
d99a389c49 merge weblate 2018-10-22 12:07:12 +02:00
Marian Hanzel
7508f9d3bb Translated using Weblate (Slovak)
Currently translated at 100.0% (383 of 383 strings)
2018-10-21 09:45:21 +02:00
Heimen Stoffels
b2512d7aee Translated using Weblate (Dutch)
Currently translated at 100.0% (383 of 383 strings)
2018-10-21 09:45:18 +02:00
Olexandr Nesterenko
eb74e1e3b2 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (383 of 383 strings)
2018-10-21 09:45:13 +02:00
boredomdenied
bd91e82b17 Update AndroidManifest.xml
remove needless additional line
2018-10-19 21:07:59 -05:00
boredomdenied
bae60acfe7 Update AndroidManifest.xml
Fix foreground playback in android Pie API 28. Tested in emulator & Pixel 3 device.
2018-10-19 20:52:22 -05:00
Emin Tufan Çetin
426cefe8ee Translated using Weblate (Turkish)
Currently translated at 100.0% (383 of 383 strings)
2018-10-20 01:33:46 +02:00
Allan Nordhøy
a36fe3ef21 Translated using Weblate (Swedish)
Currently translated at 100.0% (383 of 383 strings)
2018-10-20 01:33:46 +02:00
Allan Nordhøy
ee0756c7c0 Translated using Weblate (Russian)
Currently translated at 99.7% (382 of 383 strings)
2018-10-20 01:33:45 +02:00
Jaggie
44b96121e4 Translated using Weblate (Punjabi)
Currently translated at 7.3% (28 of 383 strings)
2018-10-20 01:33:44 +02:00
Allan Nordhøy
8add777b34 Translated using Weblate (German)
Currently translated at 100.0% (383 of 383 strings)
2018-10-20 01:33:42 +02:00
Allan Nordhøy
6932b15144 Translated using Weblate (English)
Currently translated at 100.0% (383 of 383 strings)
2018-10-20 01:33:41 +02:00
Allan Nordhøy
c09edda797 Translated using Weblate (Norwegian Bokmål)
Currently translated at 99.7% (382 of 383 strings)
2018-10-20 01:33:38 +02:00
Christian Schabesberger
0e86010565 move on to version 0.14.2 2018-10-19 15:53:19 +02:00
Ritvik Saraf
fa5896ee5b fixed screen rotation for viewpager 2018-10-19 18:44:03 +05:30
Tobias Groza
6fe3fdce11 Merge pull request #1828 from comradekingu/patch-8
Spelling and linguistic updates for readme
2018-10-19 11:00:39 +02:00
Allan Nordhøy
90769a12b1 Spelling and linguistic updates 2018-10-19 02:05:29 +02:00
Kartikey Kushwaha
3c6d27b504 Delete R.java 2018-10-18 23:04:07 +05:30
Kartikey Kushwaha
6ef25eb861 Delete Manifest.java 2018-10-18 23:03:21 +05:30
Kartikey Kushwaha
ec28e972bb Delete BuildConfig.java 2018-10-18 23:02:59 +05:30
Kartikey Kushwaha
d1a9033525 Delete R.java 2018-10-18 23:02:38 +05:30
Kartikey Kushwaha
2d5bc3ada8 Delete Manifest.java 2018-10-18 23:02:30 +05:30
Kartikey Kushwaha
506ffb9885 Delete BuildConfig.java 2018-10-18 23:02:19 +05:30
krtkush
8ef702fa07 Removed updates options from settings in case of non github apk. 2018-10-18 22:59:33 +05:30
Mostafa Ahangarha
1d49b725b6 Translated using Weblate (Persian)
Currently translated at 23.2% (89 of 383 strings)
2018-10-18 11:31:57 +02:00
Stjepan
204feb97ce Translated using Weblate (Croatian)
Currently translated at 86.9% (333 of 383 strings)
2018-10-18 11:31:56 +02:00
maa123
e12389a748 Translated using Weblate (Japanese)
Currently translated at 96.8% (371 of 383 strings)
2018-10-18 11:31:52 +02:00
Ritvik Saraf
9fc38b5bb8 improved fling behavior, added tab indicator dots, added next video in related videos 2018-10-17 00:23:02 +05:30
TobiGr
907e5a10d6 Rename screenshots to display them in correct order in the F-Droid app 2018-10-16 16:33:09 +02:00
Christian Schabesberger
670591e221 Merge pull request #1792 from TeamNewPipe/lang
Add support for Localization
2018-10-16 16:16:05 +02:00
Christian Schabesberger
8006a7d578 Merge branch 'dev' into lang 2018-10-16 16:01:32 +02:00
Christian Schabesberger
8b9078b82e Merge pull request #1801 from TacoTheDank/dev
simply update gradles to 28, AS 3.2.0, and a few dependencies
2018-10-16 16:00:44 +02:00
Christian Schabesberger
56d4edd3ae Merge branch 'dev' into dev 2018-10-16 03:47:29 +02:00
Christian Schabesberger
a7f5ea865e Merge branch 'dev' into lang 2018-10-16 03:47:11 +02:00
Christian Schabesberger
2a62144abd Merge pull request #1800 from TeamNewPipe/history-screenshot
Update screenshots and readme
2018-10-16 03:46:38 +02:00
Christian Schabesberger
c7ea7a4f65 Merge branch 'dev' into dev 2018-10-16 03:45:14 +02:00
TobiGr
7ea090ba8d Update video detail screenshot 2018-10-15 15:16:24 +02:00
TobiGr
9294e353d3 Add link to privacy policy to readme
Closes TeamNewPipe/NewPipe#1789
Part of TeamNewPipe/NewPipe#1817
2018-10-15 15:02:34 +02:00
TobiGr
71c50c58ee Move tablet screenshots for better F-Droid integration 2018-10-15 15:00:41 +02:00
TobiGr
e322299fda Update history screenshot 2018-10-15 14:52:07 +02:00
Ákos Surányi
06602a568a Translated using Weblate (Hungarian)
Currently translated at 90.8% (348 of 383 strings)
2018-10-14 21:31:53 +02:00
krtkush
54ac5e8940 Merge branch '1520_app_update_notif' of https://github.com/krtkush/NewPipe into 1520_app_update_notif 2018-10-14 19:16:59 +05:30
krtkush
e2341363d4 Added check for SHA1 key. 2018-10-14 19:16:28 +05:30
Tobias Groza
a0f0529ad8 Merge pull request #1810 from saderror256/patch-1
Fix broken bold
2018-10-14 15:01:24 +02:00
SadError256
c371d863d9 Fix broken bold
also removed useless backslash in html5
2018-10-13 18:20:47 -04:00
Ákos Surányi
16d8c75953 Translated using Weblate (Hungarian)
Currently translated at 41.5% (159 of 383 strings)
2018-10-13 17:20:53 +02:00
Toldi Balázs
fa48c0e76b Translated using Weblate (Hungarian)
Currently translated at 41.5% (159 of 383 strings)
2018-10-13 17:20:51 +02:00
Tobias Groza
2d17cfaf2b Merge pull request #1644 from ritiek/separate-gesture-options
Separate options for volume and brightness gestures
2018-10-11 17:41:34 +02:00
Matej U
f379de0a3a Translated using Weblate (Slovenian)
Currently translated at 62.6% (240 of 383 strings)
2018-10-11 11:34:26 +02:00
TacoTheDank
d3fcb0aa6a Update build.gradle 2018-10-10 14:29:26 -04:00
Ritiek Malhotra
046740f10b Merge branch 'dev' into separate-gesture-options 2018-10-10 08:41:42 -07:00
TacoTheDank
ffae2eda83 simply update gradles to 28, and some dependencies 2018-10-09 12:22:30 -04:00
Christian Schabesberger
8334b8fe46 Merge pull request #1798 from TeamNewPipe/screenshots
add tablet screenshot
2018-10-08 12:35:57 +02:00
Christian Schabesberger
c6c6e58d28 add tablet screenshot 2018-10-08 12:35:13 +02:00
Christian Schabesberger
2ff0d6665c Merge pull request #1617 from nv95/tablet_ui
Tablet ui and grid list layout
2018-10-08 12:15:02 +02:00
Christian Schabesberger
dc6108c970 Merge branch 'dev' into tablet_ui 2018-10-08 11:56:25 +02:00
Christian Schabesberger
6f52f9b4af Merge pull request #1796 from saderror256/patch-1
Update README.md
2018-10-08 11:39:31 +02:00
zelos-h
b6eb896f0b Translated using Weblate (Chinese (Traditional))
Currently translated at 99.7% (382 of 383 strings)
2018-10-08 10:57:24 +02:00
SadError256
adf861c36d Update README.md 2018-10-07 13:39:02 -04:00
Christian Schabesberger
4e1bcdf209 Merge pull request #1516 from Somethingweirdhere/LongTapInSubs
Long-tap delete and share in subscriptions
2018-10-07 14:08:09 +02:00
Christian Schabesberger
d107fe19f7 Merge branch 'dev' into LongTapInSubs 2018-10-07 13:59:39 +02:00
Christian Schabesberger
fa03ff51d4 Merge pull request #1651 from comradekingu/patch-7
Spelling: Language rework
2018-10-07 13:39:03 +02:00
Christian Schabesberger
f6c6536b48 Merge branch 'dev' into patch-7 2018-10-07 13:26:10 +02:00
Christian Schabesberger
38e4249182 Merge branch 'dev' into lang 2018-10-07 13:25:35 +02:00
Christian Schabesberger
2dd8c0beee Merge pull request #1773 from prg318/clarify-bookmarks
[Closes #1772] change 'tab_bookmarks' string to 'Bookmarked Playlists' for UI clarity
2018-10-06 18:16:38 +02:00
Christian Schabesberger
6a35494ef1 Merge branch 'dev' into patch-7 2018-10-06 18:16:10 +02:00
Christian Schabesberger
bf1c42e085 Merge branch 'dev' into clarify-bookmarks 2018-10-06 18:10:18 +02:00
Christian Schabesberger
f1aa3d8c90 Merge branch 'dev' into 1520_app_update_notif 2018-10-06 18:04:39 +02:00
Christian Schabesberger
0ef6bf8067 Merge pull request #1783 from coin3x/enqueue-autoplay
Enqueuing now triggers video playing if the play queue has already ended
2018-10-06 17:55:22 +02:00
Christian Schabesberger
9d63e2ae97 Merge branch 'dev' into enqueue-autoplay 2018-10-06 17:35:55 +02:00
Christian Schabesberger
fdd8060296 Merge branch 'dev' into lang 2018-10-06 17:29:42 +02:00
Christian Schabesberger
ebbb23ffbb fix bugs in country search 2018-10-06 17:18:19 +02:00
Christian Schabesberger
0e413109bd Merge pull request #1748 from MaX-Lo/store_last_aspect_ratio
store and reload the last used aspect ratio
2018-10-06 12:09:14 +02:00
Christian Schabesberger
f7e29c1e00 revert sdk upgrade 2018-10-06 12:06:03 +02:00
Christian Schabesberger
42f777d49a Merge branch 'dev' into clarify-bookmarks 2018-10-06 12:01:34 +02:00
Christian Schabesberger
efb8ea4c25 make local settings be live updated 2018-10-05 16:31:23 +02:00
Christian Schabesberger
52bf5690c0 add support for content language and content country 2018-10-05 16:20:27 +02:00
Jasmine Vyas
607dff9263 Translated using Weblate (Hindi)
Currently translated at 92.6% (355 of 383 strings)
2018-10-04 22:28:46 +02:00
Coin
0510db75fb Enqueuing now triggers video playing if the play queue has done playing 2018-10-03 00:31:28 +08:00
Ritvik Saraf
cf3e53eb71 update notify on dataset change 2018-10-02 21:30:11 +05:30
Ritvik Saraf
b8865e925d added content setting to disable comments 2018-10-02 20:56:14 +05:30
Ritvik Saraf
2e9a860aaa added viewpager. changed from parallaxscrollview to coordinate layout 2018-10-02 20:39:16 +05:30
MaX
859eecabc0 Merge branch 'dev' into store_last_aspect_ratio 2018-10-02 10:44:04 +02:00
MadderRagax
7a0253631b Translated using Weblate (Swedish)
Currently translated at 100.0% (383 of 383 strings)
2018-10-01 18:21:22 +02:00
ezjerry liao
c12a60c1ad Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (383 of 383 strings)
2018-10-01 18:21:21 +02:00
ButterflyOfFire
df9dbd6130 Translated using Weblate (Arabic)
Currently translated at 100.0% (383 of 383 strings)
2018-10-01 18:21:16 +02:00
Tobias Groza
a98e745116 Merge pull request #1771 from jludden/dev
Enable linear layout in Downloads activity with full video names
2018-10-01 17:40:40 +02:00
Lukas Sabota
61f1b10a06 Change 'tab_bookmarks' string to 'Bookmarked Playlists' for UI clarity 2018-09-30 11:34:16 -04:00
jludden
4bcb2a5c9d update linear layout
re-arrange elements and allow the video title to display over multiple lines
2018-09-30 07:42:02 +08:00
MaX
7e48648f9e Merge branch 'dev' into store_last_aspect_ratio 2018-09-29 12:45:10 +02:00
Ritvik Saraf
e4bef056e6 merged upstream/dev 2018-09-29 15:46:47 +05:30
jludden
bcc97d1aa7 Adding switch view button to downloads activity
Can now switch between linear and grid layouts in the downloads activity
2018-09-29 15:13:15 +08:00
Christian Schabesberger
49f9d36718 Merge pull request #1640 from BO41/code-clean
Code clean
2018-09-28 17:17:30 +02:00
O Passante
f9cb258a5e Translated using Weblate (Galician)
Currently translated at 100.0% (383 of 383 strings)
2018-09-28 11:26:41 +02:00
Ritvik Saraf
803b8eab28 updated extractor version 2018-09-28 05:04:36 +05:30
Ritvik Saraf
08bbfc50ee updated extractor 2018-09-27 23:27:52 +05:30
O Passante
97381a4908 Translated using Weblate (Galician)
Currently translated at 100.0% (383 of 383 strings)
2018-09-27 01:28:42 +02:00
Ritvik Saraf
4e6722f201 updated extractor and downloader 2018-09-27 04:20:57 +05:30
Ritvik Saraf
a29e2116a7 handling error while loading comments 2018-09-27 00:53:36 +05:30
RainSlide
7a15acc546 Translated using Weblate (Chinese (Simplified))
Currently translated at 29.2% (112 of 383 strings)
2018-09-25 18:13:16 +02:00
O Passante
0265de2137 Added translation using Weblate (Galician) 2018-09-25 18:13:14 +02:00
Nigel Ticknor
595b9910f5 Translated using Weblate (Japanese)
Currently translated at 93.9% (360 of 383 strings)
2018-09-25 14:22:36 +02:00
Heimen Stoffels
f4d4d2cc35 Translated using Weblate (Dutch)
Currently translated at 100.0% (383 of 383 strings)
2018-09-25 14:22:35 +02:00
zelos-h
b4fb4d7dcf Translated using Weblate (Chinese (Traditional))
Currently translated at 96.0% (368 of 383 strings)
2018-09-25 14:22:30 +02:00
RainSlide
1a375fb523 Translated using Weblate (Chinese (Simplified))
Currently translated at 29.2% (112 of 383 strings)
2018-09-25 14:22:27 +02:00
zelos-h
658c0ff5c4 Translated using Weblate (Chinese (Hong Kong))
Currently translated at 37.8% (145 of 383 strings)
2018-09-25 14:22:23 +02:00
Ritvik Saraf
515be677a9 no comments 2018-09-24 14:53:43 +05:30
Ritvik Saraf
d694c5f511 smoother transition to comments fragment 2018-09-23 19:45:26 +05:30
Ritvik Saraf
66c753f3a3 changed comments fragment loading animation 2018-09-23 15:22:45 +05:30
Ritvik Saraf
7047b62442 added comments fragment 2018-09-23 07:02:19 +05:30
MaX-Lo
6092f06d46 store the last used aspect ratio in SharedPreferences and reload them on
resuming the VideoPlayer Activity (similar to storing/reloading the last used: screen rotation)
2018-09-22 11:32:13 +02:00
ezjerry liao
4671b956b3 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (383 of 383 strings)
2018-09-21 16:22:02 +02:00
postsorino
552ba6999a Translated using Weblate (Greek)
Currently translated at 99.7% (382 of 383 strings)
2018-09-20 15:16:07 +02:00
zelos-h
b86bc4455f Translated using Weblate (Chinese (Traditional))
Currently translated at 94.2% (361 of 383 strings)
2018-09-20 15:16:03 +02:00
Allan Nordhøy
b6d36ee117 Translated using Weblate (Norwegian Bokmål)
Currently translated at 100.0% (383 of 383 strings)
2018-09-19 08:30:03 +02:00
Ritvik Saraf
5cc73555cd updated extractor version 2018-09-19 05:41:01 +05:30
Ritvik Saraf
219922cd82 added commentsInfo in streamInfo 2018-09-19 05:13:55 +05:30
Tobias Groza
5d57f864dc Translated using Weblate (Vietnamese)
Currently translated at 97.1% (372 of 383 strings)
2018-09-17 23:21:52 +02:00
naofum
c6ee2816db Translated using Weblate (Japanese)
Currently translated at 93.7% (359 of 383 strings)
2018-09-17 23:21:51 +02:00
Tobias Groza
0aa898b13f Translated using Weblate (Chinese (Hong Kong))
Currently translated at 37.8% (145 of 383 strings)
2018-09-17 23:21:49 +02:00
Stjepan
b22e82c8ce Translated using Weblate (Croatian)
Currently translated at 85.6% (328 of 383 strings)
2018-09-17 23:21:47 +02:00
zelos-h
63dee1e1ac Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (383 of 383 strings)
2018-09-16 13:27:50 +02:00
epsimatt
91c87ac301 Translated using Weblate (Korean)
Currently translated at 96.0% (368 of 383 strings)
2018-09-16 13:27:46 +02:00
Kartikey Kushwaha
7124d9bca5 Removed flvor checks. Added update settings under main settings. 2018-09-15 20:51:17 +05:30
Ritvik Saraf
08127e5806 added basic/crappy comments support 2018-09-15 17:15:44 +05:30
Kartikey Kushwaha
6417bd91ef Pull request changes v1. 2018-09-15 14:08:32 +05:30
Kartikey Kushwaha
cde5f7d12e Merge branch 'dev' of https://github.com/krtkush/NewPipe into 1520_app_update_notif 2018-09-15 13:30:09 +05:30
Kartikey Kushwaha
395c9587b6 Conflict resolution. 2018-09-15 13:22:13 +05:30
Kartikey Kushwaha
17197ad670 Pull request changes begins here. 2018-09-15 12:51:39 +05:30
Shuuji TAKAHASHI (shuuji3)
42371a6e8d Translated using Weblate (Japanese)
Currently translated at 88.7% (340 of 383 strings)
2018-09-14 21:20:02 +02:00
Ritiek Malhotra
d7aae849f6 Merge branch 'dev' into separate-gesture-options 2018-09-12 21:41:47 +05:30
DanieLoche
4fe9413662 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (383 of 383 strings)
2018-09-12 12:20:25 +02:00
CaptainCrumble
37bf51cebf Translated using Weblate (Portuguese)
Currently translated at 91.3% (350 of 383 strings)
2018-09-12 12:20:25 +02:00
DanieLoche
eee9cb8b87 Translated using Weblate (Portuguese)
Currently translated at 91.3% (350 of 383 strings)
2018-09-12 12:20:24 +02:00
DanieLoche
1724bca5f0 Translated using Weblate (French)
Currently translated at 100.0% (383 of 383 strings)
2018-09-12 12:20:21 +02:00
Cipisek Rumcajsu
ae6581fb55 Translated using Weblate (Czech)
Currently translated at 99.7% (382 of 383 strings)
2018-09-12 12:20:20 +02:00
Michael Moroni
0c632307ea Translated using Weblate (Italian)
Currently translated at 100.0% (383 of 383 strings)
2018-09-12 12:20:15 +02:00
BO41
479887eb84 fix broken merge resolve
fixing merge conflicts broke build
2018-09-11 19:37:12 +02:00
BO41
af280a7343 Java language level + javadoc + xml
replace with <>
String builder

BUILD SUCCESSFUL in 4s
39 actionable tasks: 4 executed, 35 up-to-date
2018-09-11 19:20:10 +02:00
BO41
802b26e870 error handling + imports + unboxing 2018-09-11 19:18:50 +02:00
BO41
0ab86937d2 data flow issue + declaration redundancy
make final
unused methods
make final

BUILD SUCCESSFUL in 0s
39 actionable tasks: 39 up-to-date
2018-09-11 19:18:41 +02:00
BO41
3ab06bf383 class structure
BUILD SUCCESSFUL in 17s
39 actionable tasks: 6 executed, 33 up-to-date
2018-09-11 19:18:14 +02:00
BO41
5db0cc5241 performance + usability
obsolete layout params
Ellipsis string can be replaced with ellipsis character
Missing inputType
Usage of showAsAction=always

BUILD SUCCESSFUL in 5s
39 actionable tasks: 4 executed, 35 up-to-date
2018-09-11 19:18:14 +02:00
BO41
a588ec084b correctness
use apply() on SharedPreferences
use dp instead of sp for text sizes

BUILD SUCCESSFUL in 22s
39 actionable tasks: 10 executed, 29 up-to-date
2018-09-11 19:18:02 +02:00
BO41
5660b5ddc6 accessibility
image without contentDescription
Keyboard inaccessible widget

BUILD SUCCESSFUL in 6s
39 actionable tasks: 4 executed, 35 up-to-date
2018-09-11 19:14:42 +02:00
BO41
27fbe69033 code cleanup
mainly removes throw statements

automated using Android Studio, staged by hand

BUILD SUCCESSFUL in 52s
39 actionable tasks: 37 executed, 2 up-to-date
2018-09-11 19:14:21 +02:00
Christian Schabesberger
1672ecb892 Merge branch 'dev' into patch-7 2018-09-11 16:34:58 +02:00
Christian Schabesberger
784e01347c fix wrong subscriptini count 2018-09-11 15:16:39 +02:00
Emin Tufan Çetin
5cc21a831b Translated using Weblate (Turkish)
Currently translated at 100.0% (383 of 383 strings)
2018-09-10 19:13:10 +02:00
Robson Cassiano
dcb9380b50 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (383 of 383 strings)
2018-09-10 19:13:08 +02:00
ButterflyOfFire
b1957773bb Translated using Weblate (Arabic)
Currently translated at 100.0% (383 of 383 strings)
2018-09-10 19:13:05 +02:00
switchtegrax1
c4f4583f20 Translated using Weblate (Portuguese (Brazil))
Currently translated at 99.7% (382 of 383 strings)
2018-09-09 18:58:19 +02:00
Dominik Zabicki
440cdbdb23 Translated using Weblate (Polish)
Currently translated at 100.0% (383 of 383 strings)
2018-09-09 18:58:18 +02:00
Duppadaadadii
58bfde33a9 Translated using Weblate (Finnish)
Currently translated at 99.7% (382 of 383 strings)
2018-09-09 18:58:17 +02:00
skil3z
7c33e49ef6 Translated using Weblate (Finnish)
Currently translated at 99.7% (382 of 383 strings)
2018-09-09 18:58:17 +02:00
Rex_sa
3bf318e2b7 Translated using Weblate (Arabic)
Currently translated at 100.0% (383 of 383 strings)
2018-09-09 18:58:10 +02:00
Ritiek Malhotra
f56193ac18 We don't need to check this 2018-09-08 23:13:04 +05:30
ScratchBuild
31efa7a8f8 Translated using Weblate (Japanese)
Currently translated at 74.1% (284 of 383 strings)
2018-09-08 19:15:24 +02:00
skil3z
372932abaf Translated using Weblate (Finnish)
Currently translated at 74.6% (286 of 383 strings)
2018-09-08 19:15:23 +02:00
M Andreev
4c6dd2cdf2 Translated using Weblate (Bulgarian)
Currently translated at 99.4% (381 of 383 strings)
2018-09-08 19:15:18 +02:00
Stjepan
6d04e39151 Translated using Weblate (Croatian)
Currently translated at 65.7% (252 of 383 strings)
2018-09-08 19:15:15 +02:00
Ritiek Malhotra
afa257e79a Merge branch 'dev' into separate-gesture-options 2018-09-08 10:05:51 -07:00
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
Ritiek Malhotra
97555645f8 Merge branch 'dev' into separate-gesture-options 2018-09-05 09:21:05 -07: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
kaka Thic
e350f43adc Translated using Weblate (Vietnamese)
Currently translated at 95.8% (367 of 383 strings)
2018-09-05 08:26:05 +02:00
Çağdaş Tatar
2257f3484d Translated using Weblate (Turkish)
Currently translated at 100.0% (383 of 383 strings)
2018-09-05 08:25:35 +02: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
f4ba8df02b Merge branch 'dev' into patch-7 2018-09-04 17:53:39 +02: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
notramo
99ebf03876 Translated using Weblate (Hungarian)
Currently translated at 40.7% (156 of 383 strings)
2018-09-03 19:18:49 +02:00
postsorino
995d79e373 Translated using Weblate (Greek)
Currently translated at 98.1% (376 of 383 strings)
2018-09-03 19:18:46 +02:00
Allan Nordhøy
0cd153ab61 Spelling: Language rework 2018-09-03 13:07:10 +02:00
Christian Schabesberger
81e76f260c fix drawer header font color for white theme 2018-09-01 12:33:08 +02:00
Vasily
b24baa68ba Tablet UI in player 2018-08-31 17:30:06 +03:00
Vasily
d4c1b8d321 Fix: remove title from PlaylistDialog 2018-08-31 17:12:56 +03:00
Vasily
1e53d6bfab Scroll top related streams when loading 2018-08-31 17:08:13 +03:00
Ritiek Malhotra
5931cd6af7 Separate options for volume and brightness gestures 2018-08-31 19:30:40 +05:30
u1
a1be03543c Grid layout for subscriptions 2018-08-31 16:49:25 +03:00
u1
b1a5547de2 Fix reordering playlist items on grid layout 2018-08-31 16:34:35 +03:00
Christian Schabesberger
93571961ee merge weblate changes 2018-08-31 14:11:16 +02:00
Vasily
ee4942dfd7 Grid layout for local lists 2018-08-31 14:34:32 +03: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
Vasily
8aef24be1e Merge branch 'tablet_ui' of https://github.com/nv95/NewPipe into tablet_ui 2018-08-29 09:00:15 +03:00
Vasily
fb25f6c7ac Automatic list layout 2018-08-29 08:19:15 +03:00
Vasily
fbd983217d Hide related streams while loading 2018-08-29 08:08:35 +03:00
Vasily
ce21fe2087 Always show description on tablets 2018-08-29 08:01:18 +03:00
Christian Schabesberger
17cd395712 Merge branch 'dev' into tablet_ui 2018-08-28 21:28:10 +02:00
Christian Schabesberger
bfe9de05cd Merge branch 'dev' into LongTapInSubs 2018-08-28 18:39:11 +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
Somethingweirdhere
4326354ca6 Code cleanup 2018-08-22 13:59:12 +02:00
Vasily
4208c852e1 Update translations 2018-08-22 10:33:10 +03:00
Vasily
7330b4532e Fix crash on screen rotation 2018-08-22 10:29:37 +03:00
Vasily
1e0f6f9e41 Grid view 2018-08-22 10:14:01 +03:00
Vasily
216e2367c6 Video details tablet layout 2018-08-22 08:32:58 +03: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
Kartikey Kushwaha
910c10f554 Removed debug code 2018-08-17 01:46:33 +05:30
Kartikey Kushwaha
04e974b326 Bug fix. 2018-08-17 01:11:51 +05:30
Kartikey Kushwaha
e7abeb5ad9 Added version code check. 2018-08-17 00:53:42 +05:30
Somethingweirdhere
f4416fe007 Doesn't use getChannelInfo() anymore. 2018-08-16 01:04:37 +02:00
Somethingweirdhere
510591ef0f Removed use of blockingFirst() and scheduleDirect() 2018-08-16 00:45:37 +02:00
Somethingweirdhere
a5e89d1dd1 Merge branch 'dev' into LongTapInSubs 2018-08-15 23:33:59 +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
Kartikey Kushwaha
2a18eacf62 More refactoring. 2018-08-12 20:57:30 +05:30
Kartikey Kushwaha
af42e32ae6 Code refactored and added comments. 2018-08-12 18:34:20 +05:30
Kartikey Kushwaha
12b93d6637 Added new icon for update notification. 2018-08-12 17:18:46 +05:30
Kartikey Kushwaha
930c971035 Added version check in the pop-up player 2018-08-12 16:41:21 +05:30
Kartikey Kushwaha
06f20c66f8 Moved the new version check to the application class. 2018-08-12 16:35:53 +05:30
Kartikey Kushwaha
7c875a8541 Merge branch 'dev' of https://github.com/krtkush/NewPipe into 1520_app_update_notif 2018-08-12 15:19:15 +05:30
Kartikey Kushwaha
f85e19c75d Added notification channel and code to show notification. 2018-08-12 15:01:50 +05:30
Kartikey Kushwaha
5e2aa51627 Moved the asynctask to its own class. 2018-08-11 19:36:23 +05:30
Kartikey Kushwaha
75a44fb30a Added HTTPS request to get version data. Added APK flaor for github and fdroid. 2018-08-11 19:13:52 +05:30
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
Christian Schabesberger
b52dee37f4 move on to version v0.13.7 2018-07-23 18:54:40 +02:00
Ali Demirtas
21bd9f09da Translated using Weblate (Turkish)
Currently translated at 100,0% (383 of 383 strings)
2018-07-22 18:53:19 +02:00
Weblate
44f24e58f6 Merge branch 'origin/dev' into Weblate 2018-07-22 15:03:59 +02:00
Allan Nordhøy
860c4d045a Translated using Weblate (Norwegian Bokmål)
Currently translated at 95.8% (367 of 383 strings)
2018-07-22 15:03:58 +02:00
Marc Riera
3b0c96f654 Translated using Weblate (Catalan)
Currently translated at 100.0% (383 of 383 strings)
2018-07-22 15:03:56 +02:00
Jeff Huang
65b744472b Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (383 of 383 strings)
2018-07-22 15:03:53 +02:00
Christian Schabesberger
26489b0f00 fix filters 2018-07-22 13:55:17 +02:00
Marc Riera
414abad05f Translated using Weblate (Catalan)
Currently translated at 100,0% (383 of 383 strings)
2018-07-22 12:41:26 +02:00
Heimen Stoffels
735d9a5391 Translated using Weblate (Dutch)
Currently translated at 100.0% (383 of 383 strings)
2018-07-22 11:40:53 +02:00
Osoitz
50571449cb Translated using Weblate (Basque)
Currently translated at 100.0% (383 of 383 strings)
2018-07-22 11:01:08 +02:00
Weblate
a31aacd115 Merge branch 'origin/dev' into Weblate 2018-07-22 10:57:42 +02:00
Rex_sa
f5b57cc0da Translated using Weblate (Arabic)
Currently translated at 100.0% (379 of 379 strings)
2018-07-22 10:57:42 +02:00
Osoitz
b7006a8f2c Translated using Weblate (Basque)
Currently translated at 100.0% (379 of 379 strings)
2018-07-22 10:57:35 +02:00
Christian Schabesberger
82bb467a2a move on to version v0.13.6 2018-07-21 14:35:03 +02:00
Christian Schabesberger
3a44e92432 Merge branch 'search' into dev 2018-07-19 16:19:33 +02:00
Christian Schabesberger
e60db5f928 make new filtersystem translatable 2018-07-19 16:18:57 +02:00
Ariel Shulman
78485287a4 Translated using Weblate (Hebrew)
Currently translated at 82.3% (312 of 379 strings)
2018-07-18 20:37:59 +02:00
D D
48b6f01b13 Translated using Weblate (Bulgarian)
Currently translated at 97.8% (371 of 379 strings)
2018-07-18 17:35:33 +02:00
Christian Schabesberger
39e04de208 show radio pin for content filter again 2018-07-18 15:05:49 +02:00
Next Hubs
68d5b59693 Translated using Weblate (Urdu)
Currently translated at 5.0% (19 of 379 strings)
2018-07-18 12:43:31 +02:00
Andrea Troiano
4ef01ef745 Translated using Weblate (Italian)
Currently translated at 100,0% (379 of 379 strings)
2018-07-18 10:28:06 +02:00
Rex_sa
573fa8870c Translated using Weblate (Arabic)
Currently translated at 100.0% (379 of 379 strings)
2018-07-17 14:34:21 +02:00
Rex_sa
88d354b08b Translated using Weblate (Arabic)
Currently translated at 100,0% (379 of 379 strings)
2018-07-16 13:17:38 +02:00
Rex_sa
0ff65b5496 Translated using Weblate (Arabic)
Currently translated at 100.0% (379 of 379 strings)
2018-07-15 21:34:22 +02:00
Christian Schabesberger
14e0dcb085 fix names from UIH to LinkHandler 2018-07-15 21:21:09 +02:00
Rex_sa
e008fd21a4 Translated using Weblate (Arabic)
Currently translated at 100.0% (379 of 379 strings)
2018-07-14 21:00:07 +02:00
Nathan Follens
4638149ad0 Translated using Weblate (Flemish)
Currently translated at 100,0% (379 of 379 strings)
2018-07-11 20:52:24 +02:00
Tobias Groza
e386bdd6b3 Translated using Weblate (Greek)
Currently translated at 19.7% (75 of 379 strings)
2018-07-11 18:38:04 +02:00
Christian Schabesberger
decb167ba9 make the new extractor refactorings work with SoundCloud 2018-07-10 16:26:42 +02:00
Tobias Groza
dd9557c13e Translated using Weblate (Greek)
Currently translated at 19.5% (74 of 379 strings)
2018-07-10 02:38:34 +02:00
Lee Hoe Mun
708d7162fb Translated using Weblate (Chinese (Mandarin))
Currently translated at 16.6% (63 of 379 strings)
2018-07-09 20:35:11 +02:00
Michalis Nikolaidis
1ee1b522f1 Translated using Weblate (Greek)
Currently translated at 19,5% (74 of 379 strings)
2018-07-09 02:11:31 +02:00
Christian Schabesberger
d5a500c037 bring everything to compile and run 2018-07-08 17:46:21 +02:00
Eduardo Caron
3e02c65bc0 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (379 of 379 strings)
2018-07-08 15:22:38 +02:00
Christian Schabesberger
d10f9a5f25 add getMoreInfo to SearchInfo 2018-07-08 14:45:00 +02:00
AB
17e7214d25 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (379 of 379 strings)
2018-07-08 09:28:02 +02:00
Víctor Manuel Tapia Ramírez
22774592db Translated using Weblate (Spanish)
Currently translated at 100,0% (379 of 379 strings)
2018-07-07 21:07:00 +02:00
Freddy Morán Jr
7c2aa6e69d Translated using Weblate (Spanish)
Currently translated at 100,0% (379 of 379 strings)
2018-07-07 21:06:55 +02:00
Ali Demirtas
d1dbcda88e Translated using Weblate (Turkish)
Currently translated at 100,0% (379 of 379 strings)
2018-07-07 19:03:18 +02:00
Jeff Huang
3e05508cf9 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (379 of 379 strings)
2018-07-07 15:10:47 +02:00
Heimen Stoffels
1acdefd358 Translated using Weblate (Dutch)
Currently translated at 100.0% (379 of 379 strings)
2018-07-07 11:06:57 +02:00
Marc Riera
23143797f9 Translated using Weblate (Catalan)
Currently translated at 100,0% (379 of 379 strings)
2018-07-07 10:27:11 +02:00
ssantos
3364bf268b Translated using Weblate (German)
Currently translated at 100,0% (379 of 379 strings)
2018-07-07 07:51:52 +02:00
Weblate
2025d6305e Merge branch 'origin/dev' into Weblate 2018-07-07 07:48:38 +02:00
cxj
639ad7698d Translated using Weblate (Chinese (Mandarin))
Currently translated at 15,6% (59 of 378 strings)
2018-07-07 07:48:37 +02:00
D D
4eaff51ba2 Translated using Weblate (Bulgarian)
Currently translated at 61,9% (234 of 378 strings)
2018-07-07 07:48:35 +02:00
ssantos
1fb30bc3d9 Translated using Weblate (German)
Currently translated at 100,0% (378 of 378 strings)
2018-07-07 07:48:33 +02:00
Christian Schabesberger
6b66f40bcb Merge pull request #1392 from karyogamy/exoplayer-2.8.0-update
ExoPlayer 2.8.2 Update
2018-07-05 13:02:21 +02:00
Allan Nordhøy
a3cd531cc8 Translated using Weblate (Norwegian Bokmål)
Currently translated at 96.0% (363 of 378 strings)
2018-07-04 00:40:31 +02:00
Nathan Follens
55c84192be Translated using Weblate (Flemish)
Currently translated at 100.0% (378 of 378 strings)
2018-07-03 09:36:55 +02:00
Víctor Manuel Tapia Ramírez
aa9018b97b Translated using Weblate (Spanish)
Currently translated at 100.0% (378 of 378 strings)
2018-07-03 00:43:30 +02:00
monolifed
1e79c146a7 Translated using Weblate (Turkish)
Currently translated at 100.0% (378 of 378 strings)
2018-07-02 15:43:34 +02:00
Marian Hanzel
cb8545e33d Translated using Weblate (Slovak)
Currently translated at 96.0% (363 of 378 strings)
2018-07-02 09:44:01 +02:00
Nathan Follens
4a484c535b Translated using Weblate (Flemish)
Currently translated at 100,0% (378 of 378 strings)
2018-07-02 09:27:09 +02:00
Víctor Manuel Tapia Ramírez
ee417e41ea Translated using Weblate (Spanish)
Currently translated at 100,0% (378 of 378 strings)
2018-07-02 00:16:04 +02:00
Eduardo Caron
3cdc1fcaee Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (378 of 378 strings)
2018-07-01 22:45:25 +02:00
Jeff Huang
0a023a61f9 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (378 of 378 strings)
2018-07-01 15:21:02 +02:00
monolifed
73f81c5b52 Translated using Weblate (Turkish)
Currently translated at 100.0% (378 of 378 strings)
2018-07-01 14:48:03 +02:00
Marc Riera
bc4acbb7e1 Translated using Weblate (Catalan)
Currently translated at 100,0% (378 of 378 strings)
2018-07-01 12:39:58 +02:00
Heimen Stoffels
26d07ea2c6 Translated using Weblate (Dutch)
Currently translated at 100.0% (378 of 378 strings)
2018-07-01 11:40:26 +02:00
Weblate
5380c8352d Merge branch 'origin/dev' into Weblate 2018-07-01 08:42:10 +02:00
Anton Shestakov
e85e91183e Translated using Weblate (Russian)
Currently translated at 99.4% (371 of 373 strings)
2018-07-01 08:42:07 +02:00
Somethingweirdhere
b48c251b36 Added unsubscription toast
Added error handling
Corrected threads
2018-06-30 02:42:47 +02:00
Somethingweirdhere
181a14ce59 Disposable now is cleaned properly 2018-06-30 02:20:01 +02:00
Christian Schabesberger
a208a22bc2 add link to the incredible bugreport converter 2018-06-29 14:03:15 +02:00
Somethingweirdhere
b9ea7ce066 Code looking nicer 2018-06-29 00:39:44 +02:00
Somethingweirdhere
f2f275512d It looks good now 2018-06-29 00:39:16 +02:00
Somethingweirdhere
5150c2ee62 This thing actually works lol 2018-06-28 23:56:39 +02:00
John Zhen Mo
644766b5a6 -Updated exoplayer lib to 2.8.2. 2018-06-28 12:21:03 -07:00
John Zhen Mo
ca679f5932 -Fixed potential NPE when updating thumbnail in background player. 2018-06-28 12:18:02 -07:00
John Zhen Mo
7f7145e8de -Fixed playback parameter dialog settings not persisting through rotation.
-Moved playback parameter dialog step size selector to below pitch slider.
2018-06-28 12:04:30 -07:00
John Zhen Mo
aa1878c15a -Changed baseplayer metadata getters to use media tag as source.
-Changed background player notification to no longer update bitmap on progress time change.
-Changed popup player to move above soft keyboard when it is opened.
2018-06-28 12:04:30 -07:00
John Zhen Mo
e7d23176b7 -Fixed database backup failing due to journal file name change after Room DB version update. 2018-06-28 12:04:30 -07:00
John Zhen Mo
31218c2a8c -Fixed popup player notification metadata not updated on stream change.
-Fixed popup player window not clipped to above soft input keyboard upon expansion.
2018-06-28 12:03:20 -07:00
John Zhen Mo
06374c82fd -Fixed video players end screen not cleared on restarting playback after single stream play queue is completed. 2018-06-28 12:03:20 -07:00
John Zhen Mo
8efcc8f80f -Fixed main video player end screen thumbnail not fitting screen aspect ratio. 2018-06-28 12:03:20 -07:00
John Zhen Mo
2d6317bd24 -Fixed audio-only streams thumbnail not displaying on video players.
-Fixed potential play queue desynchronization due to fast forwarding on silence.
-Added current thumbnail storing in base player to allow immediate retrieval for notification building.
-Removed video player buffer spinner during interim buffering but not initial buffering.
-Reverted foreground notification stopping on pause and on complete.
2018-06-28 12:03:20 -07:00
John Zhen Mo
157b064214 -Fixed player database and progress disposable disposed when destroying exoplayer.
-Fixed livestream not reloading on behind live window exception.
-Added nonnull annotation to player intent strings.
2018-06-28 12:01:34 -07:00
John Zhen Mo
0ece4851d2 -Updated ExoPlayer to 2.8.1, fixing livestream with long duration not loading.
-Updated OkHttp to 3.10.0 and RxJava to 2.1.14.
-Changed player recovery seek to use ExoPlayer built-in window seeking instead of seeking after stream window starts playing.
-Changed playback speed changer default step size to 25%.
-Changed player notification to reset on all state changes.
-Fixed gradle dependency version incorrect variable names.
-Fixed video player double tap not working during pause.
-[#1412] Fixed NPE when sharing video to main video activity when it was playing but is out of focus: Reset main player state when new intent is received.
-[#1410] Fixed fast forwarding and rewinding not working within 10 seconds from beginning or end of a stream window.
2018-06-28 12:01:34 -07:00
John Zhen Mo
f1f5996975 -Refactored playback resolvers and other persistent player objects to instantiate once only during player creation to enforce non-nullity.
-Fixed background and popup player service staying in foreground when playback is paused or completed.
-Fixed player metadata not updating on new stream.
-Fixed player intent playback quality not applied.
-Fixed player auto-queue not applied after stream transition or swapping.
2018-06-28 12:00:00 -07:00
John Zhen Mo
0a2dbc4688 -Fixed playlist fragment infinite update cycle.
-Updated Room DB version to 1.1.0.
2018-06-28 11:59:59 -07:00
John Zhen Mo
13587d7ab3 -Fixed some typos. 2018-06-28 11:58:33 -07:00
John Zhen Mo
0fcef064fb -Reduced fling speed required to close popup by 40%. 2018-06-28 11:58:33 -07:00
John Zhen Mo
19b8796cbc -Fixed statistics fragment button not animating when pressed.
-Removed background player notification button opacity change.
2018-06-28 11:58:33 -07:00
John Zhen Mo
15fb60a845 -Fixed bookmarked playlist not updating metadata when changed. 2018-06-28 11:58:32 -07:00
John Zhen Mo
5c202f04e7 -[#1383]Fixed popup player caption selector not populating due to full width aspect ratio selector.
-Fixed potential memory leak in media session connector containing player instance.
2018-06-28 11:58:32 -07:00
John Zhen Mo
bc6fdf81d2 -Refactored player media source resolution into external helpers.
-Baked resolved media metadata into media source for one-way data passing.
2018-06-28 11:58:32 -07:00
John Zhen Mo
3194a2bf2c -Fixed skip silence state not maintained by player on new queue.
-Fixed TrackSelector deprecations.
2018-06-28 11:58:32 -07:00
John Zhen Mo
72d1e5131f -Added skip silence toggle to playback speed control.
-Added step size selector to playback speed control.
-Added skip silence flag to player intents.
-Moved default preset in playback speed control to neutral dialog button, renamed as reset.
-Removed nightcore preset from playback speed control.
2018-06-28 11:58:32 -07:00
John Zhen Mo
7721098551 -Updated ExoPlayer to 2.8.0
-Updated MediaSource contracts in ManagedMediaSource.
-Changed PlaceholderMediaSource and FailedMediaSource to use built-in BaseMediaSource implementation.
-Changed deprecated DynamicConcatenatingMediaSource to ConcatenatingMediaSource.
-Removed manual playlist media source disposal in favor of player built-in disposal.
2018-06-28 11:58:32 -07:00
Somethingweirdhere
0b7593ad28 Delete on long press 2018-06-28 20:43:46 +02:00
Somethingweirdhere
a68823491c Delete on long press 2018-06-28 20:37:05 +02:00
Christian Schabesberger
f563bc4210 Merge pull request #1510 from karyogamy/lib-update
Library version update and database backup fix
2018-06-28 12:16:04 +02:00
Christian Schabesberger
43e7be9b86 Merge pull request #1509 from karyogamy/main-video-player-fix
Main video player fix
2018-06-28 12:00:54 +02:00
John Zhen Mo
27131d15dd -Updated room db to 1.1.1.
-Fixed database import/export to no longer include accessory db files to ensure backward compatibility.
2018-06-26 12:26:01 -07:00
John Zhen Mo
fb1a290bd9 -Updated okHttp to 3.10.0.
-Updated mockito to 2.8.9.
-Updated rxJava to 2.1.14.
-Fixed stetho to use correct lib version.
2018-06-26 12:02:26 -07:00
John Zhen Mo
ef16145695 -Fixed player new share intent causing main player crash due to player activity in background. 2018-06-26 10:21:43 -07:00
John Zhen Mo
4fbd1182c2 -Fixed minimizing to popup player does not destroying existing player when drawing over app permission is not granted. 2018-06-26 10:19:16 -07:00
Christian Schabesberger
2d39e65b5c Merge branch '640-screen-off' of https://github.com/krtkush/NewPipe into test 2018-06-26 11:23:45 +02:00
Kartikey Kushwaha
8e96b675fa Removed unwanted files. 2018-06-26 01:13:21 +05:30
Christian Schabesberger
adb6943420 Merge pull request #1354 from karyogamy/minimize-on-exit
Minimize main player on exit
2018-06-25 19:46:13 +02:00
Christian Schabesberger
eae7babf93 Merge pull request #1454 from Somethingweirdhere/dev
Added share option to long tap menu
2018-06-25 15:28:37 +02:00
Christian Schabesberger
7d5e18c05b Merge pull request #1498 from Somethingweirdhere/download
Download option in share menu
2018-06-25 12:35:34 +02:00
Somethingweirdhere
cbe001efd6 Added option to menu 2018-06-25 12:04:11 +02:00
Christian Schabesberger
86b783fb0f Merge pull request #1472 from acrosca/undo_delete
undo delete
2018-06-25 10:04:17 +02:00
Christian Schabesberger
ccc27b48df Merge pull request #1497 from karyogamy/view-history-fix
View history fix
2018-06-25 09:26:08 +02:00
Tobias Groza
a32391f560 Translated using Weblate (German)
Currently translated at 100.0% (373 of 373 strings)
2018-06-24 17:38:37 +02:00
nautilusx
aed0348802 Translated using Weblate (German)
Currently translated at 100.0% (373 of 373 strings)
2018-06-24 17:38:32 +02:00
Rossinière Vaud
1b66446c0d Translated using Weblate (Polish)
Currently translated at 100.0% (373 of 373 strings)
2018-06-24 12:42:40 +02:00
Tobias Groza
2bc0c8a483 Translated using Weblate (German)
Currently translated at 100,0% (373 of 373 strings)
2018-06-23 17:09:58 +02:00
DafabHoid
3f7e02e305 Translated using Weblate (German)
Currently translated at 100,0% (373 of 373 strings)
2018-06-23 17:09:53 +02:00
nautilusx
dd0d666003 Translated using Weblate (German)
Currently translated at 100,0% (373 of 373 strings)
2018-06-23 17:09:47 +02:00
Tobias Groza
81859a37de Translated using Weblate (German)
Currently translated at 100,0% (373 of 373 strings)
2018-06-23 17:05:55 +02:00
rimasx
180bb581a3 Translated using Weblate (Estonian)
Currently translated at 94.6% (353 of 373 strings)
2018-06-23 16:37:11 +02:00
yuriqdev
2f31779af4 Translated using Weblate (Russian)
Currently translated at 99.7% (372 of 373 strings)
2018-06-23 13:42:42 +02:00
Rossinière Vaud
ee6d512165 Translated using Weblate (Polish)
Currently translated at 100,0% (373 of 373 strings)
2018-06-23 12:08:23 +02:00
Weblate
1470fdc057 Merge branch 'origin/dev' into Weblate 2018-06-22 09:35:50 +02:00
rimasx
93bbaf187e Translated using Weblate (Estonian)
Currently translated at 94.6% (353 of 373 strings)
2018-06-22 09:35:46 +02:00
Víctor Manuel Tapia Ramírez
f1e43007f1 Translated using Weblate (Chinese (Mandarin))
Currently translated at 6.4% (24 of 373 strings)
2018-06-22 09:35:21 +02:00
D D
155436b85d Translated using Weblate (Bulgarian)
Currently translated at 62.7% (234 of 373 strings)
2018-06-22 09:35:18 +02:00
Somethingweirdhere
f3e029c3f6 Cleaned code, downloaddialog now also appears after giving storage permission. 2018-06-20 14:46:57 +02:00
Christian Schabesberger
90d6416f55 Merge pull request #1491 from annoyatron255/video-info-fix
Fix #1440 Broken Video Info Layout
2018-06-19 09:50:02 +02:00
Ale-Ma
af2a2e45af Translated using Weblate (Italian)
Currently translated at 100.0% (373 of 373 strings)
2018-06-19 09:40:03 +02:00
Andrea Troiano
e79eda9c5c Translated using Weblate (Italian)
Currently translated at 100.0% (373 of 373 strings)
2018-06-19 09:39:58 +02:00
John Zhen Mo
b338d9dbcf -Fixed view not registered when playback is started on external players. 2018-06-18 18:27:37 -07:00
John Zhen Mo
7fb9345344 -Fixed remote playlist metadata not updated when remote source data has changed. 2018-06-18 18:22:52 -07:00
Weblate
77b488568b Merge branch 'origin/dev' into Weblate 2018-06-17 15:56:31 +02:00
Edwar Tikhonov
fcf650c6eb Translated using Weblate (Russian)
Currently translated at 100.0% (373 of 373 strings)
2018-06-17 15:56:30 +02:00
rimasx
0c21023ad8 Added translation using Weblate (Estonian) 2018-06-17 15:56:28 +02:00
Somethingweirdhere
8f35a56ec8 Added download to share menu 2018-06-17 13:55:43 +02:00
annoyatron255
95ba1873e4 Fix #1440 Broken Video Info Layout 2018-06-16 18:12:56 -05:00
Andrei.Rosca
8b8652d44c undo delete - code format 2018-06-15 16:15:55 +02:00
Issam Maghni
2515b8167f Disable animation (hidden anyway by navigation) 2018-06-15 01:21:30 -04:00
Andrei.Rosca
09dd044f3d undo delete 2018-06-13 09:07:57 +02:00
Dual Natan
ace0ed9667 Translated using Weblate (Swedish)
Currently translated at 68.9% (257 of 373 strings)
2018-06-12 22:43:59 +02:00
Dual Natan
cf03708da2 Translated using Weblate (Macedonian)
Currently translated at 100.0% (373 of 373 strings)
2018-06-11 21:21:57 +02:00
Edwar Tikhonov
d4670bf6fa Translated using Weblate (Russe)
Currently translated at 100,0% (373 of 373 strings)
2018-06-11 16:04:29 +02:00
Edwar Tikhonov
feea448a24 Translated using Weblate (Русский)
Currently translated at 100,0% (373 of 373 strings)
2018-06-10 15:17:20 +02:00
aiddroid
71ad54652b Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (373 of 373 strings)
2018-06-10 10:35:41 +02:00
aiddroid
8e1deda7b0 Translated using Weblate (Chinese (Mandarin))
Currently translated at 0.0% (0 of 373 strings)
2018-06-10 09:35:42 +02:00
aiddroid
752f985e13 Translated using Weblate (简体中文(Chinese Simplified))
Currently translated at 100.0% (373 of 373 strings)
2018-06-09 10:09:35 +02:00
Kartikey Kushwaha
89e3219e06 Further fixes wrt FLAG_NOT_FOCUSABLE. 2018-06-09 01:38:57 +05:30
Kartikey Kushwaha
429dddc6c9 reintroduced hideControls method. 2018-06-09 01:13:37 +05:30
Kartikey Kushwaha
981174a490 Fixed bug #640. 2018-06-09 01:01:13 +05:30
Somethingweirdhere
201f7e9848 Added share option to Popup and Background queues 2018-06-08 15:59:05 +02:00
kapodamy
bb0d8ad58a Translated using Weblate (Spanish)
Currently translated at 99.7% (372 of 373 strings)
2018-06-08 01:44:11 +02:00
Weblate
29e64f7c1a Merge branch 'origin/dev' into Weblate 2018-06-06 20:37:29 +02:00
JAPP
4819ebd56e Translated using Weblate (French)
Currently translated at 99.1% (370 of 373 strings)
2018-06-06 20:37:23 +02:00
John Zhen Mo
3b603b0637 -Added back button press check to destroy rather than minimize main video player. 2018-06-05 23:37:20 -07:00
Somethingweirdhere
baa63249d1 Added share option to long tap menu 2018-06-05 19:48:31 +02:00
Somethingweirdhere
ee347e3081 Merge pull request #1 from TeamNewPipe/dev
Up to date
2018-06-05 19:35:47 +02:00
Christian Schabesberger
f96bc95053 Update CONTRIBUTING.md 2018-06-05 11:03:48 +02:00
John Zhen Mo
e1df4757e4 -Expanded minimize to exit to allow resuming on background player.
-Modified minimize to exit toggle to selection dialog.
2018-06-03 14:09:16 -07:00
John Zhen Mo
4fc37a7321 -Added toggle to allow main video player to switch to popup player when onstop is called.
-Fixed player state not recovering when player is stopped during multiwindow mode.
-Updated gradle to 3.1.2.
2018-06-03 13:20:39 -07:00
Christian Schabesberger
2a45a13f73 move on to version v0.13.5 2018-06-03 12:36:35 +02:00
Christian Schabesberger
067b15c300 Merge branch 'dev' of https://github.com/DafabHoid/NewPipe into test 2018-06-03 12:20:43 +02:00
Christian Schabesberger
8a1c283542 Merge branch 'media-session-fix' of https://github.com/karyogamy/NewPipe into test 2018-06-03 12:12:54 +02:00
Christian Schabesberger
93d1e8b2ff Merge pull request #1441 from DafabHoid/fixdownloadercrashrelease
Downloader: Fix crash on loading unfinished downloads from .giga files (Fixup for release builds)
2018-06-03 12:08:20 +02:00
Christian Schabesberger
c60d5b54fa Merge branch 'remeber_brightness' of https://github.com/acrosca/NewPipe into test 2018-06-03 11:02:49 +02:00
Andrei.Rosca
ef180f082e Remember brightness for the session 2018-06-02 09:06:40 +02:00
Rex_sa
f52741cc37 Translated using Weblate (Arabic)
Currently translated at 100.0% (373 of 373 strings)
2018-06-02 07:34:24 +02:00
DafabHoid
2a2661f066 Downloader: Fix crash on loading unfinished downloads from .giga files
This is a fixup, which fixes the crash in release builds, too. It keeps proguard from removing the new method "private void readObject(ObjectInputStream)", which is only called by the VM, but not from the code.
2018-06-01 14:35:03 +02:00
Rex_sa
b521903138 Translated using Weblate (Arabic)
Currently translated at 100.0% (373 of 373 strings)
2018-06-01 07:02:51 +02:00
Ali Toor
acbd699d95 Translated using Weblate (Urdu)
Currently translated at 2.9% (11 of 373 strings)
2018-05-31 06:43:02 +02:00
Andrea Troiano
6b8928becb Translated using Weblate (Italiano)
Currently translated at 100,0% (373 of 373 strings)
2018-05-30 11:14:38 +02:00
Víctor Manuel Tapia Ramírez
e393bdb1e5 Translated using Weblate (Spanish)
Currently translated at 99.7% (372 of 373 strings)
2018-05-30 10:43:28 +02:00
Osoitz
bba6b96765 Translated using Weblate (Basque)
Currently translated at 100.0% (373 of 373 strings)
2018-05-29 16:58:04 +02:00
John Zhen Mo
740116356c -Fixed media session activation.
-Removed redundant setShuffle call in media session callback and its user.
-Removed unused dummy playback preparer.
2018-05-28 20:02:02 -07:00
monolifed
2f6e4fa4a3 Translated using Weblate (Turkish)
Currently translated at 100.0% (373 of 373 strings)
2018-05-28 20:44:52 +02:00
Allan Nordhøy
fb3f6721b2 Translated using Weblate (Norwegian Bokmål)
Currently translated at 95.9% (358 of 373 strings)
2018-05-28 17:41:18 +02:00
AB
4e7bd21e5c Translated using Weblate (Ukrainian)
Currently translated at 100.0% (373 of 373 strings)
2018-05-28 17:07:13 +02:00
Jeff Huang
219c2030b9 Translated using Weblate (漢語(正體字))
Currently translated at 100.0% (373 of 373 strings)
2018-05-28 08:56:18 +02:00
Marc Riera
b75fdb4566 Translated using Weblate (català)
Currently translated at 100,0% (373 of 373 strings)
2018-05-28 08:41:52 +02:00
Nathan Follens
4584b14a31 Translated using Weblate (Vlaams)
Currently translated at 100,0% (373 of 373 strings)
2018-05-28 07:53:32 +02:00
thami simo
814ddb5932 Translated using Weblate (Arabic)
Currently translated at 100.0% (373 of 373 strings)
2018-05-28 07:45:58 +02:00
DafabHoid
6ea0f6290a Downloader: Notify the progress every 64K instead of every 512 Bytes
This improves downloading performance dramatically when cpu bound:
Before, even a high-end cpu from 2013 can't download faster than around 1MB/s.
The bigger read buffer size removes the need for a dedicated BufferedInputStream.
2018-05-28 01:07:30 +02:00
monolifed
c796fe1fe6 Translated using Weblate (Turkish)
Currently translated at 100.0% (373 of 373 strings)
2018-05-27 19:49:08 +02:00
Heimen Stoffels
a452a164e6 Translated using Weblate (Dutch)
Currently translated at 100.0% (373 of 373 strings)
2018-05-27 18:00:16 +02:00
ssantos
0d9dd69b19 Translated using Weblate (Deutsch)
Currently translated at 100,0% (373 of 373 strings)
2018-05-27 17:51:41 +02:00
Eduardo Caron
d7b73c18f1 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (373 of 373 strings)
2018-05-27 15:14:19 +02:00
Weblate
07f66c0e45 Merge branch 'dev' into weblate-merge-tmp 2018-05-27 15:11:40 +02:00
monolifed
d449acbf86 Translated using Weblate (Turkish)
Currently translated at 99.4% (369 of 371 strings)
2018-05-27 15:11:40 +02:00
Allan Nordhøy
fce416ba76 Translated using Weblate (Norwegian Bokmål)
Currently translated at 96.2% (357 of 371 strings)
2018-05-27 15:11:35 +02:00
dadosch
cb6bfe8556 Translated using Weblate (German)
Currently translated at 100.0% (371 of 371 strings)
2018-05-27 15:11:35 +02:00
Florian
d7b31e1d25 Translated using Weblate (French)
Currently translated at 98.6% (366 of 371 strings)
2018-05-27 15:11:34 +02:00
Eduardo Caron
85057376d6 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (371 of 371 strings)
2018-05-27 15:11:31 +02:00
Christian Schabesberger
c43ac7c869 fix conflict 2018-05-27 13:30:23 +02:00
Heimen Stoffels
b2657315f1 Translated using Weblate (Dutch)
Currently translated at 100.0% (371 of 371 strings)
2018-05-27 12:36:08 +02:00
ezjerry liao
de5ed9717c Translated using Weblate (漢語(正體字))
Currently translated at 100.0% (371 of 371 strings)
2018-05-27 01:18:13 +02:00
Ali Demirtas
e17a6cbb9f Translated using Weblate (Turkish)
Currently translated at 100.0% (371 of 371 strings)
2018-05-27 00:07:08 +02:00
Marc Riera
8e783b774b Translated using Weblate (català)
Currently translated at 100,0% (371 of 371 strings)
2018-05-27 00:03:34 +02:00
AB
733663f40d Translated using Weblate (Ukrainian)
Currently translated at 100.0% (371 of 371 strings)
2018-05-26 22:34:26 +02:00
Ali Demirtas
4b2a792a62 Translated using Weblate (Türkçe)
Currently translated at 100,0% (371 of 371 strings)
2018-05-26 21:01:30 +02:00
dadosch
f7aa171d01 Translated using Weblate (Deutsch)
Currently translated at 100,0% (371 of 371 strings)

informal "du"
2018-05-26 15:14:27 +02:00
dadosch
5eafefb683 Translated using Weblate (Deutsch)
Currently translated at 100,0% (371 of 371 strings)
2018-05-26 15:12:02 +02:00
dadosch
5d1b02a856 Translated using Weblate (Deutsch)
Currently translated at 100,0% (371 of 371 strings)

informal "du"
2018-05-26 15:08:29 +02:00
dadosch
4acda3d9ae Translated using Weblate (Deutsch)
Currently translated at 100,0% (371 of 371 strings)
2018-05-26 15:07:50 +02:00
dadosch
643e10ace2 Translated using Weblate (Deutsch)
Currently translated at 100,0% (371 of 371 strings)

informal "du"
2018-05-26 15:04:45 +02:00
dadosch
7b64a232de Translated using Weblate (Deutsch)
Currently translated at 100,0% (371 of 371 strings)
2018-05-26 15:00:23 +02:00
dadosch
d7472d837d Translated using Weblate (Deutsch)
Currently translated at 100,0% (371 of 371 strings)

informal "du"
2018-05-26 14:57:32 +02:00
dadosch
94b473ab4b Translated using Weblate (Deutsch)
Currently translated at 100,0% (371 of 371 strings)
2018-05-26 14:56:36 +02:00
dadosch
4a05bbb6c8 Translated using Weblate (Deutsch)
Currently translated at 100,0% (371 of 371 strings)
2018-05-26 14:45:26 +02:00
Linux User
0343659b35 Translated using Weblate (Deutsch)
Currently translated at 100,0% (371 of 371 strings)
2018-05-26 14:45:21 +02:00
Nathan Follens
f38eadbe30 Translated using Weblate (Vlaams)
Currently translated at 100,0% (371 of 371 strings)
2018-05-26 09:32:42 +02:00
Nathan Follens
eb29a53ac5 Translated using Weblate (Nederlands)
Currently translated at 100,0% (371 of 371 strings)
2018-05-26 09:29:31 +02:00
Weblate
bc71e260e2 Merge branch 'dev' into weblate-merge-tmp 2018-05-26 08:38:30 +02:00
Bogdan Khomutsky
ddf23a3443 Translated using Weblate (Russian)
Currently translated at 100.0% (365 of 365 strings)
2018-05-26 08:38:30 +02:00
Dual Natan
d41b248d1c Translated using Weblate (Macedonian)
Currently translated at 100.0% (365 of 365 strings)
2018-05-26 08:38:29 +02:00
SN
a025b25933 Translated using Weblate (Hindi)
Currently translated at 83.0% (303 of 365 strings)
2018-05-26 08:38:28 +02:00
Charlotte Lewer
c9a52a6088 Translated using Weblate (Esperanto)
Currently translated at 25.2% (92 of 365 strings)
2018-05-26 08:38:28 +02:00
My Account
0276dca406 Translated using Weblate (Hindi)
Currently translated at 83.0% (303 of 365 strings)
2018-05-26 08:38:22 +02:00
Christian Schabesberger
3bb95ad44c add changelog for version v0.13.4 2018-05-25 18:38:07 +02:00
Christian Schabesberger
0a6572c282 roll back to more stable version of newpipe extractor 2018-05-25 18:29:30 +02:00
Christian Schabesberger
3937067be1 move on to version 0.13.4 2018-05-25 09:45:22 +02:00
Christian Schabesberger
48e4eb44f2 remove unused imports 2018-05-25 09:43:28 +02:00
TobiGr
c78cc6f2fd Add dialog to accept privacy policy before sending crash report
Add link to privacy policy in about fragment
Replace some onClickListeners with Lamdas
2018-05-25 09:29:14 +02:00
Bogdan Khomutsky
73a71e0f5c Translated using Weblate (Russian)
Currently translated at 100.0% (365 of 365 strings)
2018-05-25 08:22:43 +02:00
SINUS (সাইনাস)
93605774f0 Translated using Weblate (Bengali (Bangladesh))
Currently translated at 40.0% (146 of 365 strings)
2018-05-24 18:34:46 +02:00
Daria Szatan
2834e5d78f Translated using Weblate (Polish)
Currently translated at 100.0% (365 of 365 strings)
2018-05-23 19:53:07 +02:00
Allan Nordhøy
dd467b4d63 Translated using Weblate (Norwegian Bokmål)
Currently translated at 96.1% (351 of 365 strings)
2018-05-22 11:40:49 +02:00
SN
1fa541776b Translated using Weblate (Hindi)
Currently translated at 80.0% (292 of 365 strings)
2018-05-22 08:37:59 +02:00
Weblate
f6f67c7b0a Merge branch 'dev' into weblate-merge-tmp 2018-05-21 06:37:51 +02:00
lartial
9b3f19c19b Translated using Weblate (Indonesian)
Currently translated at 100.0% (365 of 365 strings)
2018-05-21 06:37:49 +02:00
Christian Schabesberger
f4a9ec15e8 Merge pull request #1407 from DafabHoid/dev
Downloader: Fix crash on loading unfinished downloads from .giga files
2018-05-19 16:19:33 +02:00
Osoitz
006c4ecb02 Translated using Weblate (Basque)
Currently translated at 100.0% (365 of 365 strings)
2018-05-19 10:34:39 +02:00
Nishargo Nigar
1c752b0e18 Translated using Weblate (Bengali (Bangladesh))
Currently translated at 34.2% (125 of 365 strings)
2018-05-19 08:34:45 +02:00
AB
f84aff63e3 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (365 of 365 strings)
2018-05-18 19:06:33 +02:00
DafabHoid
ae8121b680 Utility: Buffer the output to files when serializing 2018-05-18 18:23:32 +02:00
DafabHoid
882fbf9275 Fix crash on loading not yet finished downloads from .giga files 2018-05-18 18:18:37 +02:00
Dual Natan
0a1743251e Translated using Weblate (Macedonian)
Currently translated at 100.0% (365 of 365 strings)
2018-05-18 11:49:10 +02:00
Osoitz
3403a127c1 Translated using Weblate (Basque)
Currently translated at 100.0% (365 of 365 strings)
2018-05-18 10:24:13 +02:00
Robson Cassiano
9c5ca9f09d Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (365 of 365 strings)
2018-05-17 16:40:02 +02:00
David Adrião
73eea5608a Translated using Weblate (Portuguese)
Currently translated at 86.3% (315 of 365 strings)
2018-05-17 13:40:38 +02:00
Osoitz
f48aeb91f4 Translated using Weblate (Basque)
Currently translated at 98.9% (361 of 365 strings)
2018-05-17 11:34:34 +02:00
Robson Cassiano
0bda964ece Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (365 of 365 strings)
2018-05-16 16:21:22 +02:00
Ale-Ma
14f5d54b50 Translated using Weblate (Italian)
Currently translated at 100.0% (365 of 365 strings)
2018-05-16 00:13:39 +02:00
Freddy Morán Jr
105ac2f6ff Translated using Weblate (Spanish)
Currently translated at 99.4% (363 of 365 strings)
2018-05-15 20:43:45 +02:00
HashikDonthineni
160560f1fd Translated using Weblate (Telugu)
Currently translated at 35.3% (129 of 365 strings)
2018-05-15 17:42:23 +02:00
ditokp
deeb667d6f Translated using Weblate (Indonesian)
Currently translated at 100.0% (365 of 365 strings)
2018-05-15 13:48:13 +02:00
Ali Demirtas
78123ff6f5 Translated using Weblate (Turkish)
Currently translated at 100.0% (365 of 365 strings)
2018-05-14 21:09:52 +02:00
anonymous
9b1fdff22f Translated using Weblate (Romanian)
Currently translated at 82.7% (302 of 365 strings)
2018-05-14 19:46:37 +02:00
Ciprian
0275502796 Translated using Weblate (Romanian)
Currently translated at 82.7% (302 of 365 strings)
2018-05-14 19:46:32 +02:00
Weblate
8af475e319 Merge branch 'dev' into weblate-merge-tmp 2018-05-14 13:34:29 +02:00
Allan Nordhøy
2202c8f09e Translated using Weblate (Norwegian Bokmål)
Currently translated at 95.8% (350 of 365 strings)
2018-05-14 13:34:28 +02:00
ScratchBuild
adf309d3a8 Translated using Weblate (Japanese)
Currently translated at 77.5% (283 of 365 strings)
2018-05-14 13:34:26 +02:00
Marc Riera
3071314586 Translated using Weblate (Catalan)
Currently translated at 100.0% (365 of 365 strings)
2018-05-14 13:34:24 +02:00
thami simo
88e1df840d Translated using Weblate (Arabic)
Currently translated at 100.0% (365 of 365 strings)
2018-05-14 13:34:21 +02:00
Christian Schabesberger
23c1b66f6c add note to contribution description 2018-05-14 13:24:35 +02:00
ezjerry liao
b0318a1cce Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (365 of 365 strings)
2018-05-14 11:52:01 +02:00
Nathan Follens
c130a66e4d Translated using Weblate (Flemish)
Currently translated at 100.0% (365 of 365 strings)
2018-05-13 18:44:26 +02:00
ssantos
3386ba6d1b Translated using Weblate (German)
Currently translated at 100.0% (365 of 365 strings)
2018-05-13 16:41:08 +02:00
Marc Riera
9275569fa6 Translated using Weblate (Catalan)
Currently translated at 100.0% (365 of 365 strings)
2018-05-13 13:50:07 +02:00
Heimen Stoffels
2b23dfd0a6 Translated using Weblate (Dutch)
Currently translated at 100.0% (365 of 365 strings)
2018-05-13 13:46:49 +02:00
thami simo
2a13d9990e Translated using Weblate (Arabic)
Currently translated at 100.0% (365 of 365 strings)
2018-05-13 12:37:46 +02:00
Weblate
c9669b51c6 Merge branch 'dev' into weblate-merge-tmp 2018-05-13 11:34:46 +02:00
Osoitz
486c180b3c Translated using Weblate (Basque)
Currently translated at 98.9% (360 of 364 strings)
2018-05-13 11:34:43 +02:00
Christian Schabesberger
9eb5bf9b87 Merge pull request #1375 from acrosca/code_inspection
Code inspection
2018-05-12 14:21:37 +02:00
Christian Schabesberger
953a89f3a1 Merge branch 'settingsExport' of https://github.com/Somethingweirdhere/NewPipe into test 2018-05-12 13:34:05 +02:00
Christian Schabesberger
d638fa1434 use commit from newpipeextractor master 2018-05-11 18:00:28 +02:00
Christian Schabesberger
e6d700288c fix afiliate parse link failure 2018-05-11 18:00:28 +02:00
Christian Schabesberger
371f14cdc9 make compartible to yoututbe service restructure 2018-05-11 18:00:28 +02:00
Christian Schabesberger
0733ae2404 make compatible with encosing urlidhandler commit 2018-05-11 18:00:28 +02:00
ButterflyOfFire
b1731ebd49 Translated using Weblate (French)
Currently translated at 99.1% (361 of 364 strings)
2018-05-11 17:37:14 +02:00
Somethingweirdhere
342b3191ac Changed to lambda convention 2018-05-11 17:17:07 +02:00
zmni
cd39445245 Translated using Weblate (Indonesian)
Currently translated at 91.2% (332 of 364 strings)
2018-05-11 16:39:32 +02:00
Freddy Morán Jr
92eac67367 Translated using Weblate (Spanish)
Currently translated at 97.2% (354 of 364 strings)
2018-05-10 19:41:12 +02:00
Edwar Tikhonov
0e31a0c704 Translated using Weblate (Russian)
Currently translated at 100.0% (364 of 364 strings)
2018-05-10 11:42:55 +02:00
gensitu
d60c117a70 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (364 of 364 strings)
2018-05-10 11:36:24 +02:00
thami simo
69ccad5998 Translated using Weblate (Arabic)
Currently translated at 100.0% (364 of 364 strings)
2018-05-10 11:34:28 +02:00
Edwar Tikhonov
b6d22320e6 Translated using Weblate (Russian)
Currently translated at 100.0% (364 of 364 strings)
2018-05-09 11:12:45 +02:00
Weblate
d3bf948dba Merge remote-tracking branch 'origin/dev' into dev 2018-05-09 09:50:28 +02:00
ditokp
5de3d96b31 Translated using Weblate (Indonesian)
Currently translated at 85.7% (312 of 364 strings)
2018-05-09 09:50:28 +02:00
Florian
d30dd64322 Translated using Weblate (French)
Currently translated at 99.1% (361 of 364 strings)
2018-05-09 09:50:27 +02:00
thami simo
386df10a5a Translated using Weblate (Arabic)
Currently translated at 100.0% (364 of 364 strings)
2018-05-09 09:50:25 +02:00
Christian Schabesberger
af147de547 upgrade gradle 2018-05-08 20:56:11 +02:00
anonymous
23cd0e5a5e Translated using Weblate (French)
Currently translated at 98.3% (358 of 364 strings)
2018-05-08 17:30:05 +02:00
anonymous
c5a566657c Translated using Weblate (French)
Currently translated at 98.3% (358 of 364 strings)
2018-05-08 16:21:31 +02:00
Alexander Sparzt
ce2018c864 Translated using Weblate (French)
Currently translated at 98.3% (358 of 364 strings)
2018-05-08 16:21:26 +02:00
anonymous
472ab46af2 Translated using Weblate (French)
Currently translated at 98.3% (358 of 364 strings)
2018-05-08 16:18:30 +02:00
Alexander Sparzt
7f87d45bb5 Translated using Weblate (French)
Currently translated at 98.3% (358 of 364 strings)
2018-05-08 16:18:25 +02:00
anonymous
e9f7ab18bb Translated using Weblate (French)
Currently translated at 98.3% (358 of 364 strings)
2018-05-08 16:16:29 +02:00
Alexander Sparzt
fba83a8afe Translated using Weblate (French)
Currently translated at 98.3% (358 of 364 strings)
2018-05-08 16:16:24 +02:00
nailyk
05089abddc Translated using Weblate (French)
Currently translated at 96.4% (351 of 364 strings)
2018-05-08 14:43:20 +02:00
Prabjot Singh
ccd70aac51 Translated using Weblate (Punjabi)
Currently translated at 5.2% (19 of 364 strings)
2018-05-08 07:41:59 +02:00
nailyk
5df8445d04 Translated using Weblate (French)
Currently translated at 96.1% (350 of 364 strings)

Peut-être existe des traductions existantes mais je ne les aies pas trouvées.
2018-05-07 18:17:34 +02:00
nailyk
8c43674fa4 Translated using Weblate (French)
Currently translated at 95.8% (349 of 364 strings)
2018-05-07 18:14:23 +02:00
Florent Peterschmitt
f162316a6b Translated using Weblate (French)
Currently translated at 94.5% (344 of 364 strings)
2018-05-07 14:37:09 +02:00
Andrea Troiano
670596ed88 Translated using Weblate (Italian)
Currently translated at 100.0% (364 of 364 strings)
2018-05-07 10:08:51 +02:00
Prabjot Singh
6b3eb716c4 Added translation using Weblate (Punjabi) 2018-05-07 06:39:45 +02:00
Emin Tufan Çetin
6a5180d94c Translated using Weblate (Turkish)
Currently translated at 100.0% (364 of 364 strings)
2018-05-06 17:16:30 +02:00
Florent Peterschmitt
83faaedfcc Translated using Weblate (French)
Currently translated at 93.4% (340 of 364 strings)
2018-05-06 13:54:09 +02:00
Florian
d98d790a7a Translated using Weblate (French)
Currently translated at 93.4% (340 of 364 strings)
2018-05-06 13:54:02 +02:00
Florian
36b5833a3a Translated using Weblate (French)
Currently translated at 93.4% (340 of 364 strings)
2018-05-06 13:51:12 +02:00
anonymous
5e86781a79 Translated using Weblate (French)
Currently translated at 89.5% (326 of 364 strings)
2018-05-06 13:27:25 +02:00
Florian
6a780504b4 Translated using Weblate (French)
Currently translated at 89.5% (326 of 364 strings)
2018-05-06 13:27:20 +02:00
Weblate
a0d8212136 Merge remote-tracking branch 'origin/dev' into dev 2018-05-06 13:01:58 +02:00
Marian Hanzel
0e1e6a9d62 Translated using Weblate (Slovak)
Currently translated at 95.8% (349 of 364 strings)
2018-05-06 13:01:58 +02:00
thami simo
812282a332 Translated using Weblate (Arabic)
Currently translated at 89.5% (326 of 364 strings)
2018-05-06 13:01:56 +02:00
r2308145
a8a4c9e97f Translated using Weblate (Czech)
Currently translated at 100.0% (364 of 364 strings)
2018-05-06 13:01:53 +02:00
Andrei.Rosca
24c293e335 fix context leaks 2018-05-06 10:50:02 +02:00
Andrei.Rosca
0a596df497 default ViewHolder 2018-05-06 10:14:24 +02:00
Andrei.Rosca
3d66c6572b prevent infinite loop 2018-05-06 10:08:56 +02:00
Andrei.Rosca
f45769cbb2 Reduce overdraw 2018-05-05 10:26:35 +02:00
Weblate
ef51f93c6f Merge remote-tracking branch 'origin/dev' into dev 2018-05-05 09:02:21 +02:00
ssantos
35af68f148 Translated using Weblate (German)
Currently translated at 100.0% (364 of 364 strings)
2018-05-05 09:02:19 +02:00
Christian Schabesberger
ff21430b43 move on to v0.13.3
also:
- reduce snack bar error visibility time
- fix metadata error
2018-05-04 16:21:36 +02:00
Weblate
fcb67f5119 Merge remote-tracking branch 'origin/dev' into dev 2018-05-04 09:40:15 +02:00
AB
0a41fbd185 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (364 of 364 strings)
2018-05-04 09:40:14 +02:00
Oleh Ilnytskyi
d32aaf488f Translated using Weblate (Russian)
Currently translated at 89.8% (327 of 364 strings)
2018-05-04 09:40:13 +02:00
Tobias Groza
1254798013 Translated using Weblate (German)
Currently translated at 92.5% (337 of 364 strings)
2018-05-04 09:40:12 +02:00
Heimen Stoffels
c72d2a2308 Translated using Weblate (Dutch)
Currently translated at 100.0% (364 of 364 strings)
2018-05-04 09:40:11 +02:00
Marc Riera
703181655b Translated using Weblate (Catalan)
Currently translated at 100.0% (364 of 364 strings)
2018-05-04 09:40:10 +02:00
Allan Nordhøy
da4a1c5bf0 Translated using Weblate (Norwegian Bokmål)
Currently translated at 94.5% (344 of 364 strings)
2018-05-04 09:40:06 +02:00
Christian Schabesberger
1130bd502e Merge pull request #1342 from Somethingweirdhere/popupplayerdont
PopUpPlayer now also has a Play and Pause button
2018-05-03 17:25:11 +02:00
AB
27ea4ee679 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (364 of 364 strings)
2018-05-03 14:36:57 +02:00
Heimen Stoffels
56e3b66d06 Translated using Weblate (Dutch)
Currently translated at 100.0% (364 of 364 strings)
2018-05-03 11:36:33 +02:00
Nathan Follens
7d4768e151 Translated using Weblate (Dutch)
Currently translated at 100.0% (364 of 364 strings)
2018-05-03 11:36:28 +02:00
Marc Riera
7ec1011610 Translated using Weblate (Catalan)
Currently translated at 100.0% (364 of 364 strings)
2018-05-03 11:18:20 +02:00
Nathan Follens
e1bbd2055c Translated using Weblate (Flemish)
Currently translated at 100.0% (364 of 364 strings)
2018-05-03 08:58:01 +02:00
Nathan Follens
8a19547d9f Translated using Weblate (Dutch)
Currently translated at 100.0% (364 of 364 strings)
2018-05-03 08:47:28 +02:00
Jeff Huang
6e6922dab8 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (364 of 364 strings)
2018-05-03 02:27:38 +02:00
Weblate
e009ade922 Merge remote-tracking branch 'origin/dev' into dev 2018-05-02 23:38:15 +02:00
Freddy Morán Jr
90e15bcab9 Translated using Weblate (Spanish)
Currently translated at 99.7% (362 of 363 strings)
2018-05-02 23:38:15 +02:00
Allan Nordhøy
2a040cea4b Translated using Weblate (Norwegian Bokmål)
Currently translated at 94.4% (343 of 363 strings)
2018-05-02 23:38:14 +02:00
Yoav
72f2a7f8db Translated using Weblate (Hebrew)
Currently translated at 86.7% (315 of 363 strings)
2018-05-02 23:38:11 +02:00
Somethingweirdhere
8a8022afe6 Now the play/pause button also has the correct scale! 2018-05-02 22:08:14 +02:00
Christian Schabesberger
b692bec310 Merge pull request #1350 from comradekingu/patch-6
Spelling: Rework new strings
2018-05-02 21:03:20 +02:00
Andrea Troiano
536c01c70d Translated using Weblate (Italian)
Currently translated at 100.0% (363 of 363 strings)
2018-05-02 10:15:53 +02:00
Allan Nordhøy
ec8e14e977 reverted the other "History &amp; Cache" 2018-05-02 09:18:04 +02:00
Allan Nordhøy
85b34f8809 New desc, "&amp; Cache" reverted 2018-05-02 09:16:53 +02:00
Lukas Wiedemann
22951a56a5 Translated using Weblate (German)
Currently translated at 100.0% (363 of 363 strings)
2018-05-01 23:37:09 +02:00
r2308145
46ad84b101 Translated using Weblate (Czech)
Currently translated at 100.0% (363 of 363 strings)
2018-05-01 22:58:31 +02:00
Marc Riera
5efb77e520 Translated using Weblate (Catalan)
Currently translated at 100.0% (363 of 363 strings)
2018-05-01 18:34:55 +02:00
Lukas Wiedemann
76903102b8 Translated using Weblate (German)
Currently translated at 100.0% (363 of 363 strings)
2018-04-30 23:26:35 +02:00
Nathan Follens
c8e26b429c Translated using Weblate (Flemish)
Currently translated at 100.0% (363 of 363 strings)
2018-04-30 21:49:04 +02:00
Marc Riera
55c1310f74 Translated using Weblate (Catalan)
Currently translated at 100.0% (363 of 363 strings)
2018-04-30 17:32:00 +02:00
Heimen Stoffels
b8278d91e0 Translated using Weblate (Dutch)
Currently translated at 100.0% (363 of 363 strings)
2018-04-30 16:58:57 +02:00
Marian Hanzel
b032502148 Translated using Weblate (Slovak)
Currently translated at 100.0% (363 of 363 strings)
2018-04-30 16:42:39 +02:00
Jeff Huang
bec1a4dd1a Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (363 of 363 strings)
2018-04-30 16:22:48 +02:00
AB
4dfb9e7977 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (363 of 363 strings)
2018-04-30 15:06:19 +02:00
Weblate
2bfa165cdc Merge remote-tracking branch 'origin/dev' into dev 2018-04-30 14:42:49 +02:00
AB
658666276d Translated using Weblate (Ukrainian)
Currently translated at 100.0% (354 of 354 strings)
2018-04-30 14:42:44 +02:00
Christian Schabesberger
62f91b9084 Merge pull request #1356 from DafabHoid/doubletap-playpause
Double-tap the middle of the player screen to pause the video
2018-04-29 17:16:09 +02:00
Christian Schabesberger
719140ab78 resolve conflict 2018-04-29 17:13:45 +02:00
Christian Schabesberger
0471fd8145 add clear orphans 2018-04-29 17:06:54 +02:00
Christian Schabesberger
a079a0c901 fix requested changes part 1 2018-04-29 13:15:52 +02:00
Christian Schabesberger
ac2fa74c8f merge chagnes with dev 2018-04-29 13:01:37 +02:00
Christian Schabesberger
4c10ef65f5 add delete while history
add delete whole history
2018-04-29 12:51:57 +02:00
Christian Schabesberger
cfa697fab2 make history ui more consistent 2018-04-29 12:51:57 +02:00
Christian Schabesberger
a09b9d3e4d made items actually deltable 2018-04-29 12:51:57 +02:00
Christian Schabesberger
c470909f19 add delete options for StatisticPlaylistFragment 2018-04-29 12:51:57 +02:00
Christian Schabesberger
5e59cfcf9d remove HistoryInfoItem again
blub
2018-04-29 12:51:57 +02:00
Christian Schabesberger
a099fe35d2 reorder playqueue/localPlaylist classes 2018-04-29 12:51:57 +02:00
Christian Schabesberger
bcfd8a2450 rename playlist to player.playqueue 2018-04-29 12:49:52 +02:00
Christian Schabesberger
8ed9d71e14 put local foo into local foo folder 2018-04-29 12:47:12 +02:00
Christian Schabesberger
004c2fa55a Project restructure for history part 1
- add HistoryInfoItem (deriving from StreamInfoItem)
  in order to add a special options menu for the list items
- delete HistoryActivity and everything that belongs to its UI (not the
  manager tho)
- put everything that is local into local. (subscriptions still missing)
2018-04-29 12:47:12 +02:00
DafabHoid
3dd63d03cb Double-tap the middle of the player screen to pause the video 2018-04-29 11:23:21 +02:00
AB
cceedab864 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (354 of 354 strings)
2018-04-29 10:34:40 +02:00
Andrei.Rosca
b494b2ea39 Remove GSON lib 2018-04-29 01:06:34 +02:00
Rubix
0b29cf086b Translated using Weblate (Romanian)
Currently translated at 86.7% (307 of 354 strings)
2018-04-27 21:40:56 +02:00
r2308145
11d33097f7 Translated using Weblate (Czech)
Currently translated at 100.0% (354 of 354 strings)
2018-04-27 11:35:54 +02:00
ButterflyOfFire
3ae61645de Translated using Weblate (Arabic)
Currently translated at 87.8% (311 of 354 strings)
2018-04-26 15:34:21 +02:00
Weblate
4711befffa Merge remote-tracking branch 'origin/dev' into dev 2018-04-25 02:40:49 +02:00
Robson Cassiano
5673d53a20 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (354 of 354 strings)
2018-04-25 02:40:47 +02:00
Christian Schabesberger
90ca4a5e92 Merge pull request #1346 from naXa777/dev
Fix #1345 Gradle build fails
2018-04-24 22:03:36 +02:00
Allan Nordhøy
ad252956ab Translated using Weblate (Norwegian Bokmål)
Currently translated at 94.3% (334 of 354 strings)
2018-04-24 21:39:29 +02:00
Marian Hanzel
1b2c091c39 Translated using Weblate (Slovak)
Currently translated at 100.0% (354 of 354 strings)
2018-04-24 09:40:59 +02:00
anonymous
9031bc0c7b Translated using Weblate (Portuguese (Brazil))
Currently translated at 99.7% (353 of 354 strings)
2018-04-24 02:28:17 +02:00
anonymous
1d85e0ea63 Translated using Weblate (Portuguese (Brazil))
Currently translated at 99.7% (353 of 354 strings)
2018-04-24 02:27:25 +02:00
Robson Cassiano
458774aadb Translated using Weblate (Portuguese (Brazil))
Currently translated at 99.7% (353 of 354 strings)
2018-04-24 02:27:20 +02:00
ButterflyOfFire
ae89f7bea3 Translated using Weblate (Arabic)
Currently translated at 83.8% (297 of 354 strings)
2018-04-23 23:34:19 +02:00
Allan Nordhøy
fd77b8552b Rework new strings 2018-04-23 22:07:27 +02:00
Marian Hanzel
bae9f5e844 Translated using Weblate (Slovak)
Currently translated at 100.0% (354 of 354 strings)
2018-04-23 08:05:45 +02:00
Paul
e3f3d90b68 Translated using Weblate (Russian)
Currently translated at 99.1% (351 of 354 strings)
2018-04-22 19:41:32 +02:00
James Straub
646fa877ba Update to mobile data limiting
- Moved non-key strings from string_keys.xml to strings.xml
- Code style changes
- Replaced a hard coded key string with resource constant
2018-04-22 10:20:19 -04:00
naXa!
7f3bd8aec2 Fix #1345 Gradle build fails with "error: unescaped apostrophe in string" 2018-04-22 14:31:59 +03:00
CaptainCrumble
4501203a7a Translated using Weblate (Portuguese)
Currently translated at 93.2% (330 of 354 strings)
2018-04-22 01:40:38 +02:00
Somethingweirdhere
06292bceb2 PopUpPlayer now has a play and pause button. Tapping now doesnt pause or unpause the video and instead shows this button. 2018-04-22 00:16:07 +02:00
Somethingweirdhere
f94f14ab65 Added settings export 2018-04-21 21:11:52 +02:00
AB
7145c68e03 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (354 of 354 strings)
2018-04-21 19:11:00 +02:00
James Straub
d1b0cd74be Added the ability to limit video quality if using mobile data.
* Added a dropdown to video & audio settings
* Changes to ListHelper:
** Limits resolution when code requests the default video resolution
** Limits audio bitrate when code requests the default audio bitrate
** Removed some dead code and did some cleanup
** Make methods private/protected to help understand what was in use
** The code now chooses one format over an other using a simple raking system defined in array constants. I realized I needed to do this in order to choose the most efficient video stream. I did my best to evaluate the video and audio formats based on quality and efficiency. It's not an exact science.
** Made changes to the tests to support my changes
2018-04-21 12:35:04 -04:00
wellinkstein
bac3825c87 Translated using Weblate (French)
Currently translated at 100.0% (354 of 354 strings)
2018-04-21 15:37:08 +02:00
Dante
fc1d283414 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (354 of 354 strings)
2018-04-21 09:35:29 +02:00
anonymous
c0652daa97 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (354 of 354 strings)
2018-04-21 09:35:24 +02:00
Q. A
553903bd9d Translated using Weblate (Albanian)
Currently translated at 12.7% (45 of 354 strings)
2018-04-21 09:34:14 +02:00
Freddy Morán Jr
a43ec25b7e Translated using Weblate (Spanish)
Currently translated at 99.7% (353 of 354 strings)
2018-04-20 16:42:38 +02:00
wellinkstein
bb2a66fd02 Translated using Weblate (French)
Currently translated at 100.0% (354 of 354 strings)
2018-04-20 14:41:22 +02:00
Ale-Ma
71ac830bfa Translated using Weblate (Italian)
Currently translated at 100.0% (354 of 354 strings)
2018-04-20 12:15:48 +02:00
r2308145
82bce80c62 Translated using Weblate (Czech)
Currently translated at 100.0% (354 of 354 strings)
2018-04-20 11:48:04 +02:00
Dante
67ddf78e18 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (354 of 354 strings)
2018-04-20 09:25:12 +02:00
Marc Riera
8285df0f3f Translated using Weblate (Catalan)
Currently translated at 99.1% (351 of 354 strings)
2018-04-19 22:35:00 +02:00
Somethingweirdhere
bdb45295b9 Merge pull request #2 from TeamNewPipe/dev
d
2018-04-19 15:30:12 +02:00
Emin Tufan Çetin
f330ee8f8d Translated using Weblate (Turkish)
Currently translated at 100.0% (354 of 354 strings)
2018-04-19 10:51:45 +02:00
Joseph Kim
7331e4a7f2 Translated using Weblate (Korean)
Currently translated at 100.0% (354 of 354 strings)
2018-04-19 04:40:40 +02:00
Jeff Huang
51252d3b61 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (354 of 354 strings)
2018-04-19 01:33:35 +02:00
Somethingweirdhere
dcdb2c323e Added settings export 2018-04-19 01:31:25 +02:00
Q. A
c4ac901c67 Added translation using Weblate (Albanian) 2018-04-18 23:38:25 +02:00
Tobias Groza
a7ce072ca2 Translated using Weblate (German)
Currently translated at 100.0% (354 of 354 strings)
2018-04-18 23:34:48 +02:00
Nathan Follens
a9b427b877 Translated using Weblate (Flemish)
Currently translated at 100.0% (354 of 354 strings)
2018-04-18 22:37:17 +02:00
Nathan Follens
67a9f3a4ad Added translation using Weblate (Flemish) 2018-04-18 22:12:00 +02:00
Nathan Follens
1a4905f36a Translated using Weblate (Dutch)
Currently translated at 100.0% (354 of 354 strings)
2018-04-18 21:57:15 +02:00
Weblate
cbb1fde7b0 Merge remote-tracking branch 'origin/dev' into dev 2018-04-18 21:41:20 +02:00
DafabHoid
b86bd019a7 Translated using Weblate (German)
Currently translated at 100.0% (353 of 353 strings)
2018-04-18 21:41:20 +02:00
anonymous
6a0bada9d2 Translated using Weblate (Polish)
Currently translated at 100.0% (353 of 353 strings)
2018-04-18 21:41:18 +02:00
Christian Schabesberger
a708278cf0 Merge pull request #1327 from Somethingweirdhere/dev
Fixed crash when trying to open a downloaded file without a player
2018-04-18 20:35:44 +02:00
Mauricio Colli
119462cbc9 Fix RouterActivity choice selection
- Improve behavior when external player is enabled
- Fixes #1324
2018-04-18 11:44:46 -03:00
Tobias Groza
0324a4928c Translated using Weblate (German)
Currently translated at 100.0% (353 of 353 strings)
2018-04-18 12:00:09 +02:00
syed muzammil
895a2a56b5 Translated using Weblate (Urdu)
Currently translated at 2.5% (9 of 353 strings)
2018-04-18 01:41:26 +02:00
Somethingweirdhere
d9e616beee Fixed crash when trying to open a downloaded file without a player 2018-04-17 22:26:24 +02:00
horyzont
aa5d5d2b6d Translated using Weblate (Polish)
Currently translated at 100.0% (353 of 353 strings)
2018-04-17 21:20:47 +02:00
Somethingweirdhere
85dc555358 Fixed crash when trying to open a downloaded file without a player 2018-04-17 19:47:17 +02:00
Somethingweirdhere
15b4a7d055 Fixed crash when trying to open a downloaded file without a player 2018-04-17 19:19:12 +02:00
Andrea Troiano
696c94050d Translated using Weblate (Italian)
Currently translated at 100.0% (353 of 353 strings)
2018-04-17 09:38:03 +02:00
DafabHoid
b222614c4a Translated using Weblate (German)
Currently translated at 100.0% (353 of 353 strings)
2018-04-16 22:58:47 +02:00
Emin Tufan Çetin
edff694bf3 Translated using Weblate (Turkish)
Currently translated at 100.0% (353 of 353 strings)
2018-04-16 22:42:33 +02:00
Jeff Huang
be430a6ac0 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (353 of 353 strings)
2018-04-16 13:03:02 +02:00
AB
937d40c5f7 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (353 of 353 strings)
2018-04-16 11:42:15 +02:00
Rubix
d3979676ab Translated using Weblate (Romanian)
Currently translated at 84.1% (297 of 353 strings)
2018-04-16 10:40:03 +02:00
Emin Tufan Çetin
6716262a28 Translated using Weblate (Turkish)
Currently translated at 100.0% (353 of 353 strings)
2018-04-15 22:11:00 +02:00
r2308145
06d8bafce6 Translated using Weblate (Czech)
Currently translated at 100.0% (353 of 353 strings)
2018-04-15 19:35:47 +02:00
Anton Shestakov
f814755908 Translated using Weblate (Russian)
Currently translated at 98.8% (349 of 353 strings)
2018-04-15 16:39:47 +02:00
AB
f355fd2551 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (353 of 353 strings)
2018-04-15 10:40:28 +02:00
Andrea Troiano
ea84c62d76 Translated using Weblate (Italian)
Currently translated at 100.0% (353 of 353 strings)
2018-04-14 21:24:40 +02:00
r2308145
acda71cebb Translated using Weblate (Czech)
Currently translated at 100.0% (353 of 353 strings)
2018-04-14 19:05:22 +02:00
Heimen Stoffels
67dcd2e5c6 Translated using Weblate (Dutch)
Currently translated at 100.0% (353 of 353 strings)
2018-04-14 17:42:09 +02:00
Marc Riera
c996644613 Translated using Weblate (Catalan)
Currently translated at 100.0% (353 of 353 strings)
2018-04-14 16:00:44 +02:00
M1ck
171c3e492d Translated using Weblate (French)
Currently translated at 100.0% (353 of 353 strings)
2018-04-14 12:53:15 +02:00
Weblate
8834195cc6 Merge remote-tracking branch 'origin/dev' into dev 2018-04-14 11:39:08 +02:00
M1ck
ed57e72fa1 Translated using Weblate (French)
Currently translated at 96.9% (344 of 355 strings)
2018-04-14 11:39:07 +02:00
Dar Mackani
7e84c3e167 Translated using Weblate (Esperanto)
Currently translated at 18.3% (65 of 355 strings)
2018-04-14 11:39:07 +02:00
gensitu
4adc33471b Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (355 of 355 strings)
2018-04-14 11:39:06 +02:00
gensitu
31d07cc1e2 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (355 of 355 strings)
2018-04-14 11:39:05 +02:00
Andrea Troiano
a349a66d5a Translated using Weblate (Italian)
Currently translated at 100.0% (355 of 355 strings)
2018-04-14 11:38:59 +02:00
Christian Schabesberger
7d427b4cc4 move on to v0.13.2 2018-04-14 11:22:26 +02:00
Christian Schabesberger
6c7e54868d Merge branch 'playlist_to_next' of https://github.com/Shan11812/NewPipe into test 2018-04-14 11:04:08 +02:00
Christian Schabesberger
9484a5e2ee fix conflict 2018-04-14 11:02:31 +02:00
Christian Schabesberger
3cbd2057e3 Merge branch 'qol-follow-ups' of https://github.com/karyogamy/NewPipe into test 2018-04-14 10:53:09 +02:00
Stanisław Sikorski
71ff1cf713 Translated using Weblate (Polish)
Currently translated at 100.0% (355 of 355 strings)
2018-04-14 00:27:38 +02:00
Rintaro matsuo
c737d891bc Translated using Weblate (Japanese)
Currently translated at 81.1% (288 of 355 strings)
2018-04-13 04:39:41 +02:00
ScratchBuild
82a53343fc Translated using Weblate (Japanese)
Currently translated at 81.1% (288 of 355 strings)
2018-04-13 04:39:36 +02:00
Andrea Troiano
03fdc60018 Translated using Weblate (Italian)
Currently translated at 100.0% (355 of 355 strings)
2018-04-12 12:15:57 +02:00
John Zhen Mo
42d19d98ad -Changed media source manager near edge loading to no longer load while player position is not progressing.
-Changed main video player to always self-destruct on stop.
-Extracted main video player lifecycle states into separate data class.
-Fixed play queue in full repeat mode does not load first item after expiring.
2018-04-11 20:27:39 -07:00
John Zhen Mo
c9915bba18 -Reversed special seek logic for short buffer livestreams.
-Fixed loader cleaning potentially canceling existing correct loading items.
-Updated ExoPlayer to 2.7.3.
2018-04-11 20:27:39 -07:00
John Zhen Mo
a275d7ff50 -Rollbacks the original main player UI to display nav and status bar on click.
-Changed system UI color to translucent on Lollipop and above.
2018-04-11 20:27:39 -07:00
John Zhen Mo
74199c8624 -Designated background player as default media button receiver.
-Fixed media button intent causing illegal state exception when sent from external apps.
2018-04-11 20:27:38 -07:00
John Zhen Mo
238bff1fee -Modified adaptive track selection to upgrade with 1 second of buffer.
-Modified dynamic track updates to seek to default time instead of clamping to 0 time when negative progress is reached.
2018-04-11 20:27:38 -07:00
John Zhen Mo
de534b58c5 -Removed playlist cleaning. 2018-04-11 20:27:38 -07:00
John Zhen Mo
111a0f9f17 -Modified playback parameter dialog to use translucent background. (reverted from commit 0d25254d4831aca92ad6cf6c0c772279b32b4a07) 2018-04-11 20:27:38 -07:00
John Zhen Mo
50392ed67d -Changed failed media source exception to use cause instead of top level exception. 2018-04-11 20:27:38 -07:00
John Zhen Mo
3de9da0528 -Bump exoplayer dependency to 2.7.2. 2018-04-11 20:27:38 -07:00
John Zhen Mo
ece93cadd5 -Added better player exception handling to player.
-Added expired media source cleaning to media source manager.
2018-04-11 20:27:38 -07:00
John Zhen Mo
7219c8d33c -Fixed main player multiwindow pauses when focus changes. 2018-04-11 20:27:38 -07:00
John Zhen Mo
b0a09c7876 -Added "add to playlist" button to service player play queue item drop down.
-Refactored playlist manipulations out from media source manager.
-Fixed potential ArrayOutOfBound exception when checking if player window is live.
-Fixed service player play queue potentially not refreshing when current play queue is replaced.
2018-04-11 20:27:38 -07:00
John Zhen Mo
1d017d3cbc -Modified LIVE button to be untranslatable on all players. 2018-04-11 20:27:38 -07:00
John Zhen Mo
5fa56f2aca -Modified playback parameter dialog to use translucent background. 2018-04-11 20:27:37 -07:00
Freddy Morán Jr
5ded1b139f Translated using Weblate (Spanish)
Currently translated at 99.7% (354 of 355 strings)
2018-04-11 18:41:45 +02:00
r2308145
2abb0db272 Translated using Weblate (Czech)
Currently translated at 100.0% (355 of 355 strings)
2018-04-11 18:35:42 +02:00
Marc Riera
674c041c13 Translated using Weblate (Catalan)
Currently translated at 100.0% (354 of 354 strings)
2018-04-11 10:09:56 +02:00
Marc Riera
03c89a69a2 Translated using Weblate (Catalan)
Currently translated at 95.7% (339 of 354 strings)
2018-04-11 09:30:46 +02:00
Marc Riera
dbed5e2a62 Translated using Weblate (Catalan)
Currently translated at 76.8% (272 of 354 strings)
2018-04-11 09:30:08 +02:00
Sérgio Marques
d7519903a1 Translated using Weblate (Portuguese)
Currently translated at 80.0% (284 of 355 strings)
2018-04-11 00:40:00 +02:00
E T
ab14f0d7ac Translated using Weblate (Turkish)
Currently translated at 100.0% (355 of 355 strings)
2018-04-10 23:03:32 +02:00
Marc Riera
3b6e43b7ca Translated using Weblate (Catalan)
Currently translated at 76.8% (272 of 354 strings)
2018-04-10 22:41:59 +02:00
Marc Riera
7d3b7af874 Translated using Weblate (Catalan)
Currently translated at 32.2% (114 of 354 strings)
2018-04-10 19:28:24 +02:00
r2308145
2acc348062 Translated using Weblate (Czech)
Currently translated at 100.0% (355 of 355 strings)
2018-04-10 18:01:41 +02:00
Freddy Morán Jr
33ba8acd05 Translated using Weblate (Spanish)
Currently translated at 100.0% (355 of 355 strings)
2018-04-10 17:49:03 +02:00
ezjerry liao
0c56af7090 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (355 of 355 strings)
2018-04-10 15:01:23 +02:00
Marc Riera
4375ecaad7 Translated using Weblate (Catalan)
Currently translated at 32.2% (114 of 354 strings)
2018-04-10 13:52:48 +02:00
nagizade
90c538d4b6 Translated using Weblate (Azerbaijani)
Currently translated at 12.9% (46 of 355 strings)
2018-04-10 11:34:35 +02:00
Dante
1ffa8329d0 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (355 of 355 strings)
2018-04-10 05:32:24 +02:00
Dante
b404b7974a Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (355 of 355 strings)
2018-04-10 05:20:16 +02:00
E T
de44b580f9 Translated using Weblate (Turkish)
Currently translated at 100.0% (355 of 355 strings)
2018-04-09 22:53:10 +02:00
Roger
2a630cd745 Added translation using Weblate (Chinese (Mandarin)) 2018-04-09 22:31:57 +02:00
ssantos
1ccccc5e39 Translated using Weblate (German)
Currently translated at 100.0% (355 of 355 strings)
2018-04-09 22:31:35 +02:00
Roger
aec851acfa Added translation using Weblate (Urdu) 2018-04-09 22:28:18 +02:00
nagizade
ad5ac479fd Added translation using Weblate (Azerbaijani) 2018-04-09 10:55:09 +02:00
Heimen Stoffels
67b82a404c Translated using Weblate (Dutch)
Currently translated at 100.0% (355 of 355 strings)
2018-04-09 10:45:45 +02:00
Weblate
f7f86a2f62 Merge remote-tracking branch 'origin/dev' into dev 2018-04-09 00:13:42 +02:00
Anton Shestakov
6e42b4af27 Translated using Weblate (Russian)
Currently translated at 98.3% (348 of 354 strings)
2018-04-09 00:13:42 +02:00
Filip Sebastian
fe9ed8af76 Translated using Weblate (Romanian)
Currently translated at 74.8% (265 of 354 strings)
2018-04-09 00:13:40 +02:00
Eduardo Caron
c4c3f369c0 Translated using Weblate (Portuguese (Brazil))
Currently translated at 98.8% (350 of 354 strings)
2018-04-09 00:13:40 +02:00
M1ck
9b9bf40e47 Translated using Weblate (French)
Currently translated at 97.1% (344 of 354 strings)
2018-04-09 00:13:38 +02:00
Marc Riera
14a888a610 Added translation using Weblate (Catalan) 2018-04-09 00:13:35 +02:00
John Zhen Mo
f3d777c65c -Added accessibility caption style and text color resolver.
-Added button to open captioning settings activity from appearance settings.
-Modified player captions to use custom caption style when captioning manager is enabled.
-Removed caption size settings from appearance settings.
2018-04-08 13:58:55 -07:00
Mauricio Colli
d494b6c934 Long-click to open the downloads activity
- Closes #1263
2018-04-08 16:53:22 -03:00
Mauricio Colli
7294220727 Revert removal of menu items 2018-04-08 16:26:47 -03:00
Christian Schabesberger
665b9087b1 fix conflict 2018-04-08 18:49:27 +02:00
Christian Schabesberger
f1691050cd Merge branch 'download-permissions-screen' of https://github.com/arispoloway/NewPipe into perm 2018-04-08 18:21:27 +02:00
Aris Poloway
169b6acd24 Clean up download dialog opening 2018-04-08 11:47:11 -04:00
Mauricio Colli
35b3bf2edb Show download size preview 2018-04-08 08:45:15 -03:00
Christian Schabesberger
8f73c8c98b Merge pull request #1288 from jaller94/shot-to-short
Rename shot_description.txt to short_description.txt
2018-04-08 12:33:37 +02:00
Christian Schabesberger
94ea8c21eb Merge pull request #1289 from jaller94/german-translation
Add German translation of the description for fastlane
2018-04-08 12:32:53 +02:00
anonymous
b6a19c3e2d Translated using Weblate (French)
Currently translated at 97.1% (344 of 354 strings)
2018-04-08 12:06:00 +02:00
M1ck
21455d58a6 Translated using Weblate (French)
Currently translated at 97.1% (344 of 354 strings)
2018-04-08 12:05:55 +02:00
Christian Paul
239e9bd3db Rename shot_description.txt to short_description.txt 2018-04-08 01:46:17 -07:00
Christian Paul
0832171a2b Add German translation of the description for fastlane 2018-04-08 01:38:25 -07:00
Anton Shestakov
1edfcc629c Translated using Weblate (Russian)
Currently translated at 97.7% (346 of 354 strings)
2018-04-08 09:34:00 +02:00
ezjerry liao
9a2a8698ef Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (354 of 354 strings)
2018-04-08 03:28:46 +02:00
Nathan Follens
54c31422d9 Translated using Weblate (Dutch)
Currently translated at 100.0% (354 of 354 strings)
2018-04-08 01:49:32 +02:00
Filip Sebastian
a33464284c Translated using Weblate (Romanian)
Currently translated at 74.8% (265 of 354 strings)
2018-04-08 00:57:40 +02:00
Filip Sebastian
4842caf426 Translated using Weblate (Romanian)
Currently translated at 74.8% (265 of 354 strings)
2018-04-08 00:53:04 +02:00
Filip Sebastian
02b6b4d8eb Translated using Weblate (Romanian)
Currently translated at 74.2% (263 of 354 strings)
2018-04-08 00:18:05 +02:00
Filip Sebastian
4b0d7c7d88 Translated using Weblate (Romanian)
Currently translated at 74.2% (263 of 354 strings)
2018-04-08 00:17:57 +02:00
Filip Sebastian
559b53acc3 Translated using Weblate (Romanian)
Currently translated at 74.2% (263 of 354 strings)
2018-04-08 00:17:51 +02:00
Filip Sebastian
4233c18dbb Translated using Weblate (Romanian)
Currently translated at 70.3% (249 of 354 strings)
2018-04-08 00:11:00 +02:00
Filip Sebastian
adec2c9fcc Translated using Weblate (Romanian)
Currently translated at 70.0% (248 of 354 strings)
2018-04-08 00:10:48 +02:00
Filip Sebastian
6ad2406262 Translated using Weblate (Romanian)
Currently translated at 70.0% (248 of 354 strings)
2018-04-08 00:10:43 +02:00
Filip Sebastian
bfeb8ca104 Translated using Weblate (Romanian)
Currently translated at 69.7% (247 of 354 strings)
2018-04-08 00:10:34 +02:00
Filip Sebastian
5614edfa2f Translated using Weblate (Romanian)
Currently translated at 68.9% (244 of 354 strings)
2018-04-08 00:00:14 +02:00
Filip Sebastian
946f7b2305 Translated using Weblate (Romanian)
Currently translated at 68.9% (244 of 354 strings)
2018-04-07 23:59:49 +02:00
Filip Sebastian
dfcf435f88 Translated using Weblate (Romanian)
Currently translated at 67.7% (240 of 354 strings)
2018-04-07 23:53:12 +02:00
Filip Sebastian
0e734c267c Translated using Weblate (Romanian)
Currently translated at 66.9% (237 of 354 strings)
2018-04-07 23:49:06 +02:00
Filip Sebastian
faadcec4d1 Translated using Weblate (Romanian)
Currently translated at 66.6% (236 of 354 strings)
2018-04-07 23:47:13 +02:00
Filip Sebastian
eb81784818 Translated using Weblate (Romanian)
Currently translated at 66.3% (235 of 354 strings)
2018-04-07 23:45:01 +02:00
Filip Sebastian
327010f76b Translated using Weblate (Romanian)
Currently translated at 66.3% (235 of 354 strings)
2018-04-07 23:44:26 +02:00
Filip Sebastian
371669dcb6 Translated using Weblate (Romanian)
Currently translated at 66.3% (235 of 354 strings)
2018-04-07 23:44:19 +02:00
Filip Sebastian
fb702b93ca Translated using Weblate (Romanian)
Currently translated at 66.3% (235 of 354 strings)
2018-04-07 23:37:57 +02:00
Filip Sebastian
50c8453785 Translated using Weblate (Romanian)
Currently translated at 66.3% (235 of 354 strings)
2018-04-07 23:37:43 +02:00
Filip Sebastian
14fb5ee6c4 Translated using Weblate (Romanian)
Currently translated at 66.3% (235 of 354 strings)
2018-04-07 23:37:29 +02:00
Filip Sebastian
46c2343ec6 Translated using Weblate (Romanian)
Currently translated at 66.3% (235 of 354 strings)
2018-04-07 23:35:42 +02:00
Filip Sebastian
f5c226362f Translated using Weblate (Romanian)
Currently translated at 65.8% (233 of 354 strings)
2018-04-07 23:24:43 +02:00
Filip Sebastian
9114c1157d Translated using Weblate (Romanian)
Currently translated at 64.9% (230 of 354 strings)
2018-04-07 23:22:38 +02:00
Filip Sebastian
a4f4230275 Translated using Weblate (Romanian)
Currently translated at 64.9% (230 of 354 strings)
2018-04-07 23:22:21 +02:00
Filip Sebastian
46a6147c08 Translated using Weblate (Romanian)
Currently translated at 64.9% (230 of 354 strings)
2018-04-07 23:21:19 +02:00
Filip Sebastian
0d26923d79 Translated using Weblate (Romanian)
Currently translated at 64.9% (230 of 354 strings)
2018-04-07 23:21:09 +02:00
Filip Sebastian
b57c93cf03 Translated using Weblate (Romanian)
Currently translated at 64.6% (229 of 354 strings)
2018-04-07 23:20:04 +02:00
Filip Sebastian
ec19e4bc8f Translated using Weblate (Romanian)
Currently translated at 64.6% (229 of 354 strings)
2018-04-07 23:19:36 +02:00
Filip Sebastian
cb68a9c2ce Translated using Weblate (Romanian)
Currently translated at 64.1% (227 of 354 strings)
2018-04-07 23:16:44 +02:00
Filip Sebastian
b51ded1580 Translated using Weblate (Romanian)
Currently translated at 64.1% (227 of 354 strings)
2018-04-07 23:16:38 +02:00
Filip Sebastian
4b6fb5bfeb Translated using Weblate (Romanian)
Currently translated at 63.8% (226 of 354 strings)
2018-04-07 23:08:51 +02:00
Filip Sebastian
9b8175b5be Translated using Weblate (Romanian)
Currently translated at 63.5% (225 of 354 strings)
2018-04-07 23:07:58 +02:00
Filip Sebastian
3ecfc622e2 Translated using Weblate (Romanian)
Currently translated at 63.5% (225 of 354 strings)
2018-04-07 23:07:35 +02:00
Filip Sebastian
d8d2ee4e09 Translated using Weblate (Romanian)
Currently translated at 63.5% (225 of 354 strings)
2018-04-07 23:06:38 +02:00
Filip Sebastian
be478cb088 Translated using Weblate (Romanian)
Currently translated at 62.7% (222 of 354 strings)
2018-04-07 22:57:24 +02:00
Filip Sebastian
c187b4487f Translated using Weblate (Romanian)
Currently translated at 62.7% (222 of 354 strings)
2018-04-07 22:54:09 +02:00
Weblate
f6d21e74cb Merge remote-tracking branch 'origin/dev' into dev 2018-04-07 22:50:27 +02:00
Filip Sebastian
bde4c4bd25 Translated using Weblate (Romanian)
Currently translated at 62.4% (221 of 354 strings)
2018-04-07 22:50:25 +02:00
Mauricio Colli
d9aaceea95 Fix naming inconsistency 2018-04-07 16:09:34 -03:00
Aris Poloway
676d64a24a Open download dialog if video download is clicked instead 2018-04-07 13:32:31 -04:00
Weblate
8ae1768f71 Merge remote-tracking branch 'origin/dev' into dev 2018-04-07 13:37:23 +02:00
anonymous
212705e7e3 Translated using Weblate (German)
Currently translated at 99.4% (352 of 354 strings)
2018-04-07 13:37:22 +02:00
Andreas Kromke
b3f6524e5c Translated using Weblate (German)
Currently translated at 99.4% (352 of 354 strings)
2018-04-07 13:37:20 +02:00
Aris Poloway
5ca0a0adf2 Open downloads after permission granted 2018-04-06 17:29:58 -04:00
Mauricio Colli
669d2c44c9 Dynamically create the opening choices
- Fixes TODO
2018-04-06 06:02:51 -03:00
Mauricio Colli
604bc5b4c1 Merge pull request #1255 from Poussinou/patch-1
Add distributionSha256Sum property to gradle-wrapper.properties
2018-04-06 04:50:19 -03:00
Mauricio Colli
88268ae569 Merge pull request #1268 from arispoloway/dev
Handle links with time specified (ex. &t=xxx)
2018-04-06 04:49:02 -03:00
Mauricio Colli
140fb86401 Fix Info's start time when using VideoDetailFragment
- Update extractor dependency
2018-04-06 04:35:44 -03:00
Aris Poloway
2b281c4357 Don't save stream and store recovery position from StreamInfo 2018-04-05 23:47:20 -04:00
MohammedSR Vevo
7b6dbfb456 Translated using Weblate (Kurdish)
Currently translated at 18.0% (64 of 354 strings)
2018-04-06 00:37:32 +02:00
Aris Poloway
b3c49ac86b Revert unnecessary url clean 2018-04-05 16:45:00 -04:00
Dalibor Klobučarić
d309fd9c97 Translated using Weblate (Croatian)
Currently translated at 77.4% (274 of 354 strings)
2018-04-05 11:35:13 +02:00
Aris Poloway
f3a280dcb6 Don't preemptively clean url and save StreamInfo with PlayQueueItem 2018-04-04 20:43:14 -04:00
MORTI
43894687a2 Translated using Weblate (French)
Currently translated at 96.3% (341 of 354 strings)
2018-04-05 01:40:08 +02:00
MohammedSR Vevo
8dd8928dab Translated using Weblate (Kurdish)
Currently translated at 4.2% (15 of 354 strings)
2018-04-04 23:26:31 +02:00
MohammedSR Vevo
282a4d15c2 Translated using Weblate (Kurdish)
Currently translated at 3.3% (12 of 354 strings)
2018-04-04 23:25:25 +02:00
MohammedSR Vevo
83a2ffc67e Added translation using Weblate (Kurdish) 2018-04-04 23:13:05 +02:00
Eduardo Caron
e9a6e3fae0 Translated using Weblate (Portuguese (Brazil))
Currently translated at 98.0% (347 of 354 strings)
2018-04-04 17:55:55 +02:00
Eduardo Caron
97944b9793 Translated using Weblate (Portuguese (Brazil))
Currently translated at 96.6% (342 of 354 strings)
2018-04-04 17:43:44 +02:00
Çağdaş Tatar
0b5d995b6d Translated using Weblate (Turkish)
Currently translated at 100.0% (354 of 354 strings)
2018-04-04 16:48:18 +02:00
Çağdaş Tatar
1781a9fe9a Translated using Weblate (Turkish)
Currently translated at 99.4% (352 of 354 strings)
2018-04-04 15:53:38 +02:00
Çağdaş Tatar
a0479bf7ca Translated using Weblate (Turkish)
Currently translated at 99.4% (352 of 354 strings)
2018-04-04 15:48:54 +02:00
r2308145
2affa31edf Translated using Weblate (Czech)
Currently translated at 100.0% (354 of 354 strings)
2018-04-04 13:35:50 +02:00
Poussinou
0bd040a851 Update gradle to 4.6 (and add corresponding distributionSha256Sum) 2018-04-04 13:33:21 +02:00
Tobias Groza
616c6a1607 Translated using Weblate (German)
Currently translated at 99.4% (352 of 354 strings)
2018-04-04 12:38:10 +02:00
anonymous
a5176fbf80 Translated using Weblate (German)
Currently translated at 99.4% (352 of 354 strings)
2018-04-04 12:38:01 +02:00
Nick Undnick
f2b76b7582 Translated using Weblate (German)
Currently translated at 99.4% (352 of 354 strings)
2018-04-04 12:37:56 +02:00
ssantos
f4847f31ae Translated using Weblate (German)
Currently translated at 99.4% (352 of 354 strings)
2018-04-04 12:37:51 +02:00
Dalibor Klobučarić
05adcf2c12 Translated using Weblate (Croatian)
Currently translated at 76.2% (270 of 354 strings)
2018-04-04 10:39:56 +02:00
Dalibor Klobučarić
ea301be3c1 Translated using Weblate (Croatian)
Currently translated at 76.2% (270 of 354 strings)
2018-04-04 10:39:45 +02:00
Dalibor Klobučarić
88e9785b01 Translated using Weblate (Croatian)
Currently translated at 72.8% (258 of 354 strings)
2018-04-04 10:36:04 +02:00
Dual Natan
78efabfc1c Translated using Weblate (Macedonian)
Currently translated at 100.0% (354 of 354 strings)
2018-04-04 05:38:53 +02:00
Poussinou
9d3e22200c Update gradle-wrapper.properties 2018-04-03 21:59:42 +02:00
ssantos
183f9701fd Translated using Weblate (German)
Currently translated at 99.4% (352 of 354 strings)
2018-04-03 12:16:27 +02:00
ssantos
ebbba134fe Translated using Weblate (German)
Currently translated at 98.8% (350 of 354 strings)
2018-04-03 12:07:17 +02:00
ssantos
c257be8176 Translated using Weblate (German)
Currently translated at 98.3% (348 of 354 strings)
2018-04-03 12:00:19 +02:00
Dual Natan
afb0aea660 Translated using Weblate (Macedonian)
Currently translated at 100.0% (354 of 354 strings)
2018-04-03 05:17:57 +02:00
Dual Natan
960bf46f44 Translated using Weblate (Macedonian)
Currently translated at 77.9% (276 of 354 strings)
2018-04-03 04:42:16 +02:00
Dual Natan
16db799e88 Translated using Weblate (Macedonian)
Currently translated at 57.0% (202 of 354 strings)
2018-04-03 04:12:55 +02:00
Dual Natan
f8f2cdcfcc Translated using Weblate (Macedonian)
Currently translated at 42.0% (149 of 354 strings)
2018-04-03 03:40:03 +02:00
Dual Natan
a1f7862f96 Translated using Weblate (Macedonian)
Currently translated at 7.9% (28 of 354 strings)
2018-04-03 02:29:01 +02:00
Dual Natan
3ff9284f2a Added translation using Weblate (Macedonian) 2018-04-03 02:07:39 +02:00
Weblate
edf8f27c0f Merge remote-tracking branch 'origin/dev' into dev 2018-04-02 23:39:25 +02:00
horyzont
a720953ff3 Translated using Weblate (Polish)
Currently translated at 100.0% (354 of 354 strings)
2018-04-02 23:39:23 +02:00
Christian Schabesberger
f79dd26a82 remove outdated ccc slides 2018-04-02 20:54:26 +02:00
Christian Schabesberger
36b5fce4dd remove translation stuff 2018-04-02 20:53:56 +02:00
Christian Schabesberger
89537322fd fix email button not working 2018-04-02 20:53:32 +02:00
r2308145
2229ce1fe9 Translated using Weblate (Czech)
Currently translated at 100.0% (354 of 354 strings)
2018-04-02 16:36:12 +02:00
r2308145
f257b2177a Translated using Weblate (Czech)
Currently translated at 98.5% (349 of 354 strings)
2018-04-02 16:33:35 +02:00
r2308145
056c1fd43e Translated using Weblate (Czech)
Currently translated at 97.7% (346 of 354 strings)
2018-04-02 14:59:50 +02:00
r2308145
acacd3134f Translated using Weblate (Czech)
Currently translated at 96.8% (343 of 354 strings)
2018-04-02 14:58:11 +02:00
r2308145
1fe0ba1a6e Translated using Weblate (Czech)
Currently translated at 96.3% (341 of 354 strings)
2018-04-02 14:43:01 +02:00
r2308145
e639f4b5de Translated using Weblate (Czech)
Currently translated at 95.4% (338 of 354 strings)
2018-04-02 14:35:57 +02:00
r2308145
5a234bd989 Translated using Weblate (Czech)
Currently translated at 94.6% (335 of 354 strings)
2018-04-02 14:34:55 +02:00
r2308145
661227266e Translated using Weblate (Czech)
Currently translated at 88.9% (315 of 354 strings)
2018-04-02 14:22:33 +02:00
horyzont
e01556fd9a Translated using Weblate (Polish)
Currently translated at 100.0% (354 of 354 strings)
2018-04-01 23:03:18 +02:00
horyzont
5cb53ffc18 Translated using Weblate (Polish)
Currently translated at 100.0% (354 of 354 strings)
2018-04-01 23:01:44 +02:00
horyzont
e7178626ab Translated using Weblate (Polish)
Currently translated at 100.0% (354 of 354 strings)
2018-04-01 22:56:07 +02:00
horyzont
7f0948b0ed Translated using Weblate (Polish)
Currently translated at 99.7% (353 of 354 strings)
2018-04-01 22:50:49 +02:00
Weblate
4baee67781 Merge remote-tracking branch 'origin/dev' into dev 2018-04-01 22:41:06 +02:00
Heimen Stoffels
7061f38abe Translated using Weblate (Dutch)
Currently translated at 100.0% (354 of 354 strings)
2018-04-01 22:41:06 +02:00
horyzont
2aa259c0b2 Translated using Weblate (Polish)
Currently translated at 98.3% (348 of 354 strings)
2018-04-01 22:41:02 +02:00
shan11812
2837e44bab Adds 'Add to Playlist' option to Related and Channel lists 2018-04-01 20:47:24 +05:30
499 changed files with 33054 additions and 10485 deletions

View File

@@ -5,42 +5,64 @@ PLEASE READ THESE GUIDELINES CAREFULLY BEFORE ANY CONTRIBUTION!
## Crash reporting
Do not report crashes in the GitHub issue tracker. NewPipe has an automated crash report system that will ask you to send a report via e-mail when a crash occurs. This contains all the data we need for debugging, and allows you to even add a comment to it. You'll see exactly what is sent, the system is 100% transparent.
Do not report crashes in the GitHub issue tracker. NewPipe has an automated crash report system that will ask you to
send a report via e-mail when a crash occurs. This contains all the data we need for debugging, and allows you to even
add a comment to it. You'll see exactly what is sent, the system is 100% transparent.
## Issue reporting/feature requests
* Search the [existing issues](https://github.com/TeamNewPipe/NewPipe/issues) first to make sure your issue/feature hasn't been reported/requested before
* Search the [existing issues](https://github.com/TeamNewPipe/NewPipe/issues) first to make sure your issue/feature
hasn't been reported/requested before
* Check whether your issue/feature is already fixed/implemented
* Check if the issue still exists in the latest release/beta version
* If you are an Android/Java developer, you are always welcome to fix/implement an issue/a feature yourself. PRs welcome!
* We use English for development. Issues in other languages will be closed and ignored.
* Please only add *one* issue at a time. Do not put multiple issues into one thread.
* When reporting a bug please give us a context, and a description how to reproduce it.
* Issues that only contain a generated bug report, but no description might be closed.
## Bug Fixing
* If you want to help NewPipe to become free of bugs (this is our utopic goal for NewPipe), you can send us an email to tnp@newpipe.schabi.org to let me know that you intend to help. We'll send you further instructions. You may, on request, register at our [Sentry](https://sentry.schabi.org) instance (see section "Crash reporting" for more information.
* If you want to help NewPipe to become free of bugs (this is our utopic goal for NewPipe), you can send us an email to
tnp@newpipe.schabi.org to let me know that you intend to help. We'll send you further instructions. You may, on request,
register at our [Sentry](https://sentry.schabi.org) instance (see section "Crash reporting" for more information.
## Translation
* NewPipe can be translated via [Weblate](https://hosted.weblate.org/projects/newpipe/strings/). You can log in there with your GitHub account.
* NewPipe can be translated via [Weblate](https://hosted.weblate.org/projects/newpipe/strings/). You can log in there
with your GitHub account.
## Code contribution
* Stick to NewPipe's style conventions (well, just look the other code and then do it the same way :))
* Do not bring non-free software (e.g., binary blobs) into the project. Also, make sure you do not introduce Google libraries.
* Do not bring non-free software (e.g., binary blobs) into the project. Also, make sure you do not introduce Google
libraries.
* Stick to [F-Droid contribution guidelines](https://f-droid.org/wiki/page/Inclusion_Policy)
* Make changes on a separate branch, not on the master branch. This is commonly known as *feature branch workflow*. You may then send your changes as a pull request on GitHub. Patches to the email address mentioned in this document might not be considered, GitHub is the primary platform.
* When submitting changes, you confirm that your code is licensed under the terms of the [GNU General Public License v3](https://www.gnu.org/licenses/gpl-3.0.html).
* Please test (compile and run) your code before you submit changes! Ideally, provide test feedback in the PR description. Untested code will **not** be merged!
* Make changes on a separate branch, not on the master branch. This is commonly known as *feature branch workflow*. You
may then send your changes as a pull request on GitHub. Patches to the email address mentioned in this document might
not be considered, GitHub is the primary platform. (This only affects you if you are a member of TeamNewPipe)
* When submitting changes, you confirm that your code is licensed under the terms of the
[GNU General Public License v3](https://www.gnu.org/licenses/gpl-3.0.html).
* Please test (compile and run) your code before you submit changes! Ideally, provide test feedback in the PR
description. Untested code will **not** be merged!
* Try to figure out yourself why builds on our CI fail.
* Make sure your PR is up-to-date with the rest of the code. Often, a simple click on "Update branch" will do the job, but if not, you are asked to merge the master branch manually and resolve the problems on your own. That will make the maintainers' jobs way easier.
* Please show intention to maintain your features and code after you contributed it. Unmaintained code is a hassle for the core developers, and just adds work. If you do not intend to maintain features you contributed, please think again about submission, or clearly state that in the description of your PR.
* Make sure your PR is up-to-date with the rest of the code. Often, a simple click on "Update branch" will do the job,
but if not, you are asked to merge the master branch manually and resolve the problems on your own. That will make the
maintainers' jobs way easier.
* Please show intention to maintain your features and code after you contributed it. Unmaintained code is a hassle for
the core developers, and just adds work. If you do not intend to maintain features you contributed, please think again
about submission, or clearly state that in the description of your PR.
* Respond yourselves if someone requests changes or otherwise raises issues about your PRs.
* Check if your contributions align with the [fdroid inclusion guidelines](https://f-droid.org/en/docs/Inclusion_Policy/).
* Check if your submission can be build with the current fdroid build server setup.
* Send PR that only cover one specific issue/solution/bug. Do not send PRs that are huge and consists of multiple
independent solutions.
## Communication
* WE DO NOW HAVE A MAILING LIST: [newpipe@list.schabi.org](https://list.schabi.org/cgi-bin/mailman/listinfo/newpipe).
* There is an IRC channel on Freenode which is regularly visited by the core team and other developers: [#newpipe](irc:irc.freenode.net/newpipe). [Click here for Webchat](https://webchat.freenode.net/?channels=newpipe)!
* If you want to get in touch with the core team or one of our other contributors you can send an email to tnp(at)schabi.org. Please do not send issue reports, they will be ignored and remain unanswered! Use the GitHub issue tracker described above!
* There is an IRC channel on Freenode which is regularly visited by the core team and other developers:
[#newpipe](irc:irc.freenode.net/newpipe). [Click here for Webchat](https://webchat.freenode.net/?channels=newpipe)!
* If you want to get in touch with the core team or one of our other contributors you can send an email to
tnp(at)schabi.org. Please do not send issue reports, they will be ignored and remain unanswered! Use the GitHub issue
tracker described above!
* Feel free to post suggestions, changes, ideas etc. on GitHub, IRC or the mailing list!

View File

@@ -1,2 +1,3 @@
- [ ] I carefully read the [contribution guidelines](https://github.com/TeamNewPipe/NewPipe/blob/HEAD/.github/CONTRIBUTING.md) and agree to them.
- [ ] I checked if the issue/feature exists in the latest version.
- [ ] I did use the [incredible bugreport to markdown converter](https://teamnewpipe.github.io/CrashReportToMarkdown/) to paste bug reports.

View File

@@ -5,13 +5,13 @@ android:
components:
# The BuildTools version used by NewPipe
- tools
- build-tools-27.0.3
- build-tools-28.0.3
# The SDK version used to compile NewPipe
- android-27
- android-28
before_install:
- yes | sdkmanager "platforms;android-27"
- yes | sdkmanager "platforms;android-28"
script: ./gradlew -Dorg.gradle.jvmargs=-Xmx1536m assembleDebug lintDebug testDebugUnitTest
licenses:

Binary file not shown.

View File

@@ -1,170 +0,0 @@
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.regex.*;
import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;
import java.nio.file.Files;
import java.nio.charset.Charset;
public final class CheckTranslations {
private static boolean debug = false;
private static boolean plurals = false;
private static boolean empty = false;
private static boolean remove = false;
private static int checks = 0;
private static int matches = 0;
private static int changes = 0;
private static Pattern p, pb, pe, e, o;
/**
* Search translated strings.xml files for empty item / plural tags
* and remove them.
* @param args directories which contain string.xml files (in any subdirectory)
* -e option to find all empty string tags
* -p option to find all empty plurals and item tags
* -r option to remove all occurrences from the files
* -d option to see more details
*/
public static void main(String[] args) {
if (args.length < 1 || (args[0].equals("-d") && args.length < 2)) {
System.out.println("Not enough arguments");
return;
}
for (int i = 0; i < args.length; i++) {
switch (args[i]) {
case "-d":
debug = true;
break;
case "-p":
plurals = true;
break;
case "-e":
empty = true;
break;
case "-r":
remove = true;
break;
}
}
if (!plurals && !empty) {
plurals = true;
empty = true;
}
p = Pattern.compile("(<item quantity=\")(zero|one|two|three|few|many|other)(\"></item>|\"/>)");
pb = Pattern.compile("(<plurals[\\sa-zA-Z=\"]*>)");
pe = Pattern.compile("(</plurals>)");
e = Pattern.compile("(<string[\\sa-z_\\\"=]*)((><\\/string>|\\/>){1})");
o = Pattern.compile("(<item quantity=\"other\">)[^</>]*(<\\/item>)");
for (int i = 0; i < args.length; i++) {
if (!args[i].equals("-d") && !args[i].equals("-p") && !args[i].equals("-e") && !args[i].equals("-r")) {
File f = new File(args[i]);
if (f.exists() && !f.isDirectory()) {
checkFile(f);
} else if (f.isDirectory()) {
checkFiles(f.listFiles());
} else {
System.out.println("'" + args[i] + "' does not exist!");
}
}
}
System.out.println(checks + " files were checked.");
System.out.println(matches + " corrupt lines detected.");
if (remove) {
System.out.println(matches + " corrupt lines removed and " + changes + " lines fixed.");
}
}
private static void checkFiles(File[] f) {
for (int i = 0; i < f.length; i++) {
if (f[i].exists() && !f[i].isDirectory()) {
if (f[i].toString().contains("strings.xml")) {
checkFile(f[i]);
}
} else if (f[i].isDirectory()) {
checkFiles(f[i].listFiles());
}
}
}
private static void checkFile(File f) {
// Do not check our original English strings to cause no unwanted changes
// Btw. there should not be empty plural/item tags
if (f.toString().contains("values/strings.xml")) {
return;
}
if (debug) System.out.println("Checking " + f.toString());
checks++;
List<String> lines = new ArrayList<String>();
boolean checkFailed = false;
boolean otherDetected = false;
boolean inPlurals = false;
try (BufferedReader br = new BufferedReader(new FileReader(f))) {
String line;
int ln = 0;
while ((line = br.readLine()) != null) {
ln++;
if (plurals && p.matcher(line).find()) {
matches++;
if (debug) System.out.println(" Line " + ln + " was " + ((remove) ? "removed" : "detected") + ": '" + line + "'");
checkFailed = true;
} else if (empty && e.matcher(line).find()) {
matches++;
checkFailed = true;
if (debug) System.out.println(" Line " + ln + " was " + ((remove) ? "removed" : "detected") + ": '" + line + "'");
} else {
if (remove) lines.add(line);
}
}
br.close();
int pluralsLine = 0;
for (int i = 0; i < lines.size(); i++) {
if (o.matcher(lines.get(i)).find()) {
otherDetected = true;
}
if (plurals && pb.matcher(lines.get(i)).find()) {
inPlurals = true;
pluralsLine = i;
} else if (plurals && pe.matcher(lines.get(i)).find()) {
inPlurals = false;
if (!otherDetected) {
boolean b = false;
check: for(int j = pluralsLine; j < i; j++) {
if (lines.get(j).contains("many")) {
b = true;
pluralsLine = j;
break check;
}
}
if (remove && b) {
if (debug) System.out.println(" Line " + (pluralsLine + 1) + " was " + ((remove) ? "changed" : "detected") + ": '" + lines.get(pluralsLine) + "'");
lines.set(pluralsLine, lines.get(pluralsLine).replace("many", "other"));
changes++;
checkFailed = true;
} else if (debug) {
if (debug) System.out.println(" WARNING: Line " + (i + 1) + " - No <item quantity=\"other\"> found!");
}
}
otherDetected = false;
}
}
if (remove && checkFailed) {
Files.write(f.toPath(), lines, Charset.forName("UTF-8"));
}
} catch (IOException e) {
System.out.println(e);
}
}
}

112
README.md
View File

@@ -1,74 +1,93 @@
<p align="center"><a href="https://newpipe.schabi.org"><img src="assets/new_pipe_icon_5.png" width="150"/></a></p>
<p align="center"><a href="https://newpipe.schabi.org"><img src="assets/new_pipe_icon_5.png" width="150"></a></p>
<h2 align="center"><b>NewPipe</b></h2>
<h4 align="center">A free lightweight YouTube frontend for Android.</h4>
<p align="center"><a href="https://f-droid.org/packages/org.schabi.newpipe/"><img src="https://f-droid.org/wiki/images/0/06/F-Droid-button_get-it-on.png"/></a></p>
<h4 align="center">A libre lightweight streaming frontend for Android.</h4>
<p align="center"><a href="https://f-droid.org/packages/org.schabi.newpipe/"><img src="https://f-droid.org/wiki/images/0/06/F-Droid-button_get-it-on.png"></a></p>
<p align="center">
<a href="https://github.com/TeamNewPipe/NewPipe" alt="GitHub release"><img src="https://img.shields.io/github/release/TeamNewPipe/NewPipe.svg" /></a>
<a href="https://www.gnu.org/licenses/gpl-3.0" alt="License: GPL v3"><img src="https://img.shields.io/badge/License-GPL%20v3-blue.svg" /></a>
<a href="https://travis-ci.org/TeamNewPipe/NewPipe" alt="Build Status"><img src="https://travis-ci.org/TeamNewPipe/NewPipe.svg" /></a>
<a href="https://hosted.weblate.org/engage/NewPipe/" alt="Translation Status"><img src="https://hosted.weblate.org/widgets/NewPipe/-/svg-badge.svg" /></a>
<a href="http://webchat.freenode.net/?channels=%23newpipe" alt="IRC channel: #newpipe"><img src="https://img.shields.io/badge/IRC%20chat-%23newpipe-brightgreen.svg" /></a>
<a href="https://www.bountysource.com/teams/newpipe" alt="Bountysource bounties"><img src="https://img.shields.io/bountysource/team/newpipe/activity.svg?colorB=cd201f"/></a>
<a href="https://github.com/TeamNewPipe/NewPipe" alt="GitHub release"><img src="https://img.shields.io/github/release/TeamNewPipe/NewPipe.svg" ></a>
<a href="https://www.gnu.org/licenses/gpl-3.0" alt="License: GPLv3"><img src="https://img.shields.io/badge/License-GPL%20v3-blue.svg"></a>
<a href="https://travis-ci.org/TeamNewPipe/NewPipe" alt="Build Status"><img src="https://travis-ci.org/TeamNewPipe/NewPipe.svg"></a>
<a href="https://hosted.weblate.org/engage/NewPipe/" alt="Translation Status"><img src="https://hosted.weblate.org/widgets/NewPipe/-/svg-badge.svg"></a>
<a href="http://webchat.freenode.net/?channels=%23newpipe" alt="IRC channel: #newpipe"><img src="https://img.shields.io/badge/IRC%20chat-%23newpipe-brightgreen.svg"></a>
<a href="https://www.bountysource.com/teams/newpipe" alt="Bountysource bounties"><img src="https://img.shields.io/bountysource/team/newpipe/activity.svg?colorB=cd201f"></a>
</p>
<hr />
<p align="center"><a href="#screenshots">Screenshots</a> &bull; <a href="#description">Description</a> &bull; <a href="#features">Features</a> &bull; <a href="#contribution">Contribution</a> &bull; <a href="#donate">Donate</a> &bull; <a href="#license">License</a></p>
<hr>
<p align="center"><a href="#screenshots">Screenshots</a> &bull; <a href="#description">Description</a> &bull; <a href="#features">Features</a> &bull; <a href="#updates">Updates</a> &bull; <a href="#contribution">Contribution</a> &bull; <a href="#donate">Donate</a> &bull; <a href="#license">License</a></p>
<p align="center"><a href="https://newpipe.schabi.org">Website</a> &bull; <a href="https://newpipe.schabi.org/blog/">Blog</a> &bull; <a href="https://newpipe.schabi.org/press/">Press</a></p>
<hr />
WARNING: PUTTING NEWPIPE OR ANY FORK OF IT INTO GOOGLE PLAYSTORE VIOLATES THEIR TERMS OF CONDITIONS.
<hr>
<b>WARNING: THIS IS A BETA VERSION, THEREFORE YOU MAY ENCOUNTER BUGS. IF YOU DO, OPEN AN ISSUE VIA OUR GITHUB REPOSITORY.</b>
<b>PUTTING NEWPIPE OR ANY FORK OF IT INTO GOOGLE PLAYSTORE VIOLATES THEIR TERMS OF CONDITIONS.</b>
## Screenshots
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_1.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_1.png)
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_2.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_2.png)
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_3.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_3.png)
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_4.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_4.png)
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_5.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_5.png)
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_6.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_6.png)
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_7.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_7.png)
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_8.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_8.png)
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_9.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_9.png)
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_01.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_01.png)
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_02.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_02.png)
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_03.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_03.png)
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_04.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_04.png)
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_05.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_05.png)
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_06.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_06.png)
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_07.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_07.png)
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_08.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_08.png)
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_09.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_09.png)
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_10.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_10.png)
[<img src="fastlane/metadata/android/en-US/images/tenInchScreenshots/shot_11.png" width=405>](fastlane/metadata/android/en-US/images/tenInchScreenshots/shot_11.png)
[<img src="fastlane/metadata/android/en-US/images/tenInchScreenshots/shot_12.png" width=405>](fastlane/metadata/android/en-US/images/tenInchScreenshots/shot_12.png)
## Description
NewPipe does not use any Google framework libraries, or the YouTube API. It only parses the website in order to gain the information it needs. Therefore this app can be used on devices without Google Services installed. Also, you don't need a YouTube account to use NewPipe, and it's FLOSS.
NewPipe does not use any Google framework libraries, nor the YouTube API. Websites are only parsed to fetch required info, so this app can be used on devices without Google services installed. Also, you don't need a YouTube account to use NewPipe, which is copylefted libre software.
### Features
* Search videos
* Display general information about a video
* Display general info about videos
* Watch YouTube videos
* Listen to YouTube videos
* Popup mode (floating player)
* Select the streaming player to watch the video with
* Download videos
* Select streaming player to watch video with
* Download videos
* Download audio only
* Open a video in Kodi
* Show Next/Related videos
* Show next/related videos
* Search YouTube in a specific language
* Watch/Block age restricted material
* Display general information about channels
* Display general info about channels
* Search channels
* Watch videos from a channel
* Orbot/Tor support (not yet directly)
* 1080p/2k/4k support
* 1080p/2K/4K support
* View history
* Subscribe to channels
* Search history
* Search/Watch Playlists
* Watch as queues Playlists
* Queuing videos
* Search/watch playlists
* Watch as enqueued playlists
* Enqueue videos
* Local playlists
* Subtitles
* Multi-service support (eg. SoundCloud in NewPipe Beta)
* Multi-service support (e.g. SoundCloud \[beta\])
* Livestream support
### Coming Features
* Livestream support
* Cast to UPnP and Cast
* Show comments
* ... and many more
* and many more
## Updates
When a change to the NewPipe code occurs (due to either adding features or bug fixing), eventually a release will occur. These are in the format x.xx.x . In order to get this new version, you can:
* Build a debug APK yourself. This is the fastest way to get new features on your device, but is much more complicated, so we recommend using one of the other methods.
* Download the APK from [releases](https://github.com/TeamNewPipe/NewPipe/releases) and install it.
* Update via F-droid. This is the slowest method of getting updates, as F-Droid must recognize changes, build the APK itself, sign it, then push the update to users.
When you install an APK from one of these options, it will be incompatible with an APK from one of the other options. This is due to different signing keys being used. Signing keys help ensure that a user isn't tricked into installing a malicious update to an app, and are independent. F-Droid and GitHub use different signing keys, and building an APK debug excludes a key. The signing key issue is being discussed in issue [#1981](https://github.com/TeamNewPipe/NewPipe/issues/1981), and may be fixed by setting up our own repository on F-Droid.
In the meanwhile, if you want to switch sources for some reason (e.g. NewPipe's core functionality was broken and F-Droid doesn't have the update yet), we recommend following this procedure:
1. Back up your data via "Settings>Content>Export Database" so you keep your history, subscriptions, and playlists
2. Uninstall NewPipe
3. Download the APK from the new source and install it
4. Import the data from step 1 via "Settings>Content>Import Database"
## Contribution
Whether you have ideas, translations, design changes, code cleaning, or real heavy code changes, help is always welcome.
@@ -77,26 +96,31 @@ The more is done the better it gets!
If you'd like to get involved, check our [contribution notes](.github/CONTRIBUTING.md).
## Donate
If you like NewPipe we'd be happy about a donation. You can either donate via Bitcoin, Bountysource or Liberapay. For further information about donating to NewPipe, please visit our [website](https://newpipe.schabi.org/donate).
If you like NewPipe we'd be happy about a donation. You can either send bitcoin or donate via Bountysource or Liberapay. For further info on donating to NewPipe, please visit our [website](https://newpipe.schabi.org/donate).
<table>
<tr>
<td><img src="https://bitcoin.org/img/icons/logotop.svg" alt="Bitcoin" /></td>
<td><img src="assets/bitcoin_qr_code.png" alt="Bitcoin QR Code" width="100px"/></td>
<td><img src="https://bitcoin.org/img/icons/logotop.svg" alt="Bitcoin"></td>
<td><img src="assets/bitcoin_qr_code.png" alt="Bitcoin QR code" width="100px"></td>
<td><samp>16A9J59ahMRqkLSZjhYj33n9j3fMztFxnh</samp></td>
</tr>
<tr>
<td><a href="https://liberapay.com/TeamNewPipe/"><img src="https://upload.wikimedia.org/wikipedia/commons/2/27/Liberapay_logo_v2_white-on-yellow.svg" alt="Liberapay" width="80px" /></a></td>
<td><a href="https://liberapay.com/TeamNewPipe/"><img src="assets/liberapay_qr_code.png" alt="Visit NewPipe at liberapay.com" width="100px"/></a></td>
<td><a href="https://liberapay.com/TeamNewPipe/donate"><img src="assets/liberapay_donate_button.svg" alt="Donate via Liberapay" height="35px" /></a></td>
<td><a href="https://liberapay.com/TeamNewPipe/"><img src="https://upload.wikimedia.org/wikipedia/commons/2/27/Liberapay_logo_v2_white-on-yellow.svg" alt="Liberapay" width="80px" ></a></td>
<td><a href="https://liberapay.com/TeamNewPipe/"><img src="assets/liberapay_qr_code.png" alt="Visit NewPipe at liberapay.com" width="100px"></a></td>
<td><a href="https://liberapay.com/TeamNewPipe/donate"><img src="assets/liberapay_donate_button.svg" alt="Donate via Liberapay" height="35px"></a></td>
</tr>
<tr>
<td><a href="https://www.bountysource.com/teams/newpipe"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/2/22/Bountysource.png/320px-Bountysource.png" alt="Bountysource" width="190px" /></a></td>
<td><a href="https://www.bountysource.com/teams/newpipe"><img src="assets/bountysource_qr_code.png" alt="Visit NewPipe at bountysource.com" width="100px"/></a></td>
<td><a href="https://www.bountysource.com/teams/newpipe/issues"><img src="https://img.shields.io/bountysource/team/newpipe/activity.svg?colorB=cd201f" height="30px" alt="Check out how many bounties you can earn." /></a></td>
<td><a href="https://www.bountysource.com/teams/newpipe"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/2/22/Bountysource.png/320px-Bountysource.png" alt="Bountysource" width="190px"></a></td>
<td><a href="https://www.bountysource.com/teams/newpipe"><img src="assets/bountysource_qr_code.png" alt="Visit NewPipe at bountysource.com" width="100px"></a></td>
<td><a href="https://www.bountysource.com/teams/newpipe/issues"><img src="https://img.shields.io/bountysource/team/newpipe/activity.svg?colorB=cd201f" height="30px" alt="Check out how many bounties you can earn."></a></td>
</tr>
</table>
## Privacy Policy
The NewPipe project aims to provide a private, anonymous experience for using media web services.
Therefore, the app does not collect any data without your consent. NewPipe's privacy policy explains in detail what data is sent and stored when you send a crash report, or comment in our blog. You can find the document [here](https://newpipe.schabi.org/legal/privacy/).
## License
[![GNU GPLv3 Image](https://www.gnu.org/graphics/gplv3-127x51.png)](http://www.gnu.org/licenses/gpl-3.0.en.html)

View File

@@ -1,28 +1,29 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 27
buildToolsVersion '27.0.3'
compileSdkVersion 28
buildToolsVersion '28.0.3'
defaultConfig {
applicationId "org.schabi.newpipe"
minSdkVersion 15
targetSdkVersion 27
versionCode 60
versionName "0.13.1"
minSdkVersion 19
targetSdkVersion 28
versionCode 730
versionName "0.16.1"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
}
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
multiDexEnabled true
debuggable true
applicationIdSuffix ".debug"
}
@@ -34,6 +35,7 @@ android {
// but continue the build even when errors are found:
abortOnError false
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
@@ -41,59 +43,61 @@ android {
}
ext {
supportLibVersion = '27.1.0'
exoPlayerLibVersion = '2.7.1'
roomDbLibVersion = '1.0.0'
leakCanaryLibVersion = '1.5.4'
okHttpLibVersion = '1.5.0'
supportLibVersion = '28.0.0'
exoPlayerLibVersion = '2.8.4' //2.9.0
roomDbLibVersion = '1.1.1'
leakCanaryLibVersion = '1.5.4' //1.6.1
okHttpLibVersion = '3.11.0'
icepickLibVersion = '3.2.0'
stethoLibVersion = '1.5.0'
}
dependencies {
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2') {
androidTestImplementation('com.android.support.test.espresso:espresso-core:3.0.2', {
exclude module: 'support-annotations'
}
implementation 'com.github.TeamNewPipe:NewPipeExtractor:a6b6235644474'
})
implementation 'com.github.TeamNewPipe:NewPipeExtractor:e072bf6461b295'
testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-core:1.10.19'
testImplementation 'org.mockito:mockito-core:2.23.0'
implementation "com.android.support:appcompat-v7:$supportLibVersion"
implementation "com.android.support:support-v4:$supportLibVersion"
implementation "com.android.support:design:$supportLibVersion"
implementation "com.android.support:recyclerview-v7:$supportLibVersion"
implementation "com.android.support:preference-v14:$supportLibVersion"
implementation "com.android.support:appcompat-v7:${supportLibVersion}"
implementation "com.android.support:support-v4:${supportLibVersion}"
implementation "com.android.support:design:${supportLibVersion}"
implementation "com.android.support:recyclerview-v7:${supportLibVersion}"
implementation "com.android.support:preference-v14:${supportLibVersion}"
implementation "com.android.support:cardview-v7:${supportLibVersion}"
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation 'com.google.code.gson:gson:2.8.2'
implementation 'ch.acra:acra:4.9.2'
implementation 'ch.acra:acra:4.9.2' //4.11
implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
implementation 'de.hdodenhof:circleimageview:2.2.0'
implementation 'com.github.nirhart:ParallaxScroll:dd53d1f9d1'
implementation 'com.nononsenseapps:filepicker:4.2.1'
implementation "com.google.android.exoplayer:exoplayer:$exoPlayerLibVersion"
implementation "com.google.android.exoplayer:extension-mediasession:$exoPlayerLibVersion"
debugImplementation "com.facebook.stetho:stetho:$stethoLibVersion"
debugImplementation "com.facebook.stetho:stetho-urlconnection:$stethoLibVersion"
implementation "com.google.android.exoplayer:exoplayer:${exoPlayerLibVersion}"
implementation "com.google.android.exoplayer:extension-mediasession:${exoPlayerLibVersion}"
debugImplementation "com.facebook.stetho:stetho:${stethoLibVersion}"
debugImplementation "com.facebook.stetho:stetho-urlconnection:${stethoLibVersion}"
debugImplementation 'com.android.support:multidex:1.0.3'
implementation 'io.reactivex.rxjava2:rxjava:2.1.10'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
implementation 'io.reactivex.rxjava2:rxjava:2.2.2'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
implementation 'com.jakewharton.rxbinding2:rxbinding:2.1.1'
implementation "android.arch.persistence.room:runtime:$roomDbLibVersion"
implementation "android.arch.persistence.room:rxjava2:$roomDbLibVersion"
annotationProcessor "android.arch.persistence.room:compiler:$roomDbLibVersion"
implementation "android.arch.persistence.room:runtime:${roomDbLibVersion}"
implementation "android.arch.persistence.room:rxjava2:${roomDbLibVersion}"
annotationProcessor "android.arch.persistence.room:compiler:${roomDbLibVersion}"
implementation "frankiesardo:icepick:$icepickLibVersion"
annotationProcessor "frankiesardo:icepick-processor:$icepickLibVersion"
implementation "frankiesardo:icepick:${icepickLibVersion}"
annotationProcessor "frankiesardo:icepick-processor:${icepickLibVersion}"
debugImplementation "com.squareup.leakcanary:leakcanary-android:$leakCanaryLibVersion"
releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$leakCanaryLibVersion"
debugImplementation "com.squareup.leakcanary:leakcanary-android:${leakCanaryLibVersion}"
releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:${leakCanaryLibVersion}"
implementation 'com.squareup.okhttp3:okhttp:3.9.1'
debugImplementation "com.facebook.stetho:stetho-okhttp3:$okHttpLibVersion"
implementation "com.squareup.okhttp3:okhttp:${okHttpLibVersion}"
debugImplementation "com.facebook.stetho:stetho-okhttp3:${stethoLibVersion}"
}

View File

@@ -42,3 +42,9 @@
-dontwarn javax.annotation.**
# A resource is loaded with a relative path so the package of this class must be preserved.
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
!static !transient <fields>;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
}

View File

@@ -1,13 +0,0 @@
package org.schabi.newpipe;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
}

View File

@@ -9,6 +9,7 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<application
android:name=".App"
@@ -34,15 +35,13 @@
</intent-filter>
</receiver>
<activity
android:name=".player.old.PlayVideoActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:theme="@style/OldVideoPlayerTheme"
tools:ignore="UnusedAttribute"/>
<service
android:name=".player.BackgroundPlayer"
android:exported="false"/>
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</service>
<activity
android:name=".player.BackgroundPlayerActivity"
@@ -72,12 +71,8 @@
android:name=".about.AboutActivity"
android:label="@string/title_activity_about"/>
<activity
android:name=".history.HistoryActivity"
android:label="@string/title_activity_history"/>
<service android:name=".subscription.services.SubscriptionsImportService"/>
<service android:name=".subscription.services.SubscriptionsExportService"/>
<service android:name=".local.subscription.services.SubscriptionsImportService"/>
<service android:name=".local.subscription.services.SubscriptionsExportService"/>
<activity
android:name=".PanicResponderActivity"
@@ -132,7 +127,7 @@
<activity
android:name=".RouterActivity"
android:excludeFromRecents="true"
android:label="@string/preferred_player_share_menu_title"
android:label="@string/preferred_open_action_share_menu_title"
android:taskAffinity=""
android:theme="@style/RouterActivityThemeDark">
@@ -182,6 +177,19 @@
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="http"/>
<data android:scheme="https"/>
<data android:host="www.youtube-nocookie.com"/>
<data android:pathPrefix="/embed/"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH"/>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="vnd.youtube"/>
<data android:scheme="vnd.youtube.launch"/>
</intent-filter>
@@ -208,6 +216,29 @@
<data android:pathPrefix="/user/"/>
</intent-filter>
<!-- Invidious filter -->
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH"/>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="http"/>
<data android:scheme="https"/>
<data android:host="invidio.us"/>
<data android:host="www.invidio.us"/>
<!-- video prefix -->
<data android:pathPrefix="/embed/"/>
<data android:pathPrefix="/watch"/>
<!-- channel prefix -->
<data android:pathPrefix="/channel/"/>
<data android:pathPrefix="/user/"/>
<!-- playlist prefix -->
<data android:pathPrefix="/playlist"/>
</intent-filter>
<!-- Soundcloud filter -->
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
@@ -237,4 +268,4 @@
android:name=".RouterActivity$FetcherService"
android:exported="false"/>
</application>
</manifest>
</manifest>

View File

@@ -0,0 +1,116 @@
package android.support.design.widget;
import android.animation.ValueAnimator;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.design.animation.AnimationUtils;
import android.util.AttributeSet;
import android.view.View;
// check this https://github.com/ToDou/appbarlayout-spring-behavior/blob/master/appbarspring/src/main/java/android/support/design/widget/AppBarFlingFixBehavior.java
public final class FlingBehavior extends AppBarLayout.Behavior {
private ValueAnimator mOffsetAnimator;
private static final int MAX_OFFSET_ANIMATION_DURATION = 600; // ms
public FlingBehavior() {
}
public FlingBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed, int type) {
if (dy != 0) {
int val = child.getBottom();
if (val != 0) {
int min, max;
if (dy < 0) {
// We're scrolling down
} else {
// We're scrolling up
if (mOffsetAnimator != null && mOffsetAnimator.isRunning()) {
mOffsetAnimator.cancel();
}
min = -child.getUpNestedPreScrollRange();
max = 0;
consumed[1] = scroll(coordinatorLayout, child, dy, min, max);
}
}
}
}
@Override
public boolean onNestedPreFling(@NonNull CoordinatorLayout coordinatorLayout, @NonNull AppBarLayout child, @NonNull View target, float velocityX, float velocityY) {
if (velocityY != 0) {
if (velocityY < 0) {
// We're flinging down
int val = child.getBottom();
if (val != 0) {
final int targetScroll =
+child.getDownNestedPreScrollRange();
animateOffsetTo(coordinatorLayout, child, targetScroll, velocityY);
}
} else {
// We're flinging up
int val = child.getBottom();
if (val != 0) {
final int targetScroll = -child.getUpNestedPreScrollRange();
if (getTopBottomOffsetForScrollingSibling() > targetScroll) {
animateOffsetTo(coordinatorLayout, child, targetScroll, velocityY);
}
}
}
}
return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
}
private void animateOffsetTo(final CoordinatorLayout coordinatorLayout,
final AppBarLayout child, final int offset, float velocity) {
final int distance = Math.abs(getTopBottomOffsetForScrollingSibling() - offset);
final int duration;
velocity = Math.abs(velocity);
if (velocity > 0) {
duration = 3 * Math.round(1000 * (distance / velocity));
} else {
final float distanceRatio = (float) distance / child.getHeight();
duration = (int) ((distanceRatio + 1) * 150);
}
animateOffsetWithDuration(coordinatorLayout, child, offset, duration);
}
private void animateOffsetWithDuration(final CoordinatorLayout coordinatorLayout,
final AppBarLayout child, final int offset, final int duration) {
final int currentOffset = getTopBottomOffsetForScrollingSibling();
if (currentOffset == offset) {
if (mOffsetAnimator != null && mOffsetAnimator.isRunning()) {
mOffsetAnimator.cancel();
}
return;
}
if (mOffsetAnimator == null) {
mOffsetAnimator = new ValueAnimator();
mOffsetAnimator.setInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR);
mOffsetAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animator) {
setHeaderTopBottomOffset(coordinatorLayout, child,
(Integer) animator.getAnimatedValue());
}
});
} else {
mOffsetAnimator.cancel();
}
mOffsetAnimator.setDuration(Math.min(duration, MAX_OFFSET_ANIMATION_DURATION));
mOffsetAnimator.setIntValues(currentOffset, offset);
mOffsetAnimator.start();
}
}

View File

@@ -1,10 +1,12 @@
package org.schabi.newpipe;
import android.annotation.TargetApi;
import android.app.Application;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Build;
import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
import android.util.Log;
@@ -21,6 +23,7 @@ import org.acra.config.ConfigurationBuilder;
import org.acra.sender.ReportSenderFactory;
import org.schabi.newpipe.extractor.Downloader;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.utils.Localization;
import org.schabi.newpipe.report.AcraReportSenderFactory;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
@@ -63,9 +66,11 @@ import io.reactivex.plugins.RxJavaPlugins;
public class App extends Application {
protected static final String TAG = App.class.toString();
private RefWatcher refWatcher;
private static App app;
@SuppressWarnings("unchecked")
private static final Class<? extends ReportSenderFactory>[] reportSenderFactoryClasses = new Class[]{AcraReportSenderFactory.class};
private static final Class<? extends ReportSenderFactory>[]
reportSenderFactoryClasses = new Class[]{AcraReportSenderFactory.class};
@Override
protected void attachBaseContext(Context base) {
@@ -85,10 +90,13 @@ public class App extends Application {
}
refWatcher = installLeakCanary();
app = this;
// Initialize settings first because others inits can use its values
SettingsActivity.initSettings(this);
NewPipe.init(getDownloader());
NewPipe.init(getDownloader(),
org.schabi.newpipe.util.Localization.getPreferredExtractorLocal(this));
StateSaver.init(this);
initNotificationChannel();
@@ -96,6 +104,9 @@ public class App extends Application {
ImageLoader.getInstance().init(getImageLoaderConfigurations(10, 50));
configureRxJavaErrorHandler();
// Check for new version
new CheckForNewAppVersionTask().execute();
}
protected Downloader getDownloader() {
@@ -106,7 +117,7 @@ public class App extends Application {
// https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling
RxJavaPlugins.setErrorHandler(new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
public void accept(@NonNull Throwable throwable) {
Log.e(TAG, "RxJavaPlugins.ErrorHandler called with -> : " +
"throwable = [" + throwable.getClass().getName() + "]");
@@ -180,7 +191,11 @@ public class App extends Application {
ACRA.init(this, acraConfig);
} catch (ACRAConfigurationException ace) {
ace.printStackTrace();
ErrorActivity.reportError(this, ace, null, null, ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
ErrorActivity.reportError(this,
ace,
null,
null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"Could not initialize ACRA crash report", R.string.app_ui_crash));
}
}
@@ -200,8 +215,34 @@ public class App extends Application {
NotificationChannel mChannel = new NotificationChannel(id, name, importance);
mChannel.setDescription(description);
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.createNotificationChannel(mChannel);
setUpUpdateNotificationChannel(importance);
}
/**
* Set up notification channel for app update.
* @param importance
*/
@TargetApi(Build.VERSION_CODES.O)
private void setUpUpdateNotificationChannel(int importance) {
final String appUpdateId
= getString(R.string.app_update_notification_channel_id);
final CharSequence appUpdateName
= getString(R.string.app_update_notification_channel_name);
final String appUpdateDescription
= getString(R.string.app_update_notification_channel_description);
NotificationChannel appUpdateChannel
= new NotificationChannel(appUpdateId, appUpdateName, importance);
appUpdateChannel.setDescription(appUpdateDescription);
NotificationManager appUpdateNotificationManager
= (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
appUpdateNotificationManager.createNotificationChannel(appUpdateChannel);
}
@Nullable
@@ -217,4 +258,8 @@ public class App extends Application {
protected boolean isDisposedRxExceptionsReported() {
return false;
}
public static App getApp() {
return app;
}
}

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;
@@ -12,14 +13,24 @@ import com.nostra13.universalimageloader.core.ImageLoader;
import com.squareup.leakcanary.RefWatcher;
import icepick.Icepick;
import icepick.State;
public abstract class BaseFragment extends Fragment {
protected final String TAG = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode());
protected boolean DEBUG = MainActivity.DEBUG;
protected final boolean DEBUG = MainActivity.DEBUG;
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 +83,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 +105,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

@@ -0,0 +1,230 @@
package org.schabi.newpipe;
import android.app.Application;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.net.Uri;
import android.os.AsyncTask;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import org.json.JSONException;
import org.json.JSONObject;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
/**
* AsyncTask to check if there is a newer version of the NewPipe github apk available or not.
* If there is a newer version we show a notification, informing the user. On tapping
* the notification, the user will be directed to the download link.
*/
public class CheckForNewAppVersionTask extends AsyncTask<Void, Void, String> {
private static final Application app = App.getApp();
private static final String GITHUB_APK_SHA1 = "B0:2E:90:7C:1C:D6:FC:57:C3:35:F0:88:D0:8F:50:5F:94:E4:D2:15";
private static final String newPipeApiUrl = "https://newpipe.schabi.org/api/data.json";
private static final int timeoutPeriod = 30;
private SharedPreferences mPrefs;
private OkHttpClient client;
@Override
protected void onPreExecute() {
mPrefs = PreferenceManager.getDefaultSharedPreferences(app);
// Check if user has enabled/ disabled update checking
// and if the current apk is a github one or not.
if (!mPrefs.getBoolean(app.getString(R.string.update_app_key), true)
|| !isGithubApk()) {
this.cancel(true);
}
}
@Override
protected String doInBackground(Void... voids) {
// Make a network request to get latest NewPipe data.
if (client == null) {
client = new OkHttpClient
.Builder()
.readTimeout(timeoutPeriod, TimeUnit.SECONDS)
.build();
}
Request request = new Request.Builder()
.url(newPipeApiUrl)
.build();
try {
Response response = client.newCall(request).execute();
return response.body().string();
} catch (IOException ex) {
ErrorActivity.reportError(app, ex, null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"app update API fail", R.string.app_ui_crash));
}
return null;
}
@Override
protected void onPostExecute(String response) {
// Parse the json from the response.
if (response != null) {
try {
JSONObject mainObject = new JSONObject(response);
JSONObject flavoursObject = mainObject.getJSONObject("flavors");
JSONObject githubObject = flavoursObject.getJSONObject("github");
JSONObject githubStableObject = githubObject.getJSONObject("stable");
String versionName = githubStableObject.getString("version");
String versionCode = githubStableObject.getString("version_code");
String apkLocationUrl = githubStableObject.getString("apk");
compareAppVersionAndShowNotification(versionName, apkLocationUrl, versionCode);
} catch (JSONException ex) {
ErrorActivity.reportError(app, ex, null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"could not parse app update JSON data", R.string.app_ui_crash));
}
}
}
/**
* Method to compare the current and latest available app version.
* If a newer version is available, we show the update notification.
* @param versionName
* @param apkLocationUrl
*/
private void compareAppVersionAndShowNotification(String versionName,
String apkLocationUrl,
String versionCode) {
int NOTIFICATION_ID = 2000;
if (BuildConfig.VERSION_CODE < Integer.valueOf(versionCode)) {
// A pending intent to open the apk location url in the browser.
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(apkLocationUrl));
PendingIntent pendingIntent
= PendingIntent.getActivity(app, 0, intent, 0);
NotificationCompat.Builder notificationBuilder = new NotificationCompat
.Builder(app, app.getString(R.string.app_update_notification_channel_id))
.setSmallIcon(R.drawable.ic_newpipe_update)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.setContentTitle(app.getString(R.string.app_update_notification_content_title))
.setContentText(app.getString(R.string.app_update_notification_content_text)
+ " " + versionName);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(app);
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
}
}
/**
* Method to get the apk's SHA1 key.
* https://stackoverflow.com/questions/9293019/get-certificate-fingerprint-from-android-app#22506133
*/
private static String getCertificateSHA1Fingerprint() {
PackageManager pm = app.getPackageManager();
String packageName = app.getPackageName();
int flags = PackageManager.GET_SIGNATURES;
PackageInfo packageInfo = null;
try {
packageInfo = pm.getPackageInfo(packageName, flags);
} catch (PackageManager.NameNotFoundException ex) {
ErrorActivity.reportError(app, ex, null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"Could not find package info", R.string.app_ui_crash));
}
Signature[] signatures = packageInfo.signatures;
byte[] cert = signatures[0].toByteArray();
InputStream input = new ByteArrayInputStream(cert);
CertificateFactory cf = null;
X509Certificate c = null;
try {
cf = CertificateFactory.getInstance("X509");
c = (X509Certificate) cf.generateCertificate(input);
} catch (CertificateException ex) {
ErrorActivity.reportError(app, ex, null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"Certificate error", R.string.app_ui_crash));
}
String hexString = null;
try {
MessageDigest md = MessageDigest.getInstance("SHA1");
byte[] publicKey = md.digest(c.getEncoded());
hexString = byte2HexFormatted(publicKey);
} catch (NoSuchAlgorithmException ex1) {
ErrorActivity.reportError(app, ex1, null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"Could not retrieve SHA1 key", R.string.app_ui_crash));
} catch (CertificateEncodingException ex2) {
ErrorActivity.reportError(app, ex2, null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"Could not retrieve SHA1 key", R.string.app_ui_crash));
}
return hexString;
}
private static String byte2HexFormatted(byte[] arr) {
StringBuilder str = new StringBuilder(arr.length * 2);
for (int i = 0; i < arr.length; i++) {
String h = Integer.toHexString(arr[i]);
int l = h.length();
if (l == 1) h = "0" + h;
if (l > 2) h = h.substring(l - 2, l);
str.append(h.toUpperCase());
if (i < (arr.length - 1)) str.append(':');
}
return str.toString();
}
public static boolean isGithubApk() {
return getCertificateSHA1Fingerprint().equals(GITHUB_APK_SHA1);
}
}

View File

@@ -3,17 +3,24 @@ package org.schabi.newpipe;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import org.schabi.newpipe.extractor.DownloadRequest;
import org.schabi.newpipe.extractor.DownloadResponse;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.extractor.utils.Localization;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
@@ -43,7 +50,7 @@ public class Downloader implements org.schabi.newpipe.extractor.Downloader {
private static Downloader instance;
private String mCookies;
private OkHttpClient client;
private final OkHttpClient client;
private Downloader(OkHttpClient.Builder builder) {
this.client = builder
@@ -73,18 +80,44 @@ public class Downloader implements org.schabi.newpipe.extractor.Downloader {
mCookies = cookies;
}
/**
* Get the size of the content that the url is pointing by firing a HEAD request.
*
* @param url an url pointing to the content
* @return the size of the content, in bytes
*/
public long getContentLength(String url) throws IOException {
Response response = null;
try {
final Request request = new Request.Builder()
.head().url(url)
.addHeader("User-Agent", USER_AGENT)
.build();
response = client.newCall(request).execute();
String contentLength = response.header("Content-Length");
return contentLength == null ? -1 : Long.parseLong(contentLength);
} catch (NumberFormatException e) {
throw new IOException("Invalid content length", e);
} finally {
if (response != null) {
response.close();
}
}
}
/**
* Download the text file at the supplied URL as in download(String),
* but set the HTTP header field "Accept-Language" to the supplied string.
*
* @param siteUrl the URL of the text file to return the contents of
* @param language the language (usually a 2-character code) to set as the preferred language
* @param localization the language and country (usually a 2-character code) to set
* @return the contents of the specified text file
*/
@Override
public String download(String siteUrl, String language) throws IOException, ReCaptchaException {
public String download(String siteUrl, Localization localization) throws IOException, ReCaptchaException {
Map<String, String> requestProperties = new HashMap<>();
requestProperties.put("Accept-Language", language);
requestProperties.put("Accept-Language", localization.getLanguage());
return download(siteUrl, requestProperties);
}
@@ -112,13 +145,16 @@ public class Downloader implements org.schabi.newpipe.extractor.Downloader {
private ResponseBody getBody(String siteUrl, Map<String, String> customProperties) throws IOException, ReCaptchaException {
final Request.Builder requestBuilder = new Request.Builder()
.method("GET", null).url(siteUrl)
.addHeader("User-Agent", USER_AGENT);
.method("GET", null).url(siteUrl);
for (Map.Entry<String, String> header : customProperties.entrySet()) {
requestBuilder.addHeader(header.getKey(), header.getValue());
}
if (!customProperties.containsKey("User-Agent")) {
requestBuilder.header("User-Agent", USER_AGENT);
}
if (!TextUtils.isEmpty(mCookies)) {
requestBuilder.addHeader("Cookie", mCookies);
}
@@ -150,4 +186,96 @@ public class Downloader implements org.schabi.newpipe.extractor.Downloader {
public String download(String siteUrl) throws IOException, ReCaptchaException {
return download(siteUrl, Collections.emptyMap());
}
}
@Override
public DownloadResponse get(String siteUrl, DownloadRequest request) throws IOException, ReCaptchaException {
final Request.Builder requestBuilder = new Request.Builder()
.method("GET", null).url(siteUrl);
Map<String, List<String>> requestHeaders = request.getRequestHeaders();
// set custom headers in request
for (Map.Entry<String, List<String>> pair : requestHeaders.entrySet()) {
for(String value : pair.getValue()){
requestBuilder.addHeader(pair.getKey(), value);
}
}
if (!requestHeaders.containsKey("User-Agent")) {
requestBuilder.header("User-Agent", USER_AGENT);
}
if (!TextUtils.isEmpty(mCookies)) {
requestBuilder.addHeader("Cookie", mCookies);
}
final Request okRequest = requestBuilder.build();
final Response response = client.newCall(okRequest).execute();
final ResponseBody body = response.body();
if (response.code() == 429) {
throw new ReCaptchaException("reCaptcha Challenge requested");
}
if (body == null) {
response.close();
return null;
}
return new DownloadResponse(body.string(), response.headers().toMultimap());
}
@Override
public DownloadResponse get(String siteUrl) throws IOException, ReCaptchaException {
return get(siteUrl, DownloadRequest.emptyRequest);
}
@Override
public DownloadResponse post(String siteUrl, DownloadRequest request) throws IOException, ReCaptchaException {
Map<String, List<String>> requestHeaders = request.getRequestHeaders();
if(null == requestHeaders.get("Content-Type") || requestHeaders.get("Content-Type").isEmpty()){
// content type header is required. maybe throw an exception here
return null;
}
String contentType = requestHeaders.get("Content-Type").get(0);
RequestBody okRequestBody = null;
if(null != request.getRequestBody()){
okRequestBody = RequestBody.create(MediaType.parse(contentType), request.getRequestBody());
}
final Request.Builder requestBuilder = new Request.Builder()
.method("POST", okRequestBody).url(siteUrl);
// set custom headers in request
for (Map.Entry<String, List<String>> pair : requestHeaders.entrySet()) {
for(String value : pair.getValue()){
requestBuilder.addHeader(pair.getKey(), value);
}
}
if (!requestHeaders.containsKey("User-Agent")) {
requestBuilder.header("User-Agent", USER_AGENT);
}
if (!TextUtils.isEmpty(mCookies)) {
requestBuilder.addHeader("Cookie", mCookies);
}
final Request okRequest = requestBuilder.build();
final Response response = client.newCall(okRequest).execute();
final ResponseBody body = response.body();
if (response.code() == 429) {
throw new ReCaptchaException("reCaptcha Challenge requested");
}
if (body == null) {
response.close();
return null;
}
return new DownloadResponse(body.string(), response.headers().toMultimap());
}
}

View File

@@ -22,11 +22,13 @@ package org.schabi.newpipe;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.design.widget.NavigationView;
import android.support.v4.app.Fragment;
import android.support.v4.view.GravityCompat;
@@ -41,19 +43,24 @@ 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;
import org.schabi.newpipe.util.StateSaver;
import org.schabi.newpipe.util.ThemeHelper;
@@ -67,6 +74,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
//////////////////////////////////////////////////////////////////////////*/
@@ -80,29 +100,64 @@ 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()) {
String title =
s.getServiceInfo().getName() +
(ServiceHelper.isBeta(s) ? " (beta)" : "");
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));
//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));
toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.drawer_open, R.string.drawer_close);
toggle.syncState();
@@ -117,52 +172,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 changeService(MenuItem item) {
if (item.getGroupId() == R.id.menu_services_group) {
drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(false);
ServiceHelper.setSelectedServiceId(this, item.getItemId());
drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(true);
} else {
return false;
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;
}
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.openHistory(this));
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();
@@ -233,6 +415,26 @@ public class MainActivity extends AppCompatActivity {
} else super.onBackPressed();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
for (int i: grantResults){
if (i == PackageManager.PERMISSION_DENIED){
return;
}
}
switch (requestCode) {
case PermissionHelper.DOWNLOADS_REQUEST_CODE:
NavigationHelper.openDownloads(this);
break;
case PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE:
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_holder);
if (fragment instanceof VideoDetailFragment) {
((VideoDetailFragment) fragment).openDownloadDialog();
}
break;
}
}
/**
* Implement the following diagram behavior for the up button:
* <pre>
@@ -305,14 +507,14 @@ public class MainActivity extends AppCompatActivity {
case android.R.id.home:
onHomeButtonPressed();
return true;
case R.id.action_settings:
NavigationHelper.openSettings(this);
return true;
case R.id.action_show_downloads:
return NavigationHelper.openDownloads(this);
case R.id.action_about:
NavigationHelper.openAbout(this);
return true;
return NavigationHelper.openDownloads(this);
case R.id.action_history:
NavigationHelper.openStatisticFragment(getSupportFragmentManager());
return true;
case R.id.action_settings:
NavigationHelper.openSettings(this);
return true;
default:
return super.onOptionsItemSelected(item);
}
@@ -356,31 +558,45 @@ public class MainActivity extends AppCompatActivity {
}
private void handleIntent(Intent intent) {
if (DEBUG) Log.d(TAG, "handleIntent() called with: intent = [" + intent + "]");
try {
if (DEBUG) Log.d(TAG, "handleIntent() called with: intent = [" + intent + "]");
if (intent.hasExtra(Constants.KEY_LINK_TYPE)) {
String url = intent.getStringExtra(Constants.KEY_URL);
int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0);
String title = intent.getStringExtra(Constants.KEY_TITLE);
switch (((StreamingService.LinkType) intent.getSerializableExtra(Constants.KEY_LINK_TYPE))) {
case STREAM:
boolean autoPlay = intent.getBooleanExtra(VideoDetailFragment.AUTO_PLAY, false);
NavigationHelper.openVideoDetailFragment(getSupportFragmentManager(), serviceId, url, title, autoPlay);
break;
case CHANNEL:
NavigationHelper.openChannelFragment(getSupportFragmentManager(), serviceId, url, title);
break;
case PLAYLIST:
NavigationHelper.openPlaylistFragment(getSupportFragmentManager(), serviceId, url, title);
break;
if (intent.hasExtra(Constants.KEY_LINK_TYPE)) {
String url = intent.getStringExtra(Constants.KEY_URL);
int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0);
String title = intent.getStringExtra(Constants.KEY_TITLE);
switch (((StreamingService.LinkType) intent.getSerializableExtra(Constants.KEY_LINK_TYPE))) {
case STREAM:
boolean autoPlay = intent.getBooleanExtra(VideoDetailFragment.AUTO_PLAY, false);
NavigationHelper.openVideoDetailFragment(getSupportFragmentManager(), serviceId, url, title, autoPlay);
break;
case CHANNEL:
NavigationHelper.openChannelFragment(getSupportFragmentManager(),
serviceId,
url,
title);
break;
case PLAYLIST:
NavigationHelper.openPlaylistFragment(getSupportFragmentManager(),
serviceId,
url,
title);
break;
}
} else if (intent.hasExtra(Constants.KEY_OPEN_SEARCH)) {
String searchString = intent.getStringExtra(Constants.KEY_SEARCH_STRING);
if (searchString == null) searchString = "";
int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0);
NavigationHelper.openSearchFragment(
getSupportFragmentManager(),
serviceId,
searchString);
} else {
NavigationHelper.gotoMainFragment(getSupportFragmentManager());
}
} else if (intent.hasExtra(Constants.KEY_OPEN_SEARCH)) {
String searchQuery = intent.getStringExtra(Constants.KEY_QUERY);
if (searchQuery == null) searchQuery = "";
int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0);
NavigationHelper.openSearchFragment(getSupportFragmentManager(), serviceId, searchQuery);
} else {
NavigationHelper.gotoMainFragment(getSupportFragmentManager());
} catch (Exception e) {
ErrorActivity.reportUiError(this, e);
}
}
}

View File

@@ -85,7 +85,7 @@ public class ReCaptchaActivity extends AppCompatActivity {
}
private class ReCaptchaWebViewClient extends WebViewClient {
private Activity context;
private final Activity context;
private String mCookies;
ReCaptchaWebViewClient(Activity ctx) {

View File

@@ -1,12 +1,16 @@
package org.schabi.newpipe;
import android.annotation.SuppressLint;
import android.app.IntentService;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.support.v7.app.AlertDialog;
@@ -22,30 +26,34 @@ import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Toast;
import org.schabi.newpipe.download.DownloadDialog;
import org.schabi.newpipe.extractor.Info;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.ServiceList;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.StreamingService.LinkType;
import org.schabi.newpipe.extractor.channel.ChannelInfo;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.player.helper.PlayerHelper;
import org.schabi.newpipe.playlist.ChannelPlayQueue;
import org.schabi.newpipe.playlist.PlayQueue;
import org.schabi.newpipe.playlist.PlaylistPlayQueue;
import org.schabi.newpipe.playlist.SinglePlayQueue;
import org.schabi.newpipe.player.playqueue.ChannelPlayQueue;
import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue;
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.ListHelper;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.ThemeHelper;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import icepick.Icepick;
import icepick.State;
@@ -57,6 +65,8 @@ import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.AUDIO;
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.VIDEO;
import static org.schabi.newpipe.util.ThemeHelper.resolveResourceIdFromAttr;
/**
@@ -64,17 +74,16 @@ import static org.schabi.newpipe.util.ThemeHelper.resolveResourceIdFromAttr;
*/
public class RouterActivity extends AppCompatActivity {
@State
protected int currentServiceId = -1;
@State protected int currentServiceId = -1;
private StreamingService currentService;
@State
protected LinkType currentLinkType;
@State
protected int selectedRadioPosition = -1;
@State protected LinkType currentLinkType;
@State protected int selectedRadioPosition = -1;
protected int selectedPreviously = -1;
protected String currentUrl;
protected CompositeDisposable disposables = new CompositeDisposable();
protected final CompositeDisposable disposables = new CompositeDisposable();
private boolean selectionIsDownload = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -91,8 +100,7 @@ public class RouterActivity extends AppCompatActivity {
}
setTheme(ThemeHelper.isLightThemeSelected(this)
? R.style.RouterActivityThemeLight
: R.style.RouterActivityThemeDark);
? R.style.RouterActivityThemeLight : R.style.RouterActivityThemeDark);
}
@Override
@@ -104,7 +112,7 @@ public class RouterActivity extends AppCompatActivity {
@Override
protected void onStart() {
super.onStart();
handleUrl(currentUrl);
}
@@ -122,7 +130,7 @@ public class RouterActivity extends AppCompatActivity {
currentService = NewPipe.getServiceByUrl(url);
currentServiceId = currentService.getServiceId();
currentLinkType = currentService.getLinkTypeByUrl(url);
currentUrl = NavigationHelper.getCleanUrl(currentService, url, currentLinkType);
currentUrl = url;
} else {
currentService = NewPipe.getService(currentServiceId);
}
@@ -159,57 +167,76 @@ public class RouterActivity extends AppCompatActivity {
protected void onSuccess() {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
boolean isExtVideoEnabled = preferences.getBoolean(getString(R.string.use_external_video_player_key), false);
boolean isExtAudioEnabled = preferences.getBoolean(getString(R.string.use_external_audio_player_key), false);
final String selectedChoiceKey = preferences.getString(getString(R.string.preferred_open_action_key), getString(R.string.preferred_open_action_default));
if ((isExtAudioEnabled || isExtVideoEnabled) && currentLinkType != LinkType.STREAM) {
Toast.makeText(this, R.string.external_player_unsupported_link_type, Toast.LENGTH_LONG).show();
finish();
return;
}
// TODO: Add some sort of "capabilities" field to services (audio only, video and audio, etc.)
if (currentService == ServiceList.SoundCloud) {
handleChoice(getString(R.string.background_player_key));
return;
}
final String playerChoiceKey = preferences.getString(
getString(R.string.preferred_open_action_key),
getString(R.string.preferred_open_action_default));
final String showInfoKey = getString(R.string.show_info_key);
final String videoPlayerKey = getString(R.string.video_player_key);
final String backgroundPlayerKey = getString(R.string.background_player_key);
final String popupPlayerKey = getString(R.string.popup_player_key);
final String downloadKey = getString(R.string.download_key);
final String alwaysAskKey = getString(R.string.always_ask_open_action_key);
if (playerChoiceKey.equals(alwaysAskKey)) {
showDialog();
if (selectedChoiceKey.equals(alwaysAskKey)) {
final List<AdapterChoiceItem> choices = getChoicesForService(currentService, currentLinkType);
switch (choices.size()) {
case 1:
handleChoice(choices.get(0).key);
break;
case 0:
handleChoice(showInfoKey);
break;
default:
showDialog(choices);
break;
}
} else if (selectedChoiceKey.equals(showInfoKey)) {
handleChoice(showInfoKey);
} else if (selectedChoiceKey.equals(downloadKey)) {
handleChoice(downloadKey);
} else {
handleChoice(playerChoiceKey);
final boolean isExtVideoEnabled = preferences.getBoolean(getString(R.string.use_external_video_player_key), false);
final boolean isExtAudioEnabled = preferences.getBoolean(getString(R.string.use_external_audio_player_key), false);
final boolean isVideoPlayerSelected = selectedChoiceKey.equals(videoPlayerKey) || selectedChoiceKey.equals(popupPlayerKey);
final boolean isAudioPlayerSelected = selectedChoiceKey.equals(backgroundPlayerKey);
if (currentLinkType != LinkType.STREAM) {
if (isExtAudioEnabled && isAudioPlayerSelected || isExtVideoEnabled && isVideoPlayerSelected) {
Toast.makeText(this, R.string.external_player_unsupported_link_type, Toast.LENGTH_LONG).show();
handleChoice(showInfoKey);
return;
}
}
final List<StreamingService.ServiceInfo.MediaCapability> capabilities = currentService.getServiceInfo().getMediaCapabilities();
boolean serviceSupportsChoice = false;
if (isVideoPlayerSelected) {
serviceSupportsChoice = capabilities.contains(VIDEO);
} else if (selectedChoiceKey.equals(backgroundPlayerKey)) {
serviceSupportsChoice = capabilities.contains(AUDIO);
}
if (serviceSupportsChoice) {
handleChoice(selectedChoiceKey);
} else {
handleChoice(showInfoKey);
}
}
}
private void showDialog() {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
final ContextThemeWrapper themeWrapper = new ContextThemeWrapper(this,
ThemeHelper.isLightThemeSelected(this) ? R.style.LightTheme : R.style.DarkTheme);
private void showDialog(final List<AdapterChoiceItem> choices) {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
final Context themeWrapperContext = getThemeWrapperContext();
LayoutInflater inflater = LayoutInflater.from(themeWrapper);
final LayoutInflater inflater = LayoutInflater.from(themeWrapperContext);
final LinearLayout rootLayout = (LinearLayout) inflater.inflate(R.layout.preferred_player_dialog_view, null, false);
final RadioGroup radioGroup = rootLayout.findViewById(android.R.id.list);
final AdapterChoiceItem[] choices = {
new AdapterChoiceItem(getString(R.string.show_info_key), getString(R.string.show_info),
resolveResourceIdFromAttr(themeWrapper, R.attr.info)),
new AdapterChoiceItem(getString(R.string.video_player_key), getString(R.string.video_player),
resolveResourceIdFromAttr(themeWrapper, R.attr.play)),
new AdapterChoiceItem(getString(R.string.background_player_key), getString(R.string.background_player),
resolveResourceIdFromAttr(themeWrapper, R.attr.audio)),
new AdapterChoiceItem(getString(R.string.popup_player_key), getString(R.string.popup_player),
resolveResourceIdFromAttr(themeWrapper, R.attr.popup))
};
final DialogInterface.OnClickListener dialogButtonsClickListener = (dialog, which) -> {
final int indexOfChild = radioGroup.indexOfChild(
radioGroup.findViewById(radioGroup.getCheckedRadioButtonId()));
final AdapterChoiceItem choice = choices[indexOfChild];
final AdapterChoiceItem choice = choices.get(indexOfChild);
handleChoice(choice.key);
@@ -218,15 +245,18 @@ public class RouterActivity extends AppCompatActivity {
}
};
final AlertDialog alertDialog = new AlertDialog.Builder(themeWrapper)
.setTitle(R.string.preferred_player_share_menu_title)
final AlertDialog alertDialog = new AlertDialog.Builder(themeWrapperContext)
.setTitle(R.string.preferred_open_action_share_menu_title)
.setView(radioGroup)
.setCancelable(true)
.setNegativeButton(R.string.just_once, dialogButtonsClickListener)
.setPositiveButton(R.string.always, dialogButtonsClickListener)
.setOnDismissListener((dialog) -> finish())
.setOnDismissListener((dialog) -> {
if(!selectionIsDownload) finish();
})
.create();
//noinspection CodeBlock2Expr
alertDialog.setOnShowListener(dialog -> {
setDialogButtonsState(alertDialog, radioGroup.getCheckedRadioButtonId() != -1);
});
@@ -240,7 +270,7 @@ public class RouterActivity extends AppCompatActivity {
selectedRadioPosition = indexOfChild;
if (selectedPreviously == selectedRadioPosition) {
handleChoice(choices[selectedRadioPosition].key);
handleChoice(choices.get(selectedRadioPosition).key);
}
};
@@ -259,8 +289,8 @@ public class RouterActivity extends AppCompatActivity {
if (selectedRadioPosition == -1) {
final String lastSelectedPlayer = preferences.getString(getString(R.string.preferred_open_action_last_selected_key), null);
if (!TextUtils.isEmpty(lastSelectedPlayer)) {
for (int i = 0; i < choices.length; i++) {
AdapterChoiceItem c = choices[i];
for (int i = 0; i < choices.size(); i++) {
AdapterChoiceItem c = choices.get(i);
if (lastSelectedPlayer.equals(c.key)) {
selectedRadioPosition = i;
break;
@@ -269,7 +299,7 @@ public class RouterActivity extends AppCompatActivity {
}
}
selectedRadioPosition = Math.min(Math.max(-1, selectedRadioPosition), choices.length - 1);
selectedRadioPosition = Math.min(Math.max(-1, selectedRadioPosition), choices.size() - 1);
if (selectedRadioPosition != -1) {
((RadioButton) radioGroup.getChildAt(selectedRadioPosition)).setChecked(true);
}
@@ -278,6 +308,42 @@ public class RouterActivity extends AppCompatActivity {
alertDialog.show();
}
private List<AdapterChoiceItem> getChoicesForService(StreamingService service, LinkType linkType) {
final Context context = getThemeWrapperContext();
final List<AdapterChoiceItem> returnList = new ArrayList<>();
final List<StreamingService.ServiceInfo.MediaCapability> capabilities = service.getServiceInfo().getMediaCapabilities();
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
boolean isExtVideoEnabled = preferences.getBoolean(getString(R.string.use_external_video_player_key), false);
boolean isExtAudioEnabled = preferences.getBoolean(getString(R.string.use_external_audio_player_key), false);
returnList.add(new AdapterChoiceItem(getString(R.string.show_info_key), getString(R.string.show_info),
resolveResourceIdFromAttr(context, R.attr.info)));
if (capabilities.contains(VIDEO) && !(isExtVideoEnabled && linkType != LinkType.STREAM)) {
returnList.add(new AdapterChoiceItem(getString(R.string.video_player_key), getString(R.string.video_player),
resolveResourceIdFromAttr(context, R.attr.play)));
returnList.add(new AdapterChoiceItem(getString(R.string.popup_player_key), getString(R.string.popup_player),
resolveResourceIdFromAttr(context, R.attr.popup)));
}
if (capabilities.contains(AUDIO) && !(isExtAudioEnabled && linkType != LinkType.STREAM)) {
returnList.add(new AdapterChoiceItem(getString(R.string.background_player_key), getString(R.string.background_player),
resolveResourceIdFromAttr(context, R.attr.audio)));
}
returnList.add(new AdapterChoiceItem(getString(R.string.download_key), getString(R.string.download),
resolveResourceIdFromAttr(context, R.attr.download)));
return returnList;
}
private Context getThemeWrapperContext() {
return new ContextThemeWrapper(this,
ThemeHelper.isLightThemeSelected(this) ? R.style.LightTheme : R.style.DarkTheme);
}
private void setDialogButtonsState(AlertDialog dialog, boolean state) {
final Button negativeButton = dialog.getButton(DialogInterface.BUTTON_NEGATIVE);
final Button positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
@@ -287,25 +353,31 @@ public class RouterActivity extends AppCompatActivity {
positiveButton.setEnabled(state);
}
private void handleChoice(final String playerChoiceKey) {
if (Arrays.asList(getResources()
.getStringArray(R.array.preferred_open_action_values_list))
.contains(playerChoiceKey)) {
private void handleChoice(final String selectedChoiceKey) {
final List<String> validChoicesList = Arrays.asList(getResources().getStringArray(R.array.preferred_open_action_values_list));
if (validChoicesList.contains(selectedChoiceKey)) {
PreferenceManager.getDefaultSharedPreferences(this).edit()
.putString(getString(R.string.preferred_open_action_last_selected_key),
playerChoiceKey).apply();
.putString(getString(R.string.preferred_open_action_last_selected_key), selectedChoiceKey)
.apply();
}
if (playerChoiceKey.equals(getString(R.string.popup_player_key))
&& !PermissionHelper.isPopupEnabled(this)) {
if (selectedChoiceKey.equals(getString(R.string.popup_player_key)) && !PermissionHelper.isPopupEnabled(this)) {
PermissionHelper.showPopupEnablementToast(this);
finish();
return;
}
if (selectedChoiceKey.equals(getString(R.string.download_key))) {
if (PermissionHelper.checkStoragePermissions(this, PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE)) {
selectionIsDownload = true;
openDownloadDialog();
}
return;
}
// stop and bypass FetcherService if InfoScreen was selected since
// StreamDetailFragment can fetch data itself
if(playerChoiceKey.equals(getString(R.string.show_info_key))) {
if (selectedChoiceKey.equals(getString(R.string.show_info_key))) {
disposables.add(Observable
.fromCallable(() -> NavigationHelper.getIntentByLink(this, currentUrl))
.subscribeOn(Schedulers.io())
@@ -322,20 +394,57 @@ public class RouterActivity extends AppCompatActivity {
}
final Intent intent = new Intent(this, FetcherService.class);
intent.putExtra(FetcherService.KEY_CHOICE,
new Choice(currentService.getServiceId(),
currentLinkType,
currentUrl,
playerChoiceKey));
final Choice choice = new Choice(currentService.getServiceId(), currentLinkType, currentUrl, selectedChoiceKey);
intent.putExtra(FetcherService.KEY_CHOICE, choice);
startService(intent);
finish();
}
@SuppressLint("CheckResult")
private void openDownloadDialog() {
ExtractorHelper.getStreamInfo(currentServiceId, currentUrl, true)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe((@NonNull StreamInfo result) -> {
List<VideoStream> sortedVideoStreams = ListHelper.getSortedStreamVideosList(this,
result.getVideoStreams(),
result.getVideoOnlyStreams(),
false);
int selectedVideoStreamIndex = ListHelper.getDefaultResolutionIndex(this,
sortedVideoStreams);
android.support.v4.app.FragmentManager fm = getSupportFragmentManager();
DownloadDialog downloadDialog = DownloadDialog.newInstance(result);
downloadDialog.setVideoStreams(sortedVideoStreams);
downloadDialog.setAudioStreams(result.getAudioStreams());
downloadDialog.setSelectedVideoStream(selectedVideoStreamIndex);
downloadDialog.show(fm, "downloadDialog");
fm.executePendingTransactions();
downloadDialog.getDialog().setOnDismissListener(dialog -> {
finish();
});
}, (@NonNull Throwable throwable) -> {
onError();
});
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
for (int i: grantResults){
if (i == PackageManager.PERMISSION_DENIED){
finish();
return;
}
}
if (requestCode == PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE) {
openDownloadDialog();
}
}
private static class AdapterChoiceItem {
final String description, key;
@DrawableRes
final int icon;
@DrawableRes final int icon;
AdapterChoiceItem(String key, String description, int icon) {
this.description = description;
@@ -433,8 +542,7 @@ public class RouterActivity extends AppCompatActivity {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
boolean isExtVideoEnabled = preferences.getBoolean(getString(R.string.use_external_video_player_key), false);
boolean isExtAudioEnabled = preferences.getBoolean(getString(R.string.use_external_audio_player_key), false);
boolean useOldVideoPlayer = PlayerHelper.isUsingOldPlayer(this);
boolean isExtAudioEnabled = preferences.getBoolean(getString(R.string.use_external_audio_player_key), false);;
PlayQueue playQueue;
String playerChoice = choice.playerChoice;
@@ -446,9 +554,6 @@ public class RouterActivity extends AppCompatActivity {
} else if (playerChoice.equals(videoPlayerKey) && isExtVideoEnabled) {
NavigationHelper.playOnExternalVideoPlayer(this, (StreamInfo) info);
} else if (playerChoice.equals(videoPlayerKey) && useOldVideoPlayer) {
NavigationHelper.playOnOldVideoPlayer(this, (StreamInfo) info);
} else {
playQueue = new SinglePlayQueue((StreamInfo) info);
@@ -554,7 +659,7 @@ public class RouterActivity extends AppCompatActivity {
* @param sharedText text to scan for URLs.
* @return potential URLs
*/
protected String[] getUris(final String sharedText) {
protected String[] getUris(final String sharedText) {
final Collection<String> result = new HashSet<>();
if (sharedText != null) {
final String[] array = sharedText.split("\\p{Space}");

View File

@@ -32,7 +32,6 @@ public class AboutActivity extends AppCompatActivity {
new SoftwareComponent("Giga Get", "2014", "Peter Cai", "https://github.com/PaperAirplane-Dev-Team/GigaGet", StandardLicenses.GPL2),
new SoftwareComponent("NewPipe Extractor", "2017", "Christian Schabesberger", "https://github.com/TeamNewPipe/NewPipeExtractor", StandardLicenses.GPL3),
new SoftwareComponent("Jsoup", "2017", "Jonathan Hedley", "https://github.com/jhy/jsoup", StandardLicenses.MIT),
new SoftwareComponent("Google Gson", "2008", "Google Inc", "https://github.com/google/gson", StandardLicenses.APACHE2),
new SoftwareComponent("Rhino", "2015", "Mozilla", "https://www.mozilla.org/rhino/", StandardLicenses.MPL2),
new SoftwareComponent("ACRA", "2013", "Kevin Gaudin", "http://www.acra.ch", StandardLicenses.APACHE2),
new SoftwareComponent("Universal Image Loader", "2011 - 2015", "Sergey Tarasevich", "https://github.com/nostra13/Android-Universal-Image-Loader", StandardLicenses.APACHE2),
@@ -129,47 +128,31 @@ public class AboutActivity extends AppCompatActivity {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_about, container, false);
Context context = this.getContext();
TextView version = rootView.findViewById(R.id.app_version);
version.setText(BuildConfig.VERSION_NAME);
View githubLink = rootView.findViewById(R.id.github_link);
githubLink.setOnClickListener(new OnGithubLinkClickListener());
githubLink.setOnClickListener(nv -> openWebsite(context.getString(R.string.github_url), context));
View donationLink = rootView.findViewById(R.id.donation_link);
donationLink.setOnClickListener(new OnDonationLinkClickListener());
donationLink.setOnClickListener(v -> openWebsite(context.getString(R.string.donation_url), context));
View websiteLink = rootView.findViewById(R.id.website_link);
websiteLink.setOnClickListener(new OnWebsiteLinkClickListener());
websiteLink.setOnClickListener(nv -> openWebsite(context.getString(R.string.website_url), context));
View privacyPolicyLink = rootView.findViewById(R.id.privacy_policy_link);
privacyPolicyLink.setOnClickListener(v -> openWebsite(context.getString(R.string.privacy_policy_url), context));
return rootView;
}
private static class OnGithubLinkClickListener implements View.OnClickListener {
@Override
public void onClick(final View view) {
final Context context = view.getContext();
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(context.getString(R.string.github_url)));
context.startActivity(intent);
}
private void openWebsite(String url, Context context) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
context.startActivity(intent);
}
private static class OnDonationLinkClickListener implements View.OnClickListener {
@Override
public void onClick(final View view) {
final Context context = view.getContext();
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(context.getString(R.string.donation_url)));
context.startActivity(intent);
}
}
private static class OnWebsiteLinkClickListener implements View.OnClickListener {
@Override
public void onClick(final View view) {
final Context context = view.getContext();
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(context.getString(R.string.website_url)));
context.startActivity(intent);
}
}
}

View File

@@ -1,5 +1,6 @@
package org.schabi.newpipe.about;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
@@ -39,7 +40,7 @@ public class LicenseFragment extends Fragment {
* @param license the license to show
*/
public static void showLicense(Context context, License license) {
new LicenseFragmentHelper().execute(context, license);
new LicenseFragmentHelper((Activity) context).execute(license);
}
@Override

View File

@@ -1,8 +1,10 @@
package org.schabi.newpipe.about;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog;
import android.webkit.WebView;
import org.schabi.newpipe.R;
@@ -10,26 +12,46 @@ import org.schabi.newpipe.util.ThemeHelper;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.ref.WeakReference;
public class LicenseFragmentHelper extends AsyncTask<Object, Void, Integer> {
private Context context;
final WeakReference<Activity> weakReference;
private License license;
public LicenseFragmentHelper(@Nullable Activity activity) {
weakReference = new WeakReference<>(activity);
}
@Nullable
private Activity getActivity() {
Activity activity = weakReference.get();
if (activity != null && activity.isFinishing()) {
return null;
} else {
return activity;
}
}
@Override
protected Integer doInBackground(Object... objects) {
context = (Context) objects[0];
license = (License) objects[1];
license = (License) objects[0];
return 1;
}
@Override
protected void onPostExecute(Integer result){
String webViewData = getFormattedLicense(context, license);
AlertDialog.Builder alert = new AlertDialog.Builder(context);
protected void onPostExecute(Integer result) {
Activity activity = getActivity();
if (activity == null) {
return;
}
String webViewData = getFormattedLicense(activity, license);
AlertDialog.Builder alert = new AlertDialog.Builder(activity);
alert.setTitle(license.getName());
WebView wv = new WebView(context);
WebView wv = new WebView(activity);
wv.loadData(webViewData, "text/html; charset=UTF-8", null);
alert.setView(wv);
@@ -55,18 +77,18 @@ public class LicenseFragmentHelper extends AsyncTask<Object, Void, Integer> {
throw new NullPointerException("license is null");
}
String licenseContent = "";
StringBuilder licenseContent = new StringBuilder();
String webViewData;
try {
BufferedReader in = new BufferedReader(new InputStreamReader(context.getAssets().open(license.getFilename()), "UTF-8"));
String str;
while ((str = in.readLine()) != null) {
licenseContent += str;
licenseContent.append(str);
}
in.close();
// split the HTML file and insert the stylesheet into the HEAD of the file
String[] insert = licenseContent.split("</head>");
String[] insert = licenseContent.toString().split("</head>");
webViewData = insert[0] + "<style type=\"text/css\">"
+ getLicenseStylesheet(context) + "</style></head>"
+ insert[1];

View File

@@ -30,7 +30,7 @@ public interface BasicDAO<Entity> {
/* Deletes */
@Delete
int delete(final Entity entity);
void delete(final Entity entity);
@Delete
int delete(final Collection<Entity> entities);
@@ -42,5 +42,5 @@ public interface BasicDAO<Entity> {
int update(final Entity entity);
@Update
int update(final Collection<Entity> entities);
void update(final Collection<Entity> entities);
}

View File

@@ -4,7 +4,6 @@ import android.arch.persistence.room.Dao;
import android.arch.persistence.room.Query;
import android.support.annotation.Nullable;
import org.schabi.newpipe.database.BasicDAO;
import org.schabi.newpipe.database.history.model.SearchHistoryEntry;
import java.util.List;

View File

@@ -5,7 +5,6 @@ import android.arch.persistence.room.Dao;
import android.arch.persistence.room.Query;
import android.support.annotation.Nullable;
import org.schabi.newpipe.database.BasicDAO;
import org.schabi.newpipe.database.history.model.StreamHistoryEntry;
import org.schabi.newpipe.database.stream.StreamStatisticsEntry;
import org.schabi.newpipe.database.history.model.StreamHistoryEntity;

View File

@@ -2,7 +2,6 @@ package org.schabi.newpipe.database.playlist.dao;
import android.arch.persistence.room.Dao;
import android.arch.persistence.room.Query;
import android.arch.persistence.room.Transaction;
import org.schabi.newpipe.database.BasicDAO;
import org.schabi.newpipe.database.playlist.model.PlaylistEntity;
@@ -12,7 +11,6 @@ import java.util.List;
import io.reactivex.Flowable;
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_ID;
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_NAME;
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_TABLE;
@Dao

View File

@@ -8,7 +8,6 @@ import org.schabi.newpipe.database.BasicDAO;
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
import org.schabi.newpipe.database.playlist.PlaylistStreamEntry;
import org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity;
import org.schabi.newpipe.database.stream.model.StreamEntity;
import java.util.List;

View File

@@ -5,8 +5,6 @@ import android.arch.persistence.room.Entity;
import android.arch.persistence.room.Index;
import android.arch.persistence.room.PrimaryKey;
import java.util.Date;
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_NAME;
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_TABLE;

View File

@@ -6,7 +6,6 @@ import android.arch.persistence.room.Ignore;
import android.arch.persistence.room.Index;
import android.arch.persistence.room.PrimaryKey;
import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.database.playlist.PlaylistLocalItem;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
import org.schabi.newpipe.util.Constants;
@@ -71,6 +70,14 @@ public class PlaylistRemoteEntity implements PlaylistLocalItem {
info.getUploaderName(), info.getStreamCount());
}
@Ignore
public boolean isIdenticalTo(final PlaylistInfo info) {
return getServiceId() == info.getServiceId() && getName().equals(info.getName()) &&
getStreamCount() == info.getStreamCount() && getUrl().equals(info.getUrl()) &&
getThumbnailUrl().equals(info.getThumbnailUrl()) &&
getUploader().equals(info.getUploaderName());
}
public long getUid() {
return uid;
}

View File

@@ -10,7 +10,6 @@ import org.schabi.newpipe.database.BasicDAO;
import org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity;
import org.schabi.newpipe.database.stream.model.StreamEntity;
import org.schabi.newpipe.database.history.model.StreamHistoryEntity;
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
import java.util.ArrayList;
import java.util.List;
@@ -23,7 +22,6 @@ import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_SERVI
import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_TABLE;
import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_URL;
import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.STREAM_HISTORY_TABLE;
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_STATE_TABLE;
@Dao
public abstract class StreamDAO implements BasicDAO<StreamEntity> {

View File

@@ -9,7 +9,7 @@ import android.arch.persistence.room.PrimaryKey;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.playlist.PlayQueueItem;
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import org.schabi.newpipe.util.Constants;
import java.io.Serializable;

View File

@@ -16,11 +16,12 @@ import org.schabi.newpipe.settings.SettingsActivity;
import org.schabi.newpipe.util.ThemeHelper;
import us.shandian.giga.service.DownloadManagerService;
import us.shandian.giga.ui.fragment.AllMissionsFragment;
import us.shandian.giga.ui.fragment.MissionsFragment;
public class DownloadActivity extends AppCompatActivity {
private static final String MISSIONS_FRAGMENT_TAG = "fragment_tag";
@Override
protected void onCreate(Bundle savedInstanceState) {
// Service
@@ -42,7 +43,6 @@ public class DownloadActivity extends AppCompatActivity {
actionBar.setDisplayShowTitleEnabled(true);
}
// Fragment
getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
@@ -53,10 +53,10 @@ public class DownloadActivity extends AppCompatActivity {
}
private void updateFragments() {
MissionsFragment fragment = new MissionsFragment();
MissionsFragment fragment = new AllMissionsFragment();
getFragmentManager().beginTransaction()
.replace(R.id.frame, fragment)
.replace(R.id.frame, fragment, MISSIONS_FRAGMENT_TAG)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.commit();
}
@@ -87,4 +87,8 @@ public class DownloadActivity extends AppCompatActivity {
}
}
@Override
public void onRestoreInstanceState(Bundle inState){
super.onRestoreInstanceState(inState);
}
}

View File

@@ -1,56 +1,82 @@
package org.schabi.newpipe.download;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.SeekBar;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.NewPipe;
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.SubtitlesStream;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.fragments.detail.SpinnerToolbarAdapter;
import org.schabi.newpipe.extractor.utils.Localization;
import org.schabi.newpipe.settings.NewPipeSettings;
import org.schabi.newpipe.util.FilenameUtils;
import org.schabi.newpipe.util.ListHelper;
import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.SecondaryStreamHelper;
import org.schabi.newpipe.util.StreamItemAdapter;
import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper;
import org.schabi.newpipe.util.ThemeHelper;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import icepick.Icepick;
import icepick.State;
import io.reactivex.disposables.CompositeDisposable;
import us.shandian.giga.postprocessing.Postprocessing;
import us.shandian.giga.service.DownloadManagerService;
public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheckedChangeListener, AdapterView.OnItemSelectedListener {
private static final String TAG = "DialogFragment";
private static final boolean DEBUG = MainActivity.DEBUG;
private static final String INFO_KEY = "info_key";
private static final String SORTED_VIDEOS_LIST_KEY = "sorted_videos_list_key";
private static final String SELECTED_VIDEO_KEY = "selected_video_key";
private static final String SELECTED_AUDIO_KEY = "selected_audio_key";
@State
protected StreamInfo currentInfo;
@State
protected StreamSizeWrapper<AudioStream> wrappedAudioStreams = StreamSizeWrapper.empty();
@State
protected StreamSizeWrapper<VideoStream> wrappedVideoStreams = StreamSizeWrapper.empty();
@State
protected StreamSizeWrapper<SubtitlesStream> wrappedSubtitleStreams = StreamSizeWrapper.empty();
@State
protected int selectedVideoIndex = 0;
@State
protected int selectedAudioIndex = 0;
@State
protected int selectedSubtitleIndex = 0;
private StreamInfo currentInfo;
private ArrayList<VideoStream> sortedStreamVideosList;
private int selectedVideoIndex;
private int selectedAudioIndex;
private StreamItemAdapter<AudioStream, Stream> audioStreamsAdapter;
private StreamItemAdapter<VideoStream, AudioStream> videoStreamsAdapter;
private StreamItemAdapter<SubtitlesStream, Stream> subtitleStreamsAdapter;
private final CompositeDisposable disposables = new CompositeDisposable();
private EditText nameEditText;
private Spinner streamsSpinner;
@@ -58,17 +84,66 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
private TextView threadsCountTextView;
private SeekBar threadsSeekBar;
public static DownloadDialog newInstance(StreamInfo info, ArrayList<VideoStream> sortedStreamVideosList, int selectedVideoIndex) {
private SharedPreferences prefs;
public static DownloadDialog newInstance(StreamInfo info) {
DownloadDialog dialog = new DownloadDialog();
dialog.setInfo(info, sortedStreamVideosList, selectedVideoIndex);
dialog.setStyle(DialogFragment.STYLE_NO_TITLE, 0);
dialog.setInfo(info);
return dialog;
}
private void setInfo(StreamInfo info, ArrayList<VideoStream> sortedStreamVideosList, int selectedVideoIndex) {
public static DownloadDialog newInstance(Context context, StreamInfo info) {
final ArrayList<VideoStream> streamsList = new ArrayList<>(ListHelper.getSortedStreamVideosList(context,
info.getVideoStreams(), info.getVideoOnlyStreams(), false));
final int selectedStreamIndex = ListHelper.getDefaultResolutionIndex(context, streamsList);
final DownloadDialog instance = newInstance(info);
instance.setVideoStreams(streamsList);
instance.setSelectedVideoStream(selectedStreamIndex);
instance.setAudioStreams(info.getAudioStreams());
instance.setSubtitleStreams(info.getSubtitles());
return instance;
}
private void setInfo(StreamInfo info) {
this.currentInfo = info;
}
public void setAudioStreams(List<AudioStream> audioStreams) {
setAudioStreams(new StreamSizeWrapper<>(audioStreams, getContext()));
}
public void setAudioStreams(StreamSizeWrapper<AudioStream> wrappedAudioStreams) {
this.wrappedAudioStreams = wrappedAudioStreams;
}
public void setVideoStreams(List<VideoStream> videoStreams) {
setVideoStreams(new StreamSizeWrapper<>(videoStreams, getContext()));
}
public void setVideoStreams(StreamSizeWrapper<VideoStream> wrappedVideoStreams) {
this.wrappedVideoStreams = wrappedVideoStreams;
}
public void setSubtitleStreams(List<SubtitlesStream> subtitleStreams) {
setSubtitleStreams(new StreamSizeWrapper<>(subtitleStreams, getContext()));
}
public void setSubtitleStreams(StreamSizeWrapper<SubtitlesStream> wrappedSubtitleStreams) {
this.wrappedSubtitleStreams = wrappedSubtitleStreams;
}
public void setSelectedVideoStream(int selectedVideoIndex) {
this.selectedVideoIndex = selectedVideoIndex;
this.sortedStreamVideosList = sortedStreamVideosList;
}
public void setSelectedAudioStream(int selectedAudioIndex) {
this.selectedAudioIndex = selectedAudioIndex;
}
public void setSelectedSubtitleStream(int selectedSubtitleIndex) {
this.selectedSubtitleIndex = selectedSubtitleIndex;
}
/*//////////////////////////////////////////////////////////////////////////
@@ -78,59 +153,75 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
if (!PermissionHelper.checkStoragePermissions(getActivity())) {
if (DEBUG)
Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
if (!PermissionHelper.checkStoragePermissions(getActivity(), PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE)) {
getDialog().dismiss();
return;
}
if (savedInstanceState != null) {
Serializable serial = savedInstanceState.getSerializable(INFO_KEY);
if (serial instanceof StreamInfo) currentInfo = (StreamInfo) serial;
setStyle(STYLE_NO_TITLE, ThemeHelper.getDialogTheme(getContext()));
Icepick.restoreInstanceState(this, savedInstanceState);
serial = savedInstanceState.getSerializable(SORTED_VIDEOS_LIST_KEY);
if (serial instanceof ArrayList) { //noinspection unchecked
sortedStreamVideosList = (ArrayList<VideoStream>) serial;
SparseArray<SecondaryStreamHelper<AudioStream>> secondaryStreams = new SparseArray<>(4);
List<VideoStream> videoStreams = wrappedVideoStreams.getStreamsList();
for (int i = 0; i < videoStreams.size(); i++) {
if (!videoStreams.get(i).isVideoOnly()) continue;
AudioStream audioStream = SecondaryStreamHelper.getAudioStreamFor(wrappedAudioStreams.getStreamsList(), videoStreams.get(i));
if (audioStream != null) {
secondaryStreams.append(i, new SecondaryStreamHelper<>(wrappedAudioStreams, audioStream));
} else if (DEBUG) {
Log.w(TAG, "No audio stream candidates for video format " + videoStreams.get(i).getFormat().name());
}
selectedVideoIndex = savedInstanceState.getInt(SELECTED_VIDEO_KEY, 0);
selectedAudioIndex = savedInstanceState.getInt(SELECTED_AUDIO_KEY, 0);
}
this.videoStreamsAdapter = new StreamItemAdapter<>(getContext(), wrappedVideoStreams, secondaryStreams);
this.audioStreamsAdapter = new StreamItemAdapter<>(getContext(), wrappedAudioStreams);
this.subtitleStreamsAdapter = new StreamItemAdapter<>(getContext(), wrappedSubtitleStreams);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (DEBUG) Log.d(TAG, "onCreateView() called with: inflater = [" + inflater + "], container = [" + container + "], savedInstanceState = [" + savedInstanceState + "]");
return inflater.inflate(R.layout.dialog_url, container);
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (DEBUG)
Log.d(TAG, "onCreateView() called with: inflater = [" + inflater + "], container = [" + container + "], savedInstanceState = [" + savedInstanceState + "]");
return inflater.inflate(R.layout.download_dialog, container);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
nameEditText = view.findViewById(R.id.file_name);
nameEditText.setText(FilenameUtils.createFilename(getContext(), currentInfo.getName()));
selectedAudioIndex = ListHelper.getDefaultAudioFormat(getContext(), currentInfo.getAudioStreams());
selectedSubtitleIndex = getSubtitleIndexBy(subtitleStreamsAdapter.getAll());
streamsSpinner = view.findViewById(R.id.quality_spinner);
streamsSpinner.setOnItemSelectedListener(this);
threadsCountTextView = view.findViewById(R.id.threads_count);
threadsSeekBar = view.findViewById(R.id.threads);
radioVideoAudioGroup = view.findViewById(R.id.video_audio_group);
radioVideoAudioGroup.setOnCheckedChangeListener(this);
initToolbar(view.<Toolbar>findViewById(R.id.toolbar));
checkDownloadOptions(view);
setupVideoSpinner(sortedStreamVideosList, streamsSpinner);
initToolbar(view.findViewById(R.id.toolbar));
setupDownloadOptions();
int def = 3;
threadsCountTextView.setText(String.valueOf(def));
threadsSeekBar.setProgress(def - 1);
prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
int threads = prefs.getInt(getString(R.string.default_download_threads), 3);
threadsCountTextView.setText(String.valueOf(threads));
threadsSeekBar.setProgress(threads - 1);
threadsSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekbar, int progress, boolean fromUser) {
threadsCountTextView.setText(String.valueOf(progress + 1));
progress++;
prefs.edit().putInt(getString(R.string.default_download_threads), progress).apply();
threadsCountTextView.setText(String.valueOf(progress));
}
@Override
@@ -141,15 +232,40 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
public void onStopTrackingTouch(SeekBar p1) {
}
});
fetchStreamsSize();
}
private void fetchStreamsSize() {
disposables.clear();
disposables.add(StreamSizeWrapper.fetchSizeForWrapper(wrappedVideoStreams).subscribe(result -> {
if (radioVideoAudioGroup.getCheckedRadioButtonId() == R.id.video_button) {
setupVideoSpinner();
}
}));
disposables.add(StreamSizeWrapper.fetchSizeForWrapper(wrappedAudioStreams).subscribe(result -> {
if (radioVideoAudioGroup.getCheckedRadioButtonId() == R.id.audio_button) {
setupAudioSpinner();
}
}));
disposables.add(StreamSizeWrapper.fetchSizeForWrapper(wrappedSubtitleStreams).subscribe(result -> {
if (radioVideoAudioGroup.getCheckedRadioButtonId() == R.id.subtitle_button) {
setupSubtitleSpinner();
}
}));
}
@Override
public void onDestroy() {
super.onDestroy();
disposables.clear();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable(INFO_KEY, currentInfo);
outState.putSerializable(SORTED_VIDEOS_LIST_KEY, sortedStreamVideosList);
outState.putInt(SELECTED_VIDEO_KEY, selectedVideoIndex);
outState.putInt(SELECTED_AUDIO_KEY, selectedAudioIndex);
Icepick.saveInstanceState(this, outState);
}
/*//////////////////////////////////////////////////////////////////////////
@@ -161,39 +277,39 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
toolbar.setTitle(R.string.download_dialog_title);
toolbar.setNavigationIcon(ThemeHelper.isLightThemeSelected(getActivity()) ? R.drawable.ic_arrow_back_black_24dp : R.drawable.ic_arrow_back_white_24dp);
toolbar.inflateMenu(R.menu.dialog_url);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getDialog().dismiss();
}
});
toolbar.setNavigationOnClickListener(v -> getDialog().dismiss());
toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
if (item.getItemId() == R.id.okay) {
downloadSelected();
return true;
} else return false;
toolbar.setOnMenuItemClickListener(item -> {
if (item.getItemId() == R.id.okay) {
prepareSelectedDownload();
return true;
}
return false;
});
}
public void setupAudioSpinner(final List<AudioStream> audioStreams, Spinner spinner) {
String[] items = new String[audioStreams.size()];
for (int i = 0; i < audioStreams.size(); i++) {
AudioStream audioStream = audioStreams.get(i);
items[i] = audioStream.getFormat().getName() + " " + audioStream.getAverageBitrate() + "kbps";
}
private void setupAudioSpinner() {
if (getContext() == null) return;
ArrayAdapter<String> itemAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_dropdown_item, items);
spinner.setAdapter(itemAdapter);
spinner.setSelection(selectedAudioIndex);
streamsSpinner.setAdapter(audioStreamsAdapter);
streamsSpinner.setSelection(selectedAudioIndex);
setRadioButtonsState(true);
}
public void setupVideoSpinner(final List<VideoStream> videoStreams, Spinner spinner) {
spinner.setAdapter(new SpinnerToolbarAdapter(getContext(), videoStreams, true));
spinner.setSelection(selectedVideoIndex);
private void setupVideoSpinner() {
if (getContext() == null) return;
streamsSpinner.setAdapter(videoStreamsAdapter);
streamsSpinner.setSelection(selectedVideoIndex);
setRadioButtonsState(true);
}
private void setupSubtitleSpinner() {
if (getContext() == null) return;
streamsSpinner.setAdapter(subtitleStreamsAdapter);
streamsSpinner.setSelection(selectedSubtitleIndex);
setRadioButtonsState(true);
}
/*//////////////////////////////////////////////////////////////////////////
@@ -202,15 +318,24 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
@Override
public void onCheckedChanged(RadioGroup group, @IdRes int checkedId) {
if (DEBUG) Log.d(TAG, "onCheckedChanged() called with: group = [" + group + "], checkedId = [" + checkedId + "]");
if (DEBUG)
Log.d(TAG, "onCheckedChanged() called with: group = [" + group + "], checkedId = [" + checkedId + "]");
boolean flag = true;
switch (checkedId) {
case R.id.audio_button:
setupAudioSpinner(currentInfo.getAudioStreams(), streamsSpinner);
setupAudioSpinner();
break;
case R.id.video_button:
setupVideoSpinner(sortedStreamVideosList, streamsSpinner);
setupVideoSpinner();
break;
case R.id.subtitle_button:
setupSubtitleSpinner();
flag = false;
break;
}
threadsSeekBar.setEnabled(flag);
}
/*//////////////////////////////////////////////////////////////////////////
@@ -219,7 +344,8 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (DEBUG) Log.d(TAG, "onItemSelected() called with: parent = [" + parent + "], view = [" + view + "], position = [" + position + "], id = [" + id + "]");
if (DEBUG)
Log.d(TAG, "onItemSelected() called with: parent = [" + parent + "], view = [" + view + "], position = [" + position + "], id = [" + id + "]");
switch (radioVideoAudioGroup.getCheckedRadioButtonId()) {
case R.id.audio_button:
selectedAudioIndex = position;
@@ -227,6 +353,9 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
case R.id.video_button:
selectedVideoIndex = position;
break;
case R.id.subtitle_button:
selectedSubtitleIndex = position;
break;
}
}
@@ -238,38 +367,175 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
// Utils
//////////////////////////////////////////////////////////////////////////*/
protected void checkDownloadOptions(View view) {
RadioButton audioButton = view.findViewById(R.id.audio_button);
RadioButton videoButton = view.findViewById(R.id.video_button);
protected void setupDownloadOptions() {
setRadioButtonsState(false);
if (currentInfo.getAudioStreams() == null || currentInfo.getAudioStreams().size() == 0) {
audioButton.setVisibility(View.GONE);
final RadioButton audioButton = radioVideoAudioGroup.findViewById(R.id.audio_button);
final RadioButton videoButton = radioVideoAudioGroup.findViewById(R.id.video_button);
final RadioButton subtitleButton = radioVideoAudioGroup.findViewById(R.id.subtitle_button);
final boolean isVideoStreamsAvailable = videoStreamsAdapter.getCount() > 0;
final boolean isAudioStreamsAvailable = audioStreamsAdapter.getCount() > 0;
final boolean isSubtitleStreamsAvailable = subtitleStreamsAdapter.getCount() > 0;
audioButton.setVisibility(isAudioStreamsAvailable ? View.VISIBLE : View.GONE);
videoButton.setVisibility(isVideoStreamsAvailable ? View.VISIBLE : View.GONE);
subtitleButton.setVisibility(isSubtitleStreamsAvailable ? View.VISIBLE : View.GONE);
if (isVideoStreamsAvailable) {
videoButton.setChecked(true);
} else if (sortedStreamVideosList == null || sortedStreamVideosList.size() == 0) {
videoButton.setVisibility(View.GONE);
setupVideoSpinner();
} else if (isAudioStreamsAvailable) {
audioButton.setChecked(true);
setupAudioSpinner();
} else if (isSubtitleStreamsAvailable) {
subtitleButton.setChecked(true);
setupSubtitleSpinner();
} else {
Toast.makeText(getContext(), R.string.no_streams_available_download, Toast.LENGTH_SHORT).show();
getDialog().dismiss();
}
}
private void setRadioButtonsState(boolean enabled) {
radioVideoAudioGroup.findViewById(R.id.audio_button).setEnabled(enabled);
radioVideoAudioGroup.findViewById(R.id.video_button).setEnabled(enabled);
radioVideoAudioGroup.findViewById(R.id.subtitle_button).setEnabled(enabled);
}
private void downloadSelected() {
String url, location;
private int getSubtitleIndexBy(List<SubtitlesStream> streams) {
Localization loc = NewPipe.getPreferredLocalization();
String fileName = nameEditText.getText().toString().trim();
if (fileName.isEmpty()) fileName = FilenameUtils.createFilename(getContext(), currentInfo.getName());
boolean isAudio = radioVideoAudioGroup.getCheckedRadioButtonId() == R.id.audio_button;
if (isAudio) {
url = currentInfo.getAudioStreams().get(selectedAudioIndex).getUrl();
location = NewPipeSettings.getAudioDownloadPath(getContext());
fileName += "." + currentInfo.getAudioStreams().get(selectedAudioIndex).getFormat().getSuffix();
} else {
url = sortedStreamVideosList.get(selectedVideoIndex).getUrl();
location = NewPipeSettings.getVideoDownloadPath(getContext());
fileName += "." + sortedStreamVideosList.get(selectedVideoIndex).getFormat().getSuffix();
for (int i = 0; i < streams.size(); i++) {
Locale streamLocale = streams.get(i).getLocale();
String tag = streamLocale.getLanguage().concat("-").concat(streamLocale.getCountry());
if (tag.equalsIgnoreCase(loc.getLanguage())) {
return i;
}
}
DownloadManagerService.startMission(getContext(), url, location, fileName, isAudio, threadsSeekBar.getProgress() + 1);
// fallback
// 1st loop match country & language
// 2nd loop match language only
int index = loc.getLanguage().indexOf("-");
String lang = index > 0 ? loc.getLanguage().substring(0, index) : loc.getLanguage();
for (int j = 0; j < 2; j++) {
for (int i = 0; i < streams.size(); i++) {
Locale streamLocale = streams.get(i).getLocale();
if (streamLocale.getLanguage().equalsIgnoreCase(lang)) {
if (j > 0 || streamLocale.getCountry().equalsIgnoreCase(loc.getCountry())) {
return i;
}
}
}
}
return 0;
}
private void prepareSelectedDownload() {
final Context context = getContext();
Stream stream;
String location;
char kind;
String fileName = nameEditText.getText().toString().trim();
if (fileName.isEmpty())
fileName = FilenameUtils.createFilename(context, currentInfo.getName());
switch (radioVideoAudioGroup.getCheckedRadioButtonId()) {
case R.id.audio_button:
stream = audioStreamsAdapter.getItem(selectedAudioIndex);
location = NewPipeSettings.getAudioDownloadPath(context);
kind = 'a';
break;
case R.id.video_button:
stream = videoStreamsAdapter.getItem(selectedVideoIndex);
location = NewPipeSettings.getVideoDownloadPath(context);
kind = 'v';
break;
case R.id.subtitle_button:
stream = subtitleStreamsAdapter.getItem(selectedSubtitleIndex);
location = NewPipeSettings.getVideoDownloadPath(context);// assume that subtitle & video files go together
kind = 's';
break;
default:
return;
}
int threads;
if (radioVideoAudioGroup.getCheckedRadioButtonId() == R.id.subtitle_button) {
threads = 1;// use unique thread for subtitles due small file size
fileName += ".srt";// final subtitle format
} else {
threads = threadsSeekBar.getProgress() + 1;
fileName += "." + stream.getFormat().getSuffix();
}
final String finalFileName = fileName;
DownloadManagerService.checkForRunningMission(context, location, fileName, (listed, finished) -> {
if (listed) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.download_dialog_title)
.setMessage(finished ? R.string.overwrite_warning : R.string.download_already_running)
.setPositiveButton(
finished ? R.string.overwrite : R.string.generate_unique_name,
(dialog, which) -> downloadSelected(context, stream, location, finalFileName, kind, threads)
)
.setNegativeButton(android.R.string.cancel, (dialog, which) -> {
dialog.cancel();
})
.create()
.show();
} else {
downloadSelected(context, stream, location, finalFileName, kind, threads);
}
});
}
private void downloadSelected(Context context, Stream selectedStream, String location, String fileName, char kind, int threads) {
String[] urls;
String psName = null;
String[] psArgs = null;
String secondaryStreamUrl = null;
long nearLength = 0;
if (selectedStream instanceof VideoStream) {
SecondaryStreamHelper<AudioStream> secondaryStream = videoStreamsAdapter
.getAllSecondary()
.get(wrappedVideoStreams.getStreamsList().indexOf(selectedStream));
if (secondaryStream != null) {
secondaryStreamUrl = secondaryStream.getStream().getUrl();
psName = selectedStream.getFormat() == MediaFormat.MPEG_4 ? Postprocessing.ALGORITHM_MP4_MUXER : Postprocessing.ALGORITHM_WEBM_MUXER;
psArgs = null;
long videoSize = wrappedVideoStreams.getSizeInBytes((VideoStream) selectedStream);
// set nearLength, only, if both sizes are fetched or known. this probably does not work on slow networks
if (secondaryStream.getSizeInBytes() > 0 && videoSize > 0) {
nearLength = secondaryStream.getSizeInBytes() + videoSize;
}
}
} else if ((selectedStream instanceof SubtitlesStream) && selectedStream.getFormat() == MediaFormat.TTML) {
psName = Postprocessing.ALGORITHM_TTML_CONVERTER;
psArgs = new String[]{
selectedStream.getFormat().getSuffix(),
"false",// ignore empty frames
"false",// detect youtube duplicate lines
};
}
if (secondaryStreamUrl == null) {
urls = new String[]{selectedStream.getUrl()};
} else {
urls = new String[]{selectedStream.getUrl(), secondaryStreamUrl};
}
DownloadManagerService.startMission(context, urls, location, fileName, kind, threads, currentInfo.getUrl(), psName, psArgs, nearLength);
getDialog().dismiss();
}
}

View File

@@ -32,7 +32,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
import icepick.State;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.functions.Consumer;
import static org.schabi.newpipe.util.AnimationUtils.animateView;
@@ -51,9 +50,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 +62,6 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
wasLoading.set(isLoading.get());
}
public void useAsFrontPage(boolean value) {
useAsFrontPage = value;
}
/*//////////////////////////////////////////////////////////////////////////
// Init
@@ -93,12 +86,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() {
@@ -239,7 +227,8 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
if (rootView == null && getView() != null) rootView = getView();
if (rootView == null) return;
ErrorActivity.reportError(getContext(), exception, MainActivity.class, rootView, ErrorActivity.ErrorInfo.make(userAction, serviceName, request, errorId));
ErrorActivity.reportError(getContext(), exception, MainActivity.class, rootView,
ErrorActivity.ErrorInfo.make(userAction, serviceName, request, errorId));
}
/*//////////////////////////////////////////////////////////////////////////

View File

@@ -2,7 +2,6 @@ package org.schabi.newpipe.fragments;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -14,24 +13,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;
@@ -10,48 +9,37 @@ import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
import android.support.v7.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
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.fragments.list.feed.FeedFragment;
import org.schabi.newpipe.fragments.list.kiosk.KioskFragment;
import org.schabi.newpipe.fragments.local.bookmark.BookmarkFragment;
import org.schabi.newpipe.fragments.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
@@ -61,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);
}
@@ -73,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
//////////////////////////////////////////////////////////////////////////*/
@@ -106,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) {
@@ -127,7 +120,14 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_search:
NavigationHelper.openSearchFragment(getFragmentManager(), ServiceHelper.getSelectedServiceId(activity), "");
try {
NavigationHelper.openSearchFragment(
getFragmentManager(),
ServiceHelper.getSelectedServiceId(activity),
"");
} catch (Exception e) {
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
}
return true;
}
return super.onOptionsItemSelected(item);
@@ -137,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
@@ -148,129 +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(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
try {
NavigationHelper.openKioskFragment(getFragmentManager(), currentServiceId, ks);
} catch (Exception e) {
ErrorActivity.reportError(activity, e,
activity.getClass(),
null,
ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
"none", "", R.string.app_ui_crash));
}
return true;
}
});
i++;
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
getChildFragmentManager()
.beginTransaction()
.remove((Fragment) object)
.commitNowAllowingStateLoss();
}
}
}

View File

@@ -1,74 +0,0 @@
package org.schabi.newpipe.fragments.detail;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.TextView;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.stream.VideoStream;
import java.util.List;
public class SpinnerToolbarAdapter extends BaseAdapter {
private final List<VideoStream> videoStreams;
private final boolean showIconNoAudio;
private final Context context;
public SpinnerToolbarAdapter(Context context, List<VideoStream> videoStreams, boolean showIconNoAudio) {
this.context = context;
this.videoStreams = videoStreams;
this.showIconNoAudio = showIconNoAudio;
}
@Override
public int getCount() {
return videoStreams.size();
}
@Override
public Object getItem(int position) {
return videoStreams.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
return getCustomView(position, convertView, parent, true);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return getCustomView(((Spinner) parent).getSelectedItemPosition(), convertView, parent, false);
}
private View getCustomView(int position, View convertView, ViewGroup parent, boolean isDropdownItem) {
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(R.layout.resolutions_spinner_item, parent, false);
}
ImageView woSoundIcon = convertView.findViewById(R.id.wo_sound_icon);
TextView text = convertView.findViewById(android.R.id.text1);
VideoStream item = (VideoStream) getItem(position);
text.setText(item.getFormat().getName() + " " + item.getResolution());
int visibility = !showIconNoAudio ? View.GONE
: item.isVideoOnly ? View.VISIBLE
: isDropdownItem ? View.INVISIBLE
: View.GONE;
woSoundIcon.setVisibility(visibility);
return convertView;
}
}

View File

@@ -3,9 +3,9 @@ package org.schabi.newpipe.fragments.detail;
import java.io.Serializable;
class StackItem implements Serializable {
private int serviceId;
private final int serviceId;
private String title;
private String url;
private final String url;
StackItem(int serviceId, String url, String title) {
this.serviceId = serviceId;

View File

@@ -0,0 +1,73 @@
package org.schabi.newpipe.fragments.detail;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
public class TabAdaptor extends FragmentPagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
private final FragmentManager fragmentManager;
public TabAdaptor(FragmentManager fm) {
super(fm);
this.fragmentManager = fm;
}
@Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
@Override
public int getCount() {
return mFragmentList.size();
}
public void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
public void clearAllItems() {
mFragmentList.clear();
mFragmentTitleList.clear();
}
public void removeItem(int position){
mFragmentList.remove(position == 0 ? 0 : position - 1);
mFragmentTitleList.remove(position == 0 ? 0 : position - 1);
}
public void updateItem(int position, Fragment fragment){
mFragmentList.set(position, fragment);
}
public void updateItem(String title, Fragment fragment){
int index = mFragmentTitleList.indexOf(title);
if(index != -1){
updateItem(index, fragment);
}
}
@Override
public int getItemPosition(Object object) {
if (mFragmentList.contains(object)) return mFragmentList.indexOf(object);
else return POSITION_NONE;
}
public void notifyDataSetUpdate(){
notifyDataSetChanged();
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
fragmentManager.beginTransaction().remove((Fragment) object).commitNowAllowingStateLoss();
}
}

View File

@@ -10,12 +10,16 @@ import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.DrawableRes;
import android.support.annotation.FloatRange;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.animation.FastOutSlowInInterpolator;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.text.Html;
import android.text.Spanned;
import android.text.TextUtils;
@@ -23,7 +27,6 @@ import android.text.method.LinkMovementMethod;
import android.text.util.Linkify;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -33,7 +36,6 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
@@ -41,7 +43,6 @@ import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import com.nirhart.parallaxscroll.views.ParallaxScrollView;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
@@ -51,27 +52,31 @@ import org.schabi.newpipe.ReCaptchaActivity;
import org.schabi.newpipe.download.DownloadDialog;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.ServiceList;
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor;
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.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.fragments.BackPressable;
import org.schabi.newpipe.fragments.BaseStateFragment;
import org.schabi.newpipe.fragments.local.dialog.PlaylistAppendDialog;
import org.schabi.newpipe.info_list.InfoItemBuilder;
import org.schabi.newpipe.fragments.list.comments.CommentsFragment;
import org.schabi.newpipe.fragments.list.videos.RelatedVideosFragment;
import org.schabi.newpipe.info_list.InfoItemDialog;
import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
import org.schabi.newpipe.local.history.HistoryRecordManager;
import org.schabi.newpipe.player.MainVideoPlayer;
import org.schabi.newpipe.player.PopupVideoPlayer;
import org.schabi.newpipe.player.helper.PlayerHelper;
import org.schabi.newpipe.player.old.PlayVideoActivity;
import org.schabi.newpipe.playlist.PlayQueue;
import org.schabi.newpipe.playlist.SinglePlayQueue;
import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.AnimationUtils;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.ImageDisplayConstants;
@@ -79,14 +84,15 @@ import org.schabi.newpipe.util.InfoCache;
import org.schabi.newpipe.util.ListHelper;
import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.OnClickGesture;
import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.ThemeHelper;
import org.schabi.newpipe.util.StreamItemAdapter;
import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import icepick.State;
import io.reactivex.Single;
@@ -95,6 +101,7 @@ import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.COMMENTS;
import static org.schabi.newpipe.util.AnimationUtils.animateView;
public class VideoDetailFragment
@@ -105,21 +112,15 @@ public class VideoDetailFragment
View.OnLongClickListener {
public static final String AUTO_PLAY = "auto_play";
// Amount of videos to show on start
private static final int INITIAL_RELATED_VIDEOS = 8;
private ArrayList<VideoStream> sortedStreamVideosList;
private InfoItemBuilder infoItemBuilder = null;
private int updateFlags = 0;
private static final int RELATED_STREAMS_UPDATE_FLAG = 0x1;
private static final int RESOLUTIONS_MENU_UPDATE_FLAG = 0x2;
private static final int TOOLBAR_ITEMS_UPDATE_FLAG = 0x4;
private static final int COMMENTS_UPDATE_FLAG = 0x4;
private boolean autoPlayEnabled;
private boolean showRelatedStreams;
private boolean wasRelatedStreamsExpanded = false;
private boolean showComments;
@State
protected int serviceId = Constants.NO_SERVICE_ID;
@@ -130,9 +131,11 @@ public class VideoDetailFragment
private StreamInfo currentInfo;
private Disposable currentWorker;
@NonNull
private CompositeDisposable disposables = new CompositeDisposable();
private int selectedVideoStream = -1;
private List<VideoStream> sortedVideoStreams;
private int selectedVideoStreamIndex = -1;
/*//////////////////////////////////////////////////////////////////////////
// Views
@@ -142,7 +145,6 @@ public class VideoDetailFragment
private Spinner spinnerToolbar;
private ParallaxScrollView parallaxScrollRootView;
private LinearLayout contentRootLayoutHiding;
private View thumbnailBackgroundButton;
@@ -175,10 +177,14 @@ public class VideoDetailFragment
private ImageView thumbsDownImageView;
private TextView thumbsDisabledTextView;
private TextView nextStreamTitle;
private LinearLayout relatedStreamRootLayout;
private LinearLayout relatedStreamsView;
private ImageButton relatedStreamExpandButton;
private static final String COMMENTS_TAB_TAG = "COMMENTS";
private static final String RELATED_TAB_TAG = "NEXT VIDEO";
private AppBarLayout appBarLayout;
private ViewPager viewPager;
private TabAdaptor pageAdapter;
private TabLayout tabLayout;
private FrameLayout relatedStreamsLayout;
/*////////////////////////////////////////////////////////////////////////*/
@@ -194,18 +200,23 @@ public class VideoDetailFragment
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onCreate(Bundle savedInstanceState) {
public void
onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
showRelatedStreams = PreferenceManager.getDefaultSharedPreferences(activity)
.getBoolean(getString(R.string.show_next_video_key), true);
showComments = PreferenceManager.getDefaultSharedPreferences(activity)
.getBoolean(getString(R.string.show_comments_key), true);
PreferenceManager.getDefaultSharedPreferences(activity)
.registerOnSharedPreferenceChangeListener(this);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_video_detail, container, false);
}
@@ -221,14 +232,16 @@ public class VideoDetailFragment
if (updateFlags != 0) {
if (!isLoading.get() && currentInfo != null) {
if ((updateFlags & RELATED_STREAMS_UPDATE_FLAG) != 0) initRelatedVideos(currentInfo);
if ((updateFlags & RELATED_STREAMS_UPDATE_FLAG) != 0) startLoading(false);
if ((updateFlags & RESOLUTIONS_MENU_UPDATE_FLAG) != 0) setupActionBar(currentInfo);
if ((updateFlags & COMMENTS_UPDATE_FLAG) != 0) startLoading(false);
}
if ((updateFlags & TOOLBAR_ITEMS_UPDATE_FLAG) != 0
&& menu != null) {
updateMenuItemVisibility();
}
updateFlags = 0;
}
@@ -285,6 +298,9 @@ public class VideoDetailFragment
updateFlags |= RESOLUTIONS_MENU_UPDATE_FLAG;
} else if (key.equals(getString(R.string.show_play_with_kodi_key))) {
updateFlags |= TOOLBAR_ITEMS_UPDATE_FLAG;
} else if (key.equals(getString(R.string.show_comments_key))) {
showComments = sharedPreferences.getBoolean(key, true);
updateFlags |= COMMENTS_UPDATE_FLAG;
}
}
@@ -294,7 +310,6 @@ public class VideoDetailFragment
private static final String INFO_KEY = "info_key";
private static final String STACK_KEY = "stack_key";
private static final String WAS_RELATED_EXPANDED_KEY = "was_related_expanded_key";
@Override
public void onSaveInstanceState(Bundle outState) {
@@ -303,10 +318,6 @@ public class VideoDetailFragment
// Check if the next video label and video is visible,
// if it is, include the two elements in the next check
int nextCount = currentInfo != null && currentInfo.getNextVideo() != null ? 2 : 0;
if (relatedStreamsView != null
&& relatedStreamsView.getChildCount() > INITIAL_RELATED_VIDEOS + nextCount) {
outState.putSerializable(WAS_RELATED_EXPANDED_KEY, true);
}
if (!isLoading.get() && currentInfo != null && isVisible()) {
outState.putSerializable(INFO_KEY, currentInfo);
@@ -319,12 +330,11 @@ public class VideoDetailFragment
protected void onRestoreInstanceState(@NonNull Bundle savedState) {
super.onRestoreInstanceState(savedState);
wasRelatedStreamsExpanded = savedState.getBoolean(WAS_RELATED_EXPANDED_KEY, false);
Serializable serializable = savedState.getSerializable(INFO_KEY);
if (serializable instanceof StreamInfo) {
//noinspection unchecked
currentInfo = (StreamInfo) serializable;
InfoCache.getInstance().putInfo(serviceId, url, currentInfo);
InfoCache.getInstance().putInfo(serviceId, url, currentInfo, InfoItem.InfoType.STREAM);
}
serializable = savedState.getSerializable(STACK_KEY);
@@ -332,6 +342,7 @@ public class VideoDetailFragment
//noinspection unchecked
stack.addAll((Collection<? extends StackItem>) serializable);
}
}
/*//////////////////////////////////////////////////////////////////////////
@@ -356,32 +367,24 @@ public class VideoDetailFragment
}
break;
case R.id.detail_controls_download:
if (!PermissionHelper.checkStoragePermissions(activity)) {
return;
}
try {
DownloadDialog downloadDialog =
DownloadDialog.newInstance(currentInfo,
sortedStreamVideosList,
selectedVideoStream);
downloadDialog.show(activity.getSupportFragmentManager(), "downloadDialog");
} catch (Exception e) {
Toast.makeText(activity,
R.string.could_not_setup_download_menu,
Toast.LENGTH_LONG).show();
e.printStackTrace();
if (PermissionHelper.checkStoragePermissions(activity,
PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE)) {
this.openDownloadDialog();
}
break;
case R.id.detail_uploader_root_layout:
if (TextUtils.isEmpty(currentInfo.getUploaderUrl())) {
Log.w(TAG, "Can't open channel because we got no channel URL");
} else {
try {
NavigationHelper.openChannelFragment(
getFragmentManager(),
currentInfo.getServiceId(),
currentInfo.getUploaderUrl(),
currentInfo.getUploaderName());
} catch (Exception e) {
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
}
}
break;
case R.id.detail_thumbnail_root_layout:
@@ -395,9 +398,6 @@ public class VideoDetailFragment
case R.id.detail_title_root_layout:
toggleTitleAndDescription();
break;
case R.id.detail_related_streams_expand:
toggleExpandRelatedVideos(currentInfo);
break;
}
}
@@ -412,6 +412,9 @@ public class VideoDetailFragment
case R.id.detail_controls_popup:
openPopupPlayer(true);
break;
case R.id.detail_controls_download:
NavigationHelper.openDownloads(getActivity());
break;
}
return true;
@@ -429,32 +432,6 @@ public class VideoDetailFragment
}
}
private void toggleExpandRelatedVideos(StreamInfo info) {
if (DEBUG) Log.d(TAG, "toggleExpandRelatedVideos() called with: info = [" + info + "]");
if (!showRelatedStreams) return;
int nextCount = info.getNextVideo() != null ? 2 : 0;
int initialCount = INITIAL_RELATED_VIDEOS + nextCount;
if (relatedStreamsView.getChildCount() > initialCount) {
relatedStreamsView.removeViews(initialCount,
relatedStreamsView.getChildCount() - (initialCount));
relatedStreamExpandButton.setImageDrawable(ContextCompat.getDrawable(
activity, ThemeHelper.resolveResourceIdFromAttr(activity, R.attr.expand)));
return;
}
//Log.d(TAG, "toggleExpandRelatedVideos() called with: info = [" + info + "], from = [" + INITIAL_RELATED_VIDEOS + "]");
for (int i = INITIAL_RELATED_VIDEOS; i < info.getRelatedStreams().size(); i++) {
InfoItem item = info.getRelatedStreams().get(i);
//Log.d(TAG, "i = " + i);
relatedStreamsView.addView(infoItemBuilder.buildView(relatedStreamsView, item));
}
relatedStreamExpandButton.setImageDrawable(
ContextCompat.getDrawable(activity,
ThemeHelper.resolveResourceIdFromAttr(activity, R.attr.collapse)));
}
/*//////////////////////////////////////////////////////////////////////////
// Init
//////////////////////////////////////////////////////////////////////////*/
@@ -464,8 +441,6 @@ public class VideoDetailFragment
super.initViews(rootView, savedInstanceState);
spinnerToolbar = activity.findViewById(R.id.toolbar).findViewById(R.id.toolbar_spinner);
parallaxScrollRootView = rootView.findViewById(R.id.detail_main_content);
thumbnailBackgroundButton = rootView.findViewById(R.id.detail_thumbnail_root_layout);
thumbnailImageView = rootView.findViewById(R.id.detail_thumbnail_image_view);
thumbnailPlayButton = rootView.findViewById(R.id.detail_thumbnail_play_button);
@@ -501,30 +476,23 @@ public class VideoDetailFragment
uploaderTextView = rootView.findViewById(R.id.detail_uploader_text_view);
uploaderThumb = rootView.findViewById(R.id.detail_uploader_thumbnail_view);
relatedStreamRootLayout = rootView.findViewById(R.id.detail_related_streams_root_layout);
nextStreamTitle = rootView.findViewById(R.id.detail_next_stream_title);
relatedStreamsView = rootView.findViewById(R.id.detail_related_streams_view);
appBarLayout = rootView.findViewById(R.id.appbarlayout);
viewPager = rootView.findViewById(R.id.viewpager);
pageAdapter = new TabAdaptor(getChildFragmentManager());
viewPager.setAdapter(pageAdapter);
tabLayout = rootView.findViewById(R.id.tablayout);
tabLayout.setupWithViewPager(viewPager);
relatedStreamExpandButton = rootView.findViewById(R.id.detail_related_streams_expand);
relatedStreamsLayout = rootView.findViewById(R.id.relatedStreamsLayout);
infoItemBuilder = new InfoItemBuilder(activity);
setHeightThumbnail();
}
@Override
protected void initListeners() {
super.initListeners();
infoItemBuilder.setOnStreamSelectedListener(new OnClickGesture<StreamInfoItem>() {
@Override
public void selected(StreamInfoItem selectedItem) {
selectAndLoadVideo(selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());
}
@Override
public void held(StreamInfoItem selectedItem) {
showStreamDialog(selectedItem);
}
});
videoTitleRoot.setOnClickListener(this);
uploaderRootLayout.setOnClickListener(this);
@@ -533,7 +501,7 @@ public class VideoDetailFragment
detailControlsPopup.setOnClickListener(this);
detailControlsAddToPlaylist.setOnClickListener(this);
detailControlsDownload.setOnClickListener(this);
relatedStreamExpandButton.setOnClickListener(this);
detailControlsDownload.setOnLongClickListener(this);
detailControlsBackground.setLongClickable(true);
detailControlsPopup.setLongClickable(true);
@@ -549,7 +517,9 @@ public class VideoDetailFragment
final String[] commands = new String[]{
context.getResources().getString(R.string.enqueue_on_background),
context.getResources().getString(R.string.enqueue_on_popup)
context.getResources().getString(R.string.enqueue_on_popup),
context.getResources().getString(R.string.append_playlist),
context.getResources().getString(R.string.share)
};
final DialogInterface.OnClickListener actions = (DialogInterface dialogInterface, int i) -> {
@@ -560,6 +530,15 @@ public class VideoDetailFragment
case 1:
NavigationHelper.enqueueOnPopupPlayer(getActivity(), new SinglePlayQueue(item));
break;
case 2:
if (getFragmentManager() != null) {
PlaylistAppendDialog.fromStreamInfoItems(Collections.singletonList(item))
.show(getFragmentManager(), TAG);
}
break;
case 3:
shareUrl(item.getName(), item.getUrl());
break;
default:
break;
}
@@ -605,41 +584,6 @@ public class VideoDetailFragment
}
}
private void initRelatedVideos(StreamInfo info) {
if (relatedStreamsView.getChildCount() > 0) relatedStreamsView.removeAllViews();
if (info.getNextVideo() != null && showRelatedStreams) {
nextStreamTitle.setVisibility(View.VISIBLE);
relatedStreamsView.addView(
infoItemBuilder.buildView(relatedStreamsView, info.getNextVideo()));
relatedStreamsView.addView(getSeparatorView());
relatedStreamRootLayout.setVisibility(View.VISIBLE);
} else nextStreamTitle.setVisibility(View.GONE);
if (info.getRelatedStreams() != null
&& !info.getRelatedStreams().isEmpty() && showRelatedStreams) {
//long first = System.nanoTime(), each;
int to = info.getRelatedStreams().size() >= INITIAL_RELATED_VIDEOS
? INITIAL_RELATED_VIDEOS
: info.getRelatedStreams().size();
for (int i = 0; i < to; i++) {
InfoItem item = info.getRelatedStreams().get(i);
//each = System.nanoTime();
relatedStreamsView.addView(infoItemBuilder.buildView(relatedStreamsView, item));
//if (DEBUG) Log.d(TAG, "each took " + ((System.nanoTime() - each) / 1000000L) + "ms");
}
//if (DEBUG) Log.d(TAG, "Total time " + ((System.nanoTime() - first) / 1000000L) + "ms");
relatedStreamRootLayout.setVisibility(View.VISIBLE);
relatedStreamExpandButton.setVisibility(View.VISIBLE);
relatedStreamExpandButton.setImageDrawable(ContextCompat.getDrawable(
activity, ThemeHelper.resolveResourceIdFromAttr(activity, R.attr.expand)));
} else {
if (info.getNextVideo() == null) relatedStreamRootLayout.setVisibility(View.GONE);
relatedStreamExpandButton.setVisibility(View.GONE);
}
}
/*//////////////////////////////////////////////////////////////////////////
// Menu
@@ -673,7 +617,7 @@ public class VideoDetailFragment
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if(isLoading.get()) {
if (isLoading.get()) {
// if is still loading block menu
return true;
}
@@ -681,15 +625,15 @@ public class VideoDetailFragment
int id = item.getItemId();
switch (id) {
case R.id.menu_item_share: {
if(currentInfo != null) {
shareUrl(currentInfo.getName(), url);
} else {
shareUrl(url, url);
if (currentInfo != null) {
shareUrl(currentInfo.getName(), currentInfo.getOriginalUrl());
}
return true;
}
case R.id.menu_item_openInBrowser: {
openUrlInBrowser(url);
if (currentInfo != null) {
openUrlInBrowser(currentInfo.getOriginalUrl());
}
return true;
}
case R.id.action_play_with_kodi:
@@ -697,7 +641,7 @@ public class VideoDetailFragment
NavigationHelper.playWithKore(activity, Uri.parse(
url.replace("https", "http")));
} catch (Exception e) {
if(DEBUG) Log.i(TAG, "Failed to start kore", e);
if (DEBUG) Log.i(TAG, "Failed to start kore", e);
showInstallKoreDialog(activity);
}
return true;
@@ -711,7 +655,8 @@ public class VideoDetailFragment
builder.setMessage(R.string.kore_not_found)
.setPositiveButton(R.string.install, (DialogInterface dialog, int which) ->
NavigationHelper.installKore(context))
.setNegativeButton(R.string.cancel, (DialogInterface dialog, int which) -> {});
.setNegativeButton(R.string.cancel, (DialogInterface dialog, int which) -> {
});
builder.create().show();
}
@@ -722,27 +667,31 @@ public class VideoDetailFragment
private void setupActionBar(final StreamInfo info) {
if (DEBUG) Log.d(TAG, "setupActionBarHandler() called with: info = [" + info + "]");
sortedStreamVideosList = new ArrayList<>(ListHelper.getSortedStreamVideosList(
activity, info.getVideoStreams(), info.getVideoOnlyStreams(), false));
selectedVideoStream = ListHelper.getDefaultResolutionIndex(activity, sortedStreamVideosList);
boolean isExternalPlayerEnabled = PreferenceManager.getDefaultSharedPreferences(activity)
.getBoolean(activity.getString(R.string.use_external_video_player_key), false);
spinnerToolbar.setAdapter(new SpinnerToolbarAdapter(activity, sortedStreamVideosList,
isExternalPlayerEnabled));
spinnerToolbar.setSelection(selectedVideoStream);
sortedVideoStreams = ListHelper.getSortedStreamVideosList(
activity,
info.getVideoStreams(),
info.getVideoOnlyStreams(),
false);
selectedVideoStreamIndex = ListHelper.getDefaultResolutionIndex(activity, sortedVideoStreams);
final StreamItemAdapter<VideoStream, Stream> streamsAdapter =
new StreamItemAdapter<>(activity,
new StreamSizeWrapper<>(sortedVideoStreams, activity), isExternalPlayerEnabled);
spinnerToolbar.setAdapter(streamsAdapter);
spinnerToolbar.setSelection(selectedVideoStreamIndex);
spinnerToolbar.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
selectedVideoStream = position;
selectedVideoStreamIndex = position;
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
/*//////////////////////////////////////////////////////////////////////////
@@ -753,19 +702,19 @@ public class VideoDetailFragment
* Stack that contains the "navigation history".<br>
* The peek is the current video.
*/
protected LinkedList<StackItem> stack = new LinkedList<>();
public void clearHistory() {
stack.clear();
}
protected final LinkedList<StackItem> stack = new LinkedList<>();
public void pushToStack(int serviceId, String videoUrl, String name) {
if (DEBUG) {
Log.d(TAG, "pushToStack() called with: serviceId = [" + serviceId + "], videoUrl = [" + videoUrl + "], name = [" + name + "]");
Log.d(TAG, "pushToStack() called with: serviceId = ["
+ serviceId + "], videoUrl = [" + videoUrl + "], name = [" + name + "]");
}
if (stack.size() > 0 && stack.peek().getServiceId() == serviceId && stack.peek().getUrl().equals(videoUrl)) {
Log.d(TAG, "pushToStack() called with: serviceId == peek.serviceId = [" + serviceId + "], videoUrl == peek.getUrl = [" + videoUrl + "]");
if (stack.size() > 0
&& stack.peek().getServiceId() == serviceId
&& stack.peek().getUrl().equals(videoUrl)) {
Log.d(TAG, "pushToStack() called with: serviceId == peek.serviceId = ["
+ serviceId + "], videoUrl == peek.getUrl = [" + videoUrl + "]");
return;
} else {
Log.d(TAG, "pushToStack() wasn't equal");
@@ -796,7 +745,11 @@ public class VideoDetailFragment
// Get stack item from the new top
StackItem peek = stack.peek();
selectAndLoadVideo(peek.getServiceId(), peek.getUrl(), !TextUtils.isEmpty(peek.getTitle()) ? peek.getTitle() : "");
selectAndLoadVideo(peek.getServiceId(),
peek.getUrl(),
!TextUtils.isEmpty(peek.getTitle())
? peek.getTitle()
: "");
return true;
}
@@ -816,28 +769,22 @@ public class VideoDetailFragment
}
public void prepareAndHandleInfo(final StreamInfo info, boolean scrollToTop) {
if (DEBUG) Log.d(TAG, "prepareAndHandleInfo() called with: info = [" + info + "], scrollToTop = [" + scrollToTop + "]");
if (DEBUG) Log.d(TAG, "prepareAndHandleInfo() called with: info = ["
+ info + "], scrollToTop = [" + scrollToTop + "]");
setInitialData(info.getServiceId(), info.getUrl(), info.getName());
pushToStack(serviceId, url, name);
showLoading();
initTabs();
Log.d(TAG, "prepareAndHandleInfo() called parallaxScrollRootView.getScrollY(): "
+ parallaxScrollRootView.getScrollY());
final boolean greaterThanThreshold = parallaxScrollRootView.getScrollY() > (int)
(getResources().getDisplayMetrics().heightPixels * .1f);
if (scrollToTop) appBarLayout.setExpanded(true, true);
handleResult(info);
showContent();
if (scrollToTop) parallaxScrollRootView.smoothScrollTo(0, 0);
animateView(contentRootLayoutHiding,
false,
greaterThanThreshold ? 250 : 0, 0, () -> {
handleResult(info);
showContentWithAnimation(120, 0, .01f);
});
}
protected void prepareAndLoadInfo() {
parallaxScrollRootView.smoothScrollTo(0, 0);
appBarLayout.setExpanded(true, true);
pushToStack(serviceId, url, name);
startLoading(false);
}
@@ -846,6 +793,7 @@ public class VideoDetailFragment
public void startLoading(boolean forceLoad) {
super.startLoading(forceLoad);
initTabs();
currentInfo = null;
if (currentWorker != null) currentWorker.dispose();
@@ -855,12 +803,45 @@ public class VideoDetailFragment
.subscribe((@NonNull StreamInfo result) -> {
isLoading.set(false);
currentInfo = result;
showContentWithAnimation(120, 0, 0);
handleResult(result);
showContent();
}, (@NonNull Throwable throwable) -> {
isLoading.set(false);
onError(throwable);
});
}
private void initTabs() {
pageAdapter.clearAllItems();
if(shouldShowComments()){
pageAdapter.addFragment(CommentsFragment.getInstance(serviceId, url, name), COMMENTS_TAB_TAG);
}
if(showRelatedStreams && null == relatedStreamsLayout){
//temp empty fragment. will be updated in handleResult
pageAdapter.addFragment(new Fragment(), RELATED_TAB_TAG);
}
pageAdapter.notifyDataSetUpdate();
if(pageAdapter.getCount() < 2){
tabLayout.setVisibility(View.GONE);
}else{
tabLayout.setVisibility(View.VISIBLE);
}
}
private boolean shouldShowComments() {
try {
return showComments && NewPipe.getService(serviceId)
.getServiceInfo()
.getMediaCapabilities()
.contains(COMMENTS);
} catch (ExtractionException e) {
return false;
}
}
/*//////////////////////////////////////////////////////////////////////////
@@ -877,10 +858,7 @@ public class VideoDetailFragment
if (!useExternalAudioPlayer && android.os.Build.VERSION.SDK_INT >= 16) {
openNormalBackgroundPlayer(append);
} else {
NavigationHelper.playOnExternalPlayer(activity,
currentInfo.getName(),
currentInfo.getUploaderName(),
audioStream);
startOnExternalPlayer(activity, currentInfo, audioStream);
}
}
@@ -907,12 +885,9 @@ public class VideoDetailFragment
if (PreferenceManager.getDefaultSharedPreferences(activity)
.getBoolean(this.getString(R.string.use_external_video_player_key), false)) {
NavigationHelper.playOnExternalPlayer(activity,
currentInfo.getName(),
currentInfo.getUploaderName(),
selectedVideoStream);
startOnExternalPlayer(activity, currentInfo, selectedVideoStream);
} else {
openNormalPlayer(selectedVideoStream);
openNormalPlayer();
}
}
@@ -925,24 +900,13 @@ public class VideoDetailFragment
}
}
private void openNormalPlayer(VideoStream selectedVideoStream) {
private void openNormalPlayer() {
Intent mIntent;
boolean useOldPlayer = PlayerHelper.isUsingOldPlayer(activity) || (Build.VERSION.SDK_INT < 16);
if (!useOldPlayer) {
// ExoPlayer
final PlayQueue playQueue = new SinglePlayQueue(currentInfo);
mIntent = NavigationHelper.getPlayerIntent(activity,
MainVideoPlayer.class,
playQueue,
getSelectedVideoStream().getResolution());
} else {
// Internal Player
mIntent = new Intent(activity, PlayVideoActivity.class)
.putExtra(PlayVideoActivity.VIDEO_TITLE, currentInfo.getName())
.putExtra(PlayVideoActivity.STREAM_URL, selectedVideoStream.getUrl())
.putExtra(PlayVideoActivity.VIDEO_URL, currentInfo.getUrl())
.putExtra(PlayVideoActivity.START_POSITION, currentInfo.getStartPosition());
}
final PlayQueue playQueue = new SinglePlayQueue(currentInfo);
mIntent = NavigationHelper.getPlayerIntent(activity,
MainVideoPlayer.class,
playQueue,
getSelectedVideoStream().getResolution());
startActivity(mIntent);
}
@@ -954,8 +918,23 @@ public class VideoDetailFragment
this.autoPlayEnabled = autoplay;
}
private void startOnExternalPlayer(@NonNull final Context context,
@NonNull final StreamInfo info,
@NonNull final Stream selectedStream) {
NavigationHelper.playOnExternalPlayer(context, currentInfo.getName(),
currentInfo.getUploaderName(), selectedStream);
final HistoryRecordManager recordManager = new HistoryRecordManager(requireContext());
disposables.add(recordManager.onViewed(info).onErrorComplete()
.subscribe(
ignored -> {/* successful */},
error -> Log.e(TAG, "Register view failure: ", error)
));
}
@Nullable
private VideoStream getSelectedVideoStream() {
return sortedStreamVideosList.get(selectedVideoStream);
return sortedVideoStreams != null ? sortedVideoStreams.get(selectedVideoStreamIndex) : null;
}
private void prepareDescription(final String descriptionHtml) {
@@ -982,24 +961,6 @@ public class VideoDetailFragment
}));
}
private View getSeparatorView() {
View separator = new View(activity);
LinearLayout.LayoutParams params =
new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1);
int m8 = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 8, getResources().getDisplayMetrics());
int m5 = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 5, getResources().getDisplayMetrics());
params.setMargins(m8, m5, m8, m5);
separator.setLayoutParams(params);
TypedValue typedValue = new TypedValue();
activity.getTheme().resolveAttribute(R.attr.separator_color, typedValue, true);
separator.setBackgroundColor(typedValue.data);
return separator;
}
private void setHeightThumbnail() {
final DisplayMetrics metrics = getResources().getDisplayMetrics();
boolean isPortrait = metrics.heightPixels > metrics.widthPixels;
@@ -1011,49 +972,8 @@ public class VideoDetailFragment
thumbnailImageView.setMinimumHeight(height);
}
private void showContentWithAnimation(long duration,
long delay,
@FloatRange(from = 0.0f, to = 1.0f) float translationPercent) {
int translationY = (int) (getResources().getDisplayMetrics().heightPixels *
(translationPercent > 0.0f ? translationPercent : .06f));
contentRootLayoutHiding.animate().setListener(null).cancel();
contentRootLayoutHiding.setAlpha(0f);
contentRootLayoutHiding.setTranslationY(translationY);
contentRootLayoutHiding.setVisibility(View.VISIBLE);
contentRootLayoutHiding.animate()
.alpha(1f)
.translationY(0)
.setStartDelay(delay)
.setDuration(duration)
.setInterpolator(new FastOutSlowInInterpolator())
.start();
uploaderRootLayout.animate().setListener(null).cancel();
uploaderRootLayout.setAlpha(0f);
uploaderRootLayout.setTranslationY(translationY);
uploaderRootLayout.setVisibility(View.VISIBLE);
uploaderRootLayout.animate()
.alpha(1f)
.translationY(0)
.setStartDelay((long) (duration * .5f) + delay)
.setDuration(duration)
.setInterpolator(new FastOutSlowInInterpolator())
.start();
if (showRelatedStreams) {
relatedStreamRootLayout.animate().setListener(null).cancel();
relatedStreamRootLayout.setAlpha(0f);
relatedStreamRootLayout.setTranslationY(translationY);
relatedStreamRootLayout.setVisibility(View.VISIBLE);
relatedStreamRootLayout.animate()
.alpha(1f)
.translationY(0)
.setStartDelay((long) (duration * .8f) + delay)
.setDuration(duration)
.setInterpolator(new FastOutSlowInInterpolator())
.start();
}
private void showContent() {
AnimationUtils.slideUp(contentRootLayoutHiding,120, 96, 0.06f);
}
protected void setInitialData(int serviceId, String url, String name) {
@@ -1088,7 +1008,7 @@ public class VideoDetailFragment
public void showLoading() {
super.showLoading();
animateView(contentRootLayoutHiding, false, 200);
contentRootLayoutHiding.setVisibility(View.INVISIBLE);
animateView(spinnerToolbar, false, 200);
animateView(thumbnailPlayButton, false, 50);
animateView(detailDurationView, false, 100);
@@ -1102,6 +1022,14 @@ public class VideoDetailFragment
videoTitleToggleArrow.setVisibility(View.GONE);
videoTitleRoot.setClickable(false);
if(relatedStreamsLayout != null){
if(showRelatedStreams){
relatedStreamsLayout.setVisibility(View.INVISIBLE);
}else{
relatedStreamsLayout.setVisibility(View.GONE);
}
}
imageLoader.cancelDisplayTask(thumbnailImageView);
imageLoader.cancelDisplayTask(uploaderThumb);
thumbnailImageView.setImageBitmap(null);
@@ -1112,8 +1040,21 @@ public class VideoDetailFragment
public void handleResult(@NonNull StreamInfo info) {
super.handleResult(info);
setInitialData(info.getServiceId(), info.getUrl(), info.getName());
pushToStack(serviceId, url, name);
setInitialData(info.getServiceId(), info.getOriginalUrl(), info.getName());
if(showRelatedStreams){
if(null == relatedStreamsLayout){ //phone
pageAdapter.updateItem(RELATED_TAB_TAG, RelatedVideosFragment.getInstance(currentInfo));
pageAdapter.notifyDataSetUpdate();
}else{ //tablet
getChildFragmentManager().beginTransaction()
.replace(R.id.relatedStreamsLayout, RelatedVideosFragment.getInstance(currentInfo))
.commitNow();
relatedStreamsLayout.setVisibility(View.VISIBLE);
}
}
//pushToStack(serviceId, url, name);
animateView(thumbnailPlayButton, true, 200);
videoTitleTextView.setText(name);
@@ -1164,20 +1105,22 @@ public class VideoDetailFragment
if (info.getDuration() > 0) {
detailDurationView.setText(Localization.getDurationString(info.getDuration()));
detailDurationView.setBackgroundColor(ContextCompat.getColor(activity, R.color.duration_background_color));
detailDurationView.setBackgroundColor(
ContextCompat.getColor(activity, R.color.duration_background_color));
animateView(detailDurationView, true, 100);
} else if (info.getStreamType() == StreamType.LIVE_STREAM) {
detailDurationView.setText(R.string.duration_live);
detailDurationView.setBackgroundColor(ContextCompat.getColor(activity, R.color.live_duration_background_color));
detailDurationView.setBackgroundColor(
ContextCompat.getColor(activity, R.color.live_duration_background_color));
animateView(detailDurationView, true, 100);
} else {
detailDurationView.setVisibility(View.GONE);
}
videoDescriptionView.setVisibility(View.GONE);
videoTitleRoot.setClickable(true);
videoTitleToggleArrow.setVisibility(View.VISIBLE);
videoTitleToggleArrow.setImageResource(R.drawable.arrow_down);
videoDescriptionView.setVisibility(View.GONE);
videoDescriptionRootLayout.setVisibility(View.GONE);
if (!TextUtils.isEmpty(info.getUploadDate())) {
videoUploadDateView.setText(Localization.localizeDate(activity, info.getUploadDate()));
@@ -1187,12 +1130,9 @@ public class VideoDetailFragment
animateView(spinnerToolbar, true, 500);
setupActionBar(info);
initThumbnailViews(info);
initRelatedVideos(info);
if (wasRelatedStreamsExpanded) {
toggleExpandRelatedVideos(currentInfo);
wasRelatedStreamsExpanded = false;
}
setTitleToUrl(info.getServiceId(), info.getUrl(), info.getName());
setTitleToUrl(info.getServiceId(), info.getOriginalUrl(), info.getName());
if (!info.getErrors().isEmpty()) {
showSnackBarError(info.getErrors(),
@@ -1209,10 +1149,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);
@@ -1226,6 +1166,32 @@ public class VideoDetailFragment
}
}
public void openDownloadDialog() {
try {
DownloadDialog downloadDialog = DownloadDialog.newInstance(currentInfo);
downloadDialog.setVideoStreams(sortedVideoStreams);
downloadDialog.setAudioStreams(currentInfo.getAudioStreams());
downloadDialog.setSelectedVideoStream(selectedVideoStreamIndex);
downloadDialog.setSubtitleStreams(currentInfo.getSubtitles());
downloadDialog.show(activity.getSupportFragmentManager(), "downloadDialog");
} catch (Exception e) {
ErrorActivity.ErrorInfo info = ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
ServiceList.all()
.get(currentInfo
.getServiceId())
.getServiceInfo()
.getName(), "",
R.string.could_not_setup_download_menu);
ErrorActivity.reportError(getActivity(),
e,
getActivity().getClass(),
getActivity().findViewById(android.R.id.content), info);
}
}
/*//////////////////////////////////////////////////////////////////////////
// Stream Results
//////////////////////////////////////////////////////////////////////////*/
@@ -1264,4 +1230,4 @@ public class VideoDetailFragment
showError(getString(R.string.blocked_by_gema), false, R.drawable.gruese_die_gema);
}
}
}

View File

@@ -3,9 +3,15 @@ package org.schabi.newpipe.fragments.list;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
@@ -16,14 +22,16 @@ import android.view.View;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.fragments.BaseStateFragment;
import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
import org.schabi.newpipe.fragments.local.dialog.PlaylistAppendDialog;
import org.schabi.newpipe.info_list.InfoItemDialog;
import org.schabi.newpipe.info_list.InfoListAdapter;
import org.schabi.newpipe.playlist.SinglePlayQueue;
import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.OnClickGesture;
import org.schabi.newpipe.util.StateSaver;
@@ -34,7 +42,7 @@ import java.util.Queue;
import static org.schabi.newpipe.util.AnimationUtils.animateView;
public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implements ListViewContract<I, N>, StateSaver.WriteRead {
public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implements ListViewContract<I, N>, StateSaver.WriteRead, SharedPreferences.OnSharedPreferenceChangeListener {
/*//////////////////////////////////////////////////////////////////////////
// Views
@@ -42,6 +50,9 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
protected InfoListAdapter infoListAdapter;
protected RecyclerView itemsList;
private int updateFlags = 0;
private static final int LIST_MODE_UPDATE_FLAG = 0x32;
/*//////////////////////////////////////////////////////////////////////////
// LifeCycle
@@ -57,12 +68,31 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
PreferenceManager.getDefaultSharedPreferences(activity)
.registerOnSharedPreferenceChangeListener(this);
}
@Override
public void onDestroy() {
super.onDestroy();
StateSaver.onDestroy(savedState);
PreferenceManager.getDefaultSharedPreferences(activity)
.unregisterOnSharedPreferenceChangeListener(this);
}
@Override
public void onResume() {
super.onResume();
if (updateFlags != 0) {
if ((updateFlags & LIST_MODE_UPDATE_FLAG) != 0) {
final boolean useGrid = isGridLayout();
itemsList.setLayoutManager(useGrid ? getGridLayoutManager() : getListLayoutManager());
infoListAdapter.setGridItemVariants(useGrid);
infoListAdapter.notifyDataSetChanged();
}
updateFlags = 0;
}
}
/*//////////////////////////////////////////////////////////////////////////
@@ -117,13 +147,25 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
return new LinearLayoutManager(activity);
}
protected RecyclerView.LayoutManager getGridLayoutManager() {
final Resources resources = activity.getResources();
int width = resources.getDimensionPixelSize(R.dimen.video_item_grid_thumbnail_image_width);
width += (24 * resources.getDisplayMetrics().density);
final int spanCount = (int) Math.floor(resources.getDisplayMetrics().widthPixels / (double)width);
final GridLayoutManager lm = new GridLayoutManager(activity, spanCount);
lm.setSpanSizeLookup(infoListAdapter.getSpanSizeLookup(spanCount));
return lm;
}
@Override
protected void initViews(View rootView, Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState);
final boolean useGrid = isGridLayout();
itemsList = rootView.findViewById(R.id.items_list);
itemsList.setLayoutManager(getListLayoutManager());
itemsList.setLayoutManager(useGrid ? getGridLayoutManager() : getListLayoutManager());
infoListAdapter.setGridItemVariants(useGrid);
infoListAdapter.setFooter(getListFooter());
infoListAdapter.setHeader(getListHeader());
@@ -140,9 +182,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
infoListAdapter.setOnStreamSelectedListener(new OnClickGesture<StreamInfoItem>() {
@Override
public void selected(StreamInfoItem selectedItem) {
onItemSelected(selectedItem);
NavigationHelper.openVideoDetailFragment(useAsFrontPage ? getParentFragment().getFragmentManager() : getFragmentManager(),
selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());
onStreamSelected(selectedItem);
}
@Override
@@ -154,18 +194,37 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
infoListAdapter.setOnChannelSelectedListener(new OnClickGesture<ChannelInfoItem>() {
@Override
public void selected(ChannelInfoItem selectedItem) {
onItemSelected(selectedItem);
NavigationHelper.openChannelFragment(useAsFrontPage ? getParentFragment().getFragmentManager() : getFragmentManager(),
selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());
try {
onItemSelected(selectedItem);
NavigationHelper.openChannelFragment(getFM(),
selectedItem.getServiceId(),
selectedItem.getUrl(),
selectedItem.getName());
} catch (Exception e) {
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
}
}
});
infoListAdapter.setOnPlaylistSelectedListener(new OnClickGesture<PlaylistInfoItem>() {
@Override
public void selected(PlaylistInfoItem selectedItem) {
try {
onItemSelected(selectedItem);
NavigationHelper.openPlaylistFragment(getFM(),
selectedItem.getServiceId(),
selectedItem.getUrl(),
selectedItem.getName());
} catch (Exception e) {
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
}
}
});
infoListAdapter.setOnCommentsSelectedListener(new OnClickGesture<CommentsInfoItem>() {
@Override
public void selected(CommentsInfoItem selectedItem) {
onItemSelected(selectedItem);
NavigationHelper.openPlaylistFragment(useAsFrontPage ? getParentFragment().getFragmentManager() : getFragmentManager(),
selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());
}
});
@@ -178,6 +237,12 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
});
}
private void onStreamSelected(StreamInfoItem selectedItem) {
onItemSelected(selectedItem);
NavigationHelper.openVideoDetailFragment(getFM(),
selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());
}
protected void onScrollToBottom() {
if (hasMoreItems() && !isLoading.get()) {
loadMoreItems();
@@ -192,7 +257,8 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
final String[] commands = new String[]{
context.getResources().getString(R.string.enqueue_on_background),
context.getResources().getString(R.string.enqueue_on_popup),
context.getResources().getString(R.string.append_playlist)
context.getResources().getString(R.string.append_playlist),
context.getResources().getString(R.string.share)
};
final DialogInterface.OnClickListener actions = (dialogInterface, i) -> {
@@ -209,6 +275,9 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
.show(getFragmentManager(), TAG);
}
break;
case 3:
shareUrl(item.getName(), item.getUrl());
break;
default:
break;
}
@@ -216,6 +285,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
new InfoItemDialog(getActivity(), item, commands, actions).show();
}
/*//////////////////////////////////////////////////////////////////////////
// Menu
//////////////////////////////////////////////////////////////////////////*/
@@ -285,4 +355,22 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
public void handleNextItems(N result) {
isLoading.set(false);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals(getString(R.string.list_view_mode_key))) {
updateFlags |= LIST_MODE_UPDATE_FLAG;
}
}
protected boolean isGridLayout() {
final String list_mode = PreferenceManager.getDefaultSharedPreferences(activity).getString(getString(R.string.list_view_mode_key), getString(R.string.list_view_mode_value));
if ("auto".equals(list_mode)) {
final Configuration configuration = getResources().getConfiguration();
return configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
&& configuration.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE);
} else {
return "grid".equals(list_mode);
}
}
}

View File

@@ -16,7 +16,6 @@ import icepick.State;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;
public abstract class BaseListInfoFragment<I extends ListInfo>
@@ -166,7 +165,6 @@ public abstract class BaseListInfoFragment<I extends ListInfo>
public void handleResult(@NonNull I result) {
super.handleResult(result);
url = result.getUrl();
name = result.getName();
setTitle(name);

View File

@@ -32,16 +32,15 @@ import org.schabi.newpipe.extractor.ListExtractor;
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.Stream;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
import org.schabi.newpipe.info_list.InfoItemDialog;
import org.schabi.newpipe.playlist.ChannelPlayQueue;
import org.schabi.newpipe.playlist.PlayQueue;
import org.schabi.newpipe.playlist.SinglePlayQueue;
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.subscription.SubscriptionService;
import org.schabi.newpipe.util.AnimationUtils;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.ImageDisplayConstants;
@@ -49,6 +48,7 @@ import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.NavigationHelper;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -68,7 +68,7 @@ import static org.schabi.newpipe.util.AnimationUtils.animateView;
public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
private CompositeDisposable disposables = new CompositeDisposable();
private final CompositeDisposable disposables = new CompositeDisposable();
private Disposable subscribeButtonMonitor;
private SubscriptionService subscriptionService;
@@ -161,31 +161,39 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
context.getResources().getString(R.string.start_here_on_main),
context.getResources().getString(R.string.start_here_on_background),
context.getResources().getString(R.string.start_here_on_popup),
context.getResources().getString(R.string.append_playlist),
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;
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;
}
};
@@ -225,10 +233,10 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
openRssFeed();
break;
case R.id.menu_item_openInBrowser:
openUrlInBrowser(url);
openUrlInBrowser(currentInfo.getOriginalUrl());
break;
case R.id.menu_item_share:
shareUrl(name, url);
shareUrl(name, currentInfo.getOriginalUrl());
break;
default:
return super.onOptionsItemSelected(item);
@@ -243,12 +251,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()
@@ -264,50 +272,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())
@@ -316,19 +312,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)
@@ -340,25 +333,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));
}
};
}
@@ -425,10 +418,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()));
@@ -476,8 +471,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);
}
}
@@ -490,7 +488,11 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
if (super.onError(exception)) return true;
int errorId = exception instanceof ExtractionException ? R.string.parsing_error : R.string.general_error;
onUnrecoverableError(exception, UserAction.REQUESTED_CHANNEL, NewPipe.getNameOfService(serviceId), url, errorId);
onUnrecoverableError(exception,
UserAction.REQUESTED_CHANNEL,
NewPipe.getNameOfService(serviceId),
url,
errorId);
return true;
}
@@ -501,6 +503,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

@@ -0,0 +1,149 @@
package org.schabi.newpipe.fragments.list.comments;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.comments.CommentsInfo;
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.AnimationUtils;
import org.schabi.newpipe.util.ExtractorHelper;
import io.reactivex.Single;
import io.reactivex.disposables.CompositeDisposable;
public class CommentsFragment extends BaseListInfoFragment<CommentsInfo> {
private CompositeDisposable disposables = new CompositeDisposable();
/*//////////////////////////////////////////////////////////////////////////
// Views
//////////////////////////////////////////////////////////////////////////*/
private boolean mIsVisibleToUser = false;
public static CommentsFragment getInstance(int serviceId, String url, String name) {
CommentsFragment instance = new CommentsFragment();
instance.setInitialData(serviceId, url, name);
return instance;
}
/*//////////////////////////////////////////////////////////////////////////
// LifeCycle
//////////////////////////////////////////////////////////////////////////*/
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
mIsVisibleToUser = isVisibleToUser;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_comments, container, false);
}
@Override
public void onDestroy() {
super.onDestroy();
if (disposables != null) disposables.clear();
}
/*//////////////////////////////////////////////////////////////////////////
// Load and handle
//////////////////////////////////////////////////////////////////////////*/
@Override
protected Single<ListExtractor.InfoItemsPage> loadMoreItemsLogic() {
return ExtractorHelper.getMoreCommentItems(serviceId, currentInfo, currentNextPageUrl);
}
@Override
protected Single<CommentsInfo> loadResult(boolean forceLoad) {
return ExtractorHelper.getCommentsInfo(serviceId, url, forceLoad);
}
/*//////////////////////////////////////////////////////////////////////////
// Contract
//////////////////////////////////////////////////////////////////////////*/
@Override
public void showLoading() {
super.showLoading();
}
@Override
public void handleResult(@NonNull CommentsInfo result) {
super.handleResult(result);
AnimationUtils.slideUp(getView(),120, 96, 0.06f);
if (!result.getErrors().isEmpty()) {
showSnackBarError(result.getErrors(), UserAction.REQUESTED_COMMENTS, NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
}
if (disposables != null) disposables.clear();
}
@Override
public void handleNextItems(ListExtractor.InfoItemsPage result) {
super.handleNextItems(result);
if (!result.getErrors().isEmpty()) {
showSnackBarError(result.getErrors(),
UserAction.REQUESTED_COMMENTS,
NewPipe.getNameOfService(serviceId),
"Get next page of: " + url,
R.string.general_error);
}
}
/*//////////////////////////////////////////////////////////////////////////
// OnError
//////////////////////////////////////////////////////////////////////////*/
@Override
protected boolean onError(Throwable exception) {
if (super.onError(exception)) return true;
hideLoading();
showSnackBarError(exception, UserAction.REQUESTED_COMMENTS, NewPipe.getNameOfService(serviceId), url, R.string.error_unable_to_load_comments);
return true;
}
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
@Override
public void setTitle(String title) {
return;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
return;
}
@Override
protected boolean isGridLayout() {
return false;
}
}

View File

@@ -5,28 +5,23 @@ import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.ActionBar;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.UrlIdHandler;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.kiosk.KioskInfo;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory;
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
import org.schabi.newpipe.info_list.InfoItemBuilder;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.KioskTranslator;
import org.schabi.newpipe.util.NavigationHelper;
import icepick.State;
import io.reactivex.Single;
@@ -59,6 +54,7 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
protected String kioskId = "";
protected String kioskTranslatedName;
/*//////////////////////////////////////////////////////////////////////////
// Views
//////////////////////////////////////////////////////////////////////////*/
@@ -74,11 +70,10 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
throws ExtractionException {
KioskFragment instance = new KioskFragment();
StreamingService service = NewPipe.getService(serviceId);
UrlIdHandler kioskTypeUrlIdHandler = service.getKioskList()
.getUrlIdHandlerByType(kioskId);
ListLinkHandlerFactory kioskLinkHandlerFactory = service.getKioskList()
.getListLinkHandlerFactoryByType(kioskId);
instance.setInitialData(serviceId,
kioskTypeUrlIdHandler.getUrl(kioskId),
kioskId);
kioskLinkHandlerFactory.fromId(kioskId).getUrl(), kioskId);
instance.kioskId = kioskId;
return instance;
}
@@ -133,20 +128,16 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
@Override
public Single<KioskInfo> loadResult(boolean forceReload) {
String contentCountry = PreferenceManager
.getDefaultSharedPreferences(activity)
.getString(getString(R.string.content_country_key),
getString(R.string.default_country_value));
return ExtractorHelper.getKioskInfo(serviceId, url, contentCountry, forceReload);
return ExtractorHelper.getKioskInfo(serviceId,
url,
forceReload);
}
@Override
public Single<ListExtractor.InfoItemsPage> loadMoreItemsLogic() {
String contentCountry = PreferenceManager
.getDefaultSharedPreferences(activity)
.getString(getString(R.string.content_country_key),
getString(R.string.default_country_value));
return ExtractorHelper.getMoreKioskItems(serviceId, url, currentNextPageUrl, contentCountry);
return ExtractorHelper.getMoreKioskItems(serviceId,
url,
currentNextPageUrl);
}
/*//////////////////////////////////////////////////////////////////////////
@@ -164,7 +155,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

@@ -6,6 +6,7 @@ import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
@@ -27,14 +28,14 @@ import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
import org.schabi.newpipe.fragments.local.RemotePlaylistManager;
import org.schabi.newpipe.info_list.InfoItemDialog;
import org.schabi.newpipe.playlist.PlayQueue;
import org.schabi.newpipe.playlist.PlaylistPlayQueue;
import org.schabi.newpipe.playlist.SinglePlayQueue;
import org.schabi.newpipe.local.playlist.RemotePlaylistManager;
import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue;
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.ImageDisplayConstants;
@@ -45,6 +46,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import io.reactivex.Flowable;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
@@ -94,7 +96,8 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
super.onCreate(savedInstanceState);
disposables = new CompositeDisposable();
isBookmarkButtonReady = new AtomicBoolean(false);
remotePlaylistManager = new RemotePlaylistManager(NewPipeDatabase.getInstance(getContext()));
remotePlaylistManager = new RemotePlaylistManager(NewPipeDatabase.getInstance(
requireContext()));
}
@Override
@@ -143,6 +146,7 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
context.getResources().getString(R.string.start_here_on_main),
context.getResources().getString(R.string.start_here_on_background),
context.getResources().getString(R.string.start_here_on_popup),
context.getResources().getString(R.string.share)
};
final DialogInterface.OnClickListener actions = (dialogInterface, i) -> {
@@ -163,6 +167,9 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
case 4:
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index));
break;
case 5:
shareUrl(item.getName(), item.getUrl());
break;
default:
break;
}
@@ -262,11 +269,16 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
if (!TextUtils.isEmpty(result.getUploaderName())) {
headerUploaderName.setText(result.getUploaderName());
if (!TextUtils.isEmpty(result.getUploaderUrl())) {
headerUploaderLayout.setOnClickListener(v ->
headerUploaderLayout.setOnClickListener(v -> {
try {
NavigationHelper.openChannelFragment(getFragmentManager(),
result.getServiceId(), result.getUploaderUrl(),
result.getUploaderName())
);
result.getServiceId(),
result.getUploaderUrl(),
result.getUploaderName());
} catch (Exception e) {
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
}
});
}
}
@@ -282,14 +294,11 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
}
remotePlaylistManager.getPlaylist(result)
.flatMap(lists -> getUpdateProcessor(lists, result), (lists, id) -> lists)
.onBackpressureLatest()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getPlaylistBookmarkSubscriber());
remotePlaylistManager.onUpdate(result)
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(integer -> {/* Do nothing*/}, this::onError);
headerPlayAllButton.setOnClickListener(view ->
NavigationHelper.playOnMainPlayer(activity, getPlayQueue()));
headerPopupButton.setOnClickListener(view ->
@@ -337,7 +346,11 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
if (super.onError(exception)) return true;
int errorId = exception instanceof ExtractionException ? R.string.parsing_error : R.string.general_error;
onUnrecoverableError(exception, UserAction.REQUESTED_PLAYLIST, NewPipe.getNameOfService(serviceId), url, errorId);
onUnrecoverableError(exception,
UserAction.REQUESTED_PLAYLIST,
NewPipe.getNameOfService(serviceId),
url,
errorId);
return true;
}
@@ -345,6 +358,17 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
// Utils
//////////////////////////////////////////////////////////////////////////*/
private Flowable<Integer> getUpdateProcessor(@NonNull List<PlaylistRemoteEntity> playlists,
@NonNull PlaylistInfo result) {
final Flowable<Integer> noItemToUpdate = Flowable.just(/*noItemToUpdate=*/-1);
if (playlists.isEmpty()) return noItemToUpdate;
final PlaylistRemoteEntity playlistEntity = playlists.get(0);
if (playlistEntity.isIdenticalTo(result)) return noItemToUpdate;
return remotePlaylistManager.onUpdate(playlists.get(0).getUid(), result).toFlowable();
}
private Subscriber<List<PlaylistRemoteEntity>> getPlaylistBookmarkSubscriber() {
return new Subscriber<List<PlaylistRemoteEntity>>() {
@Override
@@ -417,4 +441,4 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
playlistBookmarkButton.setIcon(ThemeHelper.resolveResourceIdFromAttr(activity, iconAttr));
playlistBookmarkButton.setTitle(titleRes);
}
}
}

View File

@@ -37,26 +37,31 @@ import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.search.SearchEngine;
import org.schabi.newpipe.extractor.search.SearchResult;
import org.schabi.newpipe.extractor.search.SearchExtractor;
import org.schabi.newpipe.extractor.search.SearchInfo;
import org.schabi.newpipe.util.FireTvUtils;
import org.schabi.newpipe.fragments.BackPressable;
import org.schabi.newpipe.fragments.list.BaseListFragment;
import org.schabi.newpipe.history.HistoryRecordManager;
import org.schabi.newpipe.local.history.HistoryRecordManager;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.AnimationUtils;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.LayoutManagerSmoothScroller;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.ServiceHelper;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import icepick.State;
@@ -65,14 +70,15 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;
import io.reactivex.subjects.PublishSubject;
import static java.util.Arrays.asList;
import static org.schabi.newpipe.util.AnimationUtils.animateView;
public class SearchFragment
extends BaseListFragment<SearchResult, ListExtractor.InfoItemsPage>
extends BaseListFragment<SearchInfo, ListExtractor.InfoItemsPage>
implements BackPressable {
/*//////////////////////////////////////////////////////////////////////////
@@ -92,27 +98,41 @@ public class SearchFragment
@State
protected int filterItemCheckedId = -1;
private SearchEngine.Filter filter = SearchEngine.Filter.ANY;
@State
protected int serviceId = Constants.NO_SERVICE_ID;
// this three represet the current search query
@State
protected String searchQuery;
protected String searchString;
/**
* No content filter should add like contentfilter = all
* be aware of this when implementing an extractor.
*/
@State
protected String lastSearchedQuery;
protected String[] contentFilter = new String[0];
@State
protected String sortFilter;
// these represtent the last search
@State
protected String lastSearchedString;
@State
protected boolean wasSearchFocused = false;
private int currentPage = 0;
private int currentNextPage = 0;
private Map<Integer, String> menuItemToFilterName;
private StreamingService service;
private String currentPageUrl;
private String nextPageUrl;
private String contentCountry;
private boolean isSuggestionsEnabled = true;
private boolean isSearchHistoryEnabled = true;
private PublishSubject<String> suggestionPublisher = PublishSubject.create();
private final PublishSubject<String> suggestionPublisher = PublishSubject.create();
private Disposable searchDisposable;
private Disposable suggestionDisposable;
private CompositeDisposable disposables = new CompositeDisposable();
private final CompositeDisposable disposables = new CompositeDisposable();
private SuggestionListAdapter suggestionListAdapter;
private HistoryRecordManager historyRecordManager;
@@ -130,11 +150,11 @@ public class SearchFragment
/*////////////////////////////////////////////////////////////////////////*/
public static SearchFragment getInstance(int serviceId, String query) {
public static SearchFragment getInstance(int serviceId, String searchString) {
SearchFragment searchFragment = new SearchFragment();
searchFragment.setQuery(serviceId, query);
searchFragment.setQuery(serviceId, searchString, new String[0], "");
if (!TextUtils.isEmpty(query)) {
if (!TextUtils.isEmpty(searchString)) {
searchFragment.setSearchOnResume();
}
@@ -158,7 +178,7 @@ public class SearchFragment
suggestionListAdapter = new SuggestionListAdapter(activity);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
isSearchHistoryEnabled = preferences.getBoolean(getString(R.string.enable_search_history_key), true);
boolean isSearchHistoryEnabled = preferences.getBoolean(getString(R.string.enable_search_history_key), true);
suggestionListAdapter.setShowSuggestionHistory(isSearchHistoryEnabled);
historyRecordManager = new HistoryRecordManager(context);
@@ -202,13 +222,22 @@ public class SearchFragment
if (DEBUG) Log.d(TAG, "onResume() called");
super.onResume();
if (!TextUtils.isEmpty(searchQuery)) {
try {
service = NewPipe.getService(serviceId);
} catch (Exception e) {
ErrorActivity.reportError(getActivity(), e, getActivity().getClass(),
getActivity().findViewById(android.R.id.content),
ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
"",
"", R.string.general_error));
}
if (!TextUtils.isEmpty(searchString)) {
if (wasLoading.getAndSet(false)) {
if (currentNextPage > currentPage) loadMoreItems();
else search(searchQuery);
search(searchString, contentFilter, sortFilter);
} else if (infoListAdapter.getItemsList().size() == 0) {
if (savedState == null) {
search(searchQuery);
search(searchString, contentFilter, sortFilter);
} else if (!isLoading.get() && !wasSearchFocused) {
infoListAdapter.clearStreamItemList();
showEmptyState();
@@ -218,7 +247,7 @@ public class SearchFragment
if (suggestionDisposable == null || suggestionDisposable.isDisposed()) initSuggestionObserver();
if (TextUtils.isEmpty(searchQuery) || wasSearchFocused) {
if (TextUtils.isEmpty(searchString) || wasSearchFocused) {
showKeyboardSearch();
showSuggestionsPanel();
} else {
@@ -247,8 +276,9 @@ public class SearchFragment
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case ReCaptchaActivity.RECAPTCHA_REQUEST:
if (resultCode == Activity.RESULT_OK && !TextUtils.isEmpty(searchQuery)) {
search(searchQuery);
if (resultCode == Activity.RESULT_OK
&& !TextUtils.isEmpty(searchString)) {
search(searchString, contentFilter, sortFilter);
} else Log.e(TAG, "ReCaptcha failed");
break;
@@ -282,20 +312,22 @@ public class SearchFragment
@Override
public void writeTo(Queue<Object> objectsToSave) {
super.writeTo(objectsToSave);
objectsToSave.add(currentPage);
objectsToSave.add(currentNextPage);
objectsToSave.add(currentPageUrl);
objectsToSave.add(nextPageUrl);
}
@Override
public void readFrom(@NonNull Queue<Object> savedObjects) throws Exception {
super.readFrom(savedObjects);
currentPage = (int) savedObjects.poll();
currentNextPage = (int) savedObjects.poll();
currentPageUrl = (String) savedObjects.poll();
nextPageUrl = (String) savedObjects.poll();
}
@Override
public void onSaveInstanceState(Bundle bundle) {
searchQuery = searchEditText != null ? searchEditText.getText().toString() : searchQuery;
searchString = searchEditText != null
? searchEditText.getText().toString()
: searchString;
super.onSaveInstanceState(bundle);
}
@@ -305,8 +337,11 @@ public class SearchFragment
@Override
public void reloadContent() {
if (!TextUtils.isEmpty(searchQuery) || (searchEditText != null && !TextUtils.isEmpty(searchEditText.getText()))) {
search(!TextUtils.isEmpty(searchQuery) ? searchQuery : searchEditText.getText().toString());
if (!TextUtils.isEmpty(searchString)
|| (searchEditText != null && !TextUtils.isEmpty(searchEditText.getText()))) {
search(!TextUtils.isEmpty(searchString)
? searchString
: searchEditText.getText().toString(), this.contentFilter, "");
} else {
if (searchEditText != null) {
searchEditText.setText("");
@@ -330,22 +365,35 @@ public class SearchFragment
supportActionBar.setDisplayHomeAsUpEnabled(true);
}
inflater.inflate(R.menu.menu_search, menu);
menuItemToFilterName = new HashMap<>();
int itemId = 0;
boolean isFirstItem = true;
final Context c = getContext();
for(String filter : service.getSearchQHFactory().getAvailableContentFilter()) {
menuItemToFilterName.put(itemId, filter);
MenuItem item = menu.add(1,
itemId++,
0,
ServiceHelper.getTranslatedFilterString(filter, c));
if(isFirstItem) {
item.setChecked(true);
isFirstItem = false;
}
}
menu.setGroupCheckable(1, true, true);
restoreFilterChecked(menu, filterItemCheckedId);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_filter_all:
case R.id.menu_filter_video:
case R.id.menu_filter_channel:
case R.id.menu_filter_playlist:
changeFilter(item, getFilterFromMenuId(item.getItemId()));
return true;
default:
return super.onOptionsItemSelected(item);
}
List<String> contentFilter = new ArrayList<>(1);
contentFilter.add(menuItemToFilterName.get(item.getItemId()));
changeContentFilter(item, contentFilter);
return true;
}
private void restoreFilterChecked(Menu menu, int itemId) {
@@ -354,21 +402,6 @@ public class SearchFragment
if (item == null) return;
item.setChecked(true);
filter = getFilterFromMenuId(itemId);
}
}
private SearchEngine.Filter getFilterFromMenuId(int itemId) {
switch (itemId) {
case R.id.menu_filter_video:
return SearchEngine.Filter.STREAM;
case R.id.menu_filter_channel:
return SearchEngine.Filter.CHANNEL;
case R.id.menu_filter_playlist:
return SearchEngine.Filter.PLAYLIST;
case R.id.menu_filter_all:
default:
return SearchEngine.Filter.ANY;
}
}
@@ -379,14 +412,21 @@ public class SearchFragment
private TextWatcher textWatcher;
private void showSearchOnStart() {
if (DEBUG) Log.d(TAG, "showSearchOnStart() called, searchQuery → " + searchQuery+", lastSearchedQuery → " + lastSearchedQuery);
searchEditText.setText(searchQuery);
if (DEBUG) Log.d(TAG, "showSearchOnStart() called, searchQuery → "
+ searchString
+ ", lastSearchedQuery → "
+ lastSearchedString);
searchEditText.setText(searchString);
if (TextUtils.isEmpty(searchQuery) || TextUtils.isEmpty(searchEditText.getText())) {
if (TextUtils.isEmpty(searchString) || TextUtils.isEmpty(searchEditText.getText())) {
searchToolbarContainer.setTranslationX(100);
searchToolbarContainer.setAlpha(0f);
searchToolbarContainer.setVisibility(View.VISIBLE);
searchToolbarContainer.animate().translationX(0).alpha(1f).setDuration(200).setInterpolator(new DecelerateInterpolator()).start();
searchToolbarContainer.animate()
.translationX(0)
.alpha(1f)
.setDuration(200)
.setInterpolator(new DecelerateInterpolator()).start();
} else {
searchToolbarContainer.setTranslationX(0);
searchToolbarContainer.setAlpha(1f);
@@ -396,47 +436,41 @@ public class SearchFragment
private void initSearchListeners() {
if (DEBUG) Log.d(TAG, "initSearchListeners() called");
searchClear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (DEBUG) Log.d(TAG, "onClick() called with: v = [" + v + "]");
if (TextUtils.isEmpty(searchEditText.getText())) {
NavigationHelper.gotoMainFragment(getFragmentManager());
return;
}
searchEditText.setText("");
suggestionListAdapter.setItems(new ArrayList<SuggestionItem>());
showKeyboardSearch();
searchClear.setOnClickListener(v -> {
if (DEBUG) Log.d(TAG, "onClick() called with: v = [" + v + "]");
if (TextUtils.isEmpty(searchEditText.getText())) {
NavigationHelper.gotoMainFragment(getFragmentManager());
return;
}
searchEditText.setText("");
suggestionListAdapter.setItems(new ArrayList<>());
showKeyboardSearch();
});
TooltipCompat.setTooltipText(searchClear, getString(R.string.clear));
searchEditText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (DEBUG) Log.d(TAG, "onClick() called with: v = [" + v + "]");
if (isSuggestionsEnabled && errorPanelRoot.getVisibility() != View.VISIBLE) {
showSuggestionsPanel();
}
searchEditText.setOnClickListener(v -> {
if (DEBUG) Log.d(TAG, "onClick() called with: v = [" + v + "]");
if (isSuggestionsEnabled && errorPanelRoot.getVisibility() != View.VISIBLE) {
showSuggestionsPanel();
}
if(FireTvUtils.isFireTv()){
showKeyboardSearch();
}
});
searchEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (DEBUG) Log.d(TAG, "onFocusChange() called with: v = [" + v + "], hasFocus = [" + hasFocus + "]");
if (isSuggestionsEnabled && hasFocus && errorPanelRoot.getVisibility() != View.VISIBLE) {
showSuggestionsPanel();
}
searchEditText.setOnFocusChangeListener((View v, boolean hasFocus) -> {
if (DEBUG) Log.d(TAG, "onFocusChange() called with: v = [" + v + "], hasFocus = [" + hasFocus + "]");
if (isSuggestionsEnabled && hasFocus && errorPanelRoot.getVisibility() != View.VISIBLE) {
showSuggestionsPanel();
}
});
suggestionListAdapter.setListener(new SuggestionListAdapter.OnSuggestionItemSelected() {
@Override
public void onSuggestionItemSelected(SuggestionItem item) {
search(item.query);
search(item.query, new String[0], "");
searchEditText.setText(item.query);
}
@@ -469,21 +503,24 @@ public class SearchFragment
}
};
searchEditText.addTextChangedListener(textWatcher);
searchEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (DEBUG) {
Log.d(TAG, "onEditorAction() called with: v = [" + v + "], actionId = [" + actionId + "], event = [" + event + "]");
}
if (event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER || event.getAction() == EditorInfo.IME_ACTION_SEARCH)) {
search(searchEditText.getText().toString());
return true;
}
return false;
}
});
searchEditText.setOnEditorActionListener(
(TextView v, int actionId, KeyEvent event) -> {
if (DEBUG) {
Log.d(TAG, "onEditorAction() called with: v = [" + v + "], actionId = [" + actionId + "], event = [" + event + "]");
}
if(actionId == EditorInfo.IME_ACTION_PREVIOUS){
hideKeyboardSearch();
} else if (event != null
&& (event.getKeyCode() == KeyEvent.KEYCODE_ENTER
|| event.getAction() == EditorInfo.IME_ACTION_SEARCH)) {
search(searchEditText.getText().toString(), new String[0], "");
return true;
}
return false;
});
if (suggestionDisposable == null || suggestionDisposable.isDisposed()) initSuggestionObserver();
if (suggestionDisposable == null || suggestionDisposable.isDisposed())
initSuggestionObserver();
}
private void unsetSearchListeners() {
@@ -513,8 +550,9 @@ public class SearchFragment
if (searchEditText == null) return;
if (searchEditText.requestFocus()) {
InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(searchEditText, InputMethodManager.SHOW_IMPLICIT);
InputMethodManager imm = (InputMethodManager) activity.getSystemService(
Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(searchEditText, InputMethodManager.SHOW_FORCED);
}
}
@@ -522,8 +560,9 @@ public class SearchFragment
if (DEBUG) Log.d(TAG, "hideKeyboardSearch() called");
if (searchEditText == null) return;
InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(searchEditText.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
InputMethodManager imm = (InputMethodManager) activity.getSystemService(
Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(searchEditText.getWindowToken(), InputMethodManager.RESULT_UNCHANGED_SHOWN);
searchEditText.clearFocus();
}
@@ -544,9 +583,8 @@ public class SearchFragment
howManyDeleted -> suggestionPublisher
.onNext(searchEditText.getText().toString()),
throwable -> showSnackBarError(throwable,
UserAction.SOMETHING_ELSE, "none",
"Deleting item failed", R.string.general_error)
);
UserAction.DELETE_FROM_HISTORY, "none",
"Deleting item failed", R.string.general_error));
disposables.add(onDelete);
})
.show();
@@ -554,10 +592,12 @@ public class SearchFragment
@Override
public boolean onBackPressed() {
if (suggestionsPanel.getVisibility() == View.VISIBLE && infoListAdapter.getItemsList().size() > 0 && !isLoading.get()) {
if (suggestionsPanel.getVisibility() == View.VISIBLE
&& infoListAdapter.getItemsList().size() > 0
&& !isLoading.get()) {
hideSuggestionsPanel();
hideKeyboardSearch();
searchEditText.setText(lastSearchedQuery);
searchEditText.setText(lastSearchedString);
return true;
}
return false;
@@ -573,8 +613,10 @@ public class SearchFragment
final Observable<String> observable = suggestionPublisher
.debounce(SUGGESTIONS_DEBOUNCE, TimeUnit.MILLISECONDS)
.startWith(searchQuery != null ? searchQuery : "")
.filter(query -> isSuggestionsEnabled);
.startWith(searchString != null
? searchString
: "")
.filter(searchString -> isSuggestionsEnabled);
suggestionDisposable = observable
.switchMap(query -> {
@@ -594,7 +636,7 @@ public class SearchFragment
}
final Observable<List<SuggestionItem>> network = ExtractorHelper
.suggestionsFor(serviceId, query, contentCountry)
.suggestionsFor(serviceId, query)
.toObservable()
.map(strings -> {
List<SuggestionItem> result = new ArrayList<>();
@@ -645,56 +687,44 @@ public class SearchFragment
// no-op
}
private void search(final String query) {
if (DEBUG) Log.d(TAG, "search() called with: query = [" + query + "]");
if (query.isEmpty()) return;
private void search(final String searchString, String[] contentFilter, String sortFilter) {
if (DEBUG) Log.d(TAG, "search() called with: query = [" + searchString + "]");
if (searchString.isEmpty()) return;
try {
final StreamingService service = NewPipe.getServiceByUrl(query);
final StreamingService service = NewPipe.getServiceByUrl(searchString);
if (service != null) {
showLoading();
disposables.add(Observable
.fromCallable(new Callable<Intent>() {
@Override
public Intent call() throws Exception {
return NavigationHelper.getIntentByLink(activity, service, query);
}
})
.fromCallable(() ->
NavigationHelper.getIntentByLink(activity, service, searchString))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Intent>() {
@Override
public void accept(Intent intent) throws Exception {
getFragmentManager().popBackStackImmediate();
activity.startActivity(intent);
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
showError(getString(R.string.url_not_supported_toast), false);
}
}));
.subscribe(intent -> {
getFragmentManager().popBackStackImmediate();
activity.startActivity(intent);
}, throwable ->
showError(getString(R.string.url_not_supported_toast), false)));
return;
}
} catch (Exception e) {
// Exception occurred, it's not a url
}
lastSearchedQuery = query;
searchQuery = query;
currentPage = 0;
lastSearchedString = this.searchString;
this.searchString = searchString;
infoListAdapter.clearStreamItemList();
hideSuggestionsPanel();
hideKeyboardSearch();
historyRecordManager.onSearched(serviceId, query)
historyRecordManager.onSearched(serviceId, searchString)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
ignored -> {},
error -> showSnackBarError(error, UserAction.SEARCHED,
NewPipe.getNameOfService(serviceId), query, 0)
NewPipe.getNameOfService(serviceId), searchString, 0)
);
suggestionPublisher.onNext(query);
suggestionPublisher.onNext(searchString);
startLoading(false);
}
@@ -703,20 +733,29 @@ public class SearchFragment
super.startLoading(forceLoad);
if (disposables != null) disposables.clear();
if (searchDisposable != null) searchDisposable.dispose();
searchDisposable = ExtractorHelper.searchFor(serviceId, searchQuery, currentPage, contentCountry, filter)
searchDisposable = ExtractorHelper.searchFor(serviceId,
searchString,
Arrays.asList(contentFilter),
sortFilter)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnEvent((searchResult, throwable) -> isLoading.set(false))
.subscribe(this::handleResult, this::onError);
}
@Override
protected void loadMoreItems() {
if(nextPageUrl == null || nextPageUrl.isEmpty()) return;
isLoading.set(true);
showListFooter(true);
if (searchDisposable != null) searchDisposable.dispose();
currentNextPage = currentPage + 1;
searchDisposable = ExtractorHelper.getMoreSearchItems(serviceId, searchQuery, currentNextPage, contentCountry, filter)
searchDisposable = ExtractorHelper.getMoreSearchItems(
serviceId,
searchString,
asList(contentFilter),
sortFilter,
nextPageUrl)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnEvent((nextItemsResult, throwable) -> isLoading.set(false))
@@ -739,19 +778,22 @@ public class SearchFragment
// Utils
//////////////////////////////////////////////////////////////////////////*/
private void changeFilter(MenuItem item, SearchEngine.Filter filter) {
this.filter = filter;
private void changeContentFilter(MenuItem item, List<String> contentFilter) {
this.filterItemCheckedId = item.getItemId();
item.setChecked(true);
if (!TextUtils.isEmpty(searchQuery)) {
search(searchQuery);
this.contentFilter = new String[] {contentFilter.get(0)};
if (!TextUtils.isEmpty(searchString)) {
search(searchString, this.contentFilter, sortFilter);
}
}
private void setQuery(int serviceId, String searchQuery) {
private void setQuery(int serviceId, String searchString, String[] contentfilter, String sortFilter) {
this.serviceId = serviceId;
this.searchQuery = searchQuery;
this.searchString = searchString;
this.contentFilter = contentfilter;
this.sortFilter = sortFilter;
}
/*//////////////////////////////////////////////////////////////////////////
@@ -772,8 +814,11 @@ public class SearchFragment
if (DEBUG) Log.d(TAG, "onSuggestionError() called with: exception = [" + exception + "]");
if (super.onError(exception)) return;
int errorId = exception instanceof ParsingException ? R.string.parsing_error : R.string.general_error;
onUnrecoverableError(exception, UserAction.GET_SUGGESTIONS, NewPipe.getNameOfService(serviceId), searchQuery, errorId);
int errorId = exception instanceof ParsingException
? R.string.parsing_error
: R.string.general_error;
onUnrecoverableError(exception, UserAction.GET_SUGGESTIONS,
NewPipe.getNameOfService(serviceId), searchString, errorId);
}
/*//////////////////////////////////////////////////////////////////////////
@@ -798,16 +843,22 @@ public class SearchFragment
//////////////////////////////////////////////////////////////////////////*/
@Override
public void handleResult(@NonNull SearchResult result) {
if (!result.errors.isEmpty()) {
showSnackBarError(result.errors, UserAction.SEARCHED, NewPipe.getNameOfService(serviceId), searchQuery, 0);
public void handleResult(@NonNull SearchInfo result) {
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);
}
lastSearchedQuery = searchQuery;
lastSearchedString = searchString;
nextPageUrl = result.getNextPageUrl();
currentPageUrl = result.getUrl();
if (infoListAdapter.getItemsList().size() == 0) {
if (!result.getResults().isEmpty()) {
infoListAdapter.addInfoItemList(result.getResults());
if (!result.getRelatedItems().isEmpty()) {
infoListAdapter.addInfoItemList(result.getRelatedItems());
} else {
infoListAdapter.clearStreamItemList();
showEmptyState();
@@ -821,12 +872,14 @@ public class SearchFragment
@Override
public void handleNextItems(ListExtractor.InfoItemsPage result) {
showListFooter(false);
currentPage = Integer.parseInt(result.getNextPageUrl());
currentPageUrl = result.getNextPageUrl();
infoListAdapter.addInfoItemList(result.getItems());
nextPageUrl = result.getNextPageUrl();
if (!result.getErrors().isEmpty()) {
showSnackBarError(result.getErrors(), UserAction.SEARCHED, NewPipe.getNameOfService(serviceId)
, "\"" + searchQuery + "\" → page " + currentPage, 0);
showSnackBarError(result.getErrors(), UserAction.SEARCHED,
NewPipe.getNameOfService(serviceId)
, "\"" + searchString + "\" → page: " + nextPageUrl, 0);
}
super.handleNextItems(result);
}
@@ -835,12 +888,15 @@ public class SearchFragment
protected boolean onError(Throwable exception) {
if (super.onError(exception)) return true;
if (exception instanceof SearchEngine.NothingFoundException) {
if (exception instanceof SearchExtractor.NothingFoundException) {
infoListAdapter.clearStreamItemList();
showEmptyState();
} else {
int errorId = exception instanceof ParsingException ? R.string.parsing_error : R.string.general_error;
onUnrecoverableError(exception, UserAction.SEARCHED, NewPipe.getNameOfService(serviceId), searchQuery, errorId);
int errorId = exception instanceof ParsingException
? R.string.parsing_error
: R.string.general_error;
onUnrecoverableError(exception, UserAction.SEARCHED,
NewPipe.getNameOfService(serviceId), searchString, errorId);
}
return true;

View File

@@ -63,24 +63,15 @@ public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAd
public void onBindViewHolder(SuggestionItemHolder holder, int position) {
final SuggestionItem currentItem = getItem(position);
holder.updateFrom(currentItem);
holder.queryView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null) listener.onSuggestionItemSelected(currentItem);
}
holder.queryView.setOnClickListener(v -> {
if (listener != null) listener.onSuggestionItemSelected(currentItem);
});
holder.queryView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
holder.queryView.setOnLongClickListener(v -> {
if (listener != null) listener.onSuggestionItemLongClick(currentItem);
return true;
}
});
holder.insertView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null) listener.onSuggestionItemInserted(currentItem);
}
holder.insertView.setOnClickListener(v -> {
if (listener != null) listener.onSuggestionItemInserted(currentItem);
});
}

View File

@@ -0,0 +1,208 @@
package org.schabi.newpipe.fragments.list.videos;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.Switch;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.AnimationUtils;
import org.schabi.newpipe.util.RelatedStreamInfo;
import java.io.Serializable;
import io.reactivex.Single;
import io.reactivex.disposables.CompositeDisposable;
public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInfo> implements SharedPreferences.OnSharedPreferenceChangeListener{
private CompositeDisposable disposables = new CompositeDisposable();
private RelatedStreamInfo relatedStreamInfo;
/*//////////////////////////////////////////////////////////////////////////
// Views
//////////////////////////////////////////////////////////////////////////*/
private View headerRootLayout;
private Switch aSwitch;
private boolean mIsVisibleToUser = false;
public static RelatedVideosFragment getInstance(StreamInfo info) {
RelatedVideosFragment instance = new RelatedVideosFragment();
instance.setInitialData(info);
return instance;
}
/*//////////////////////////////////////////////////////////////////////////
// LifeCycle
//////////////////////////////////////////////////////////////////////////*/
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
mIsVisibleToUser = isVisibleToUser;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_related_streams, container, false);
}
@Override
public void onDestroy() {
super.onDestroy();
if (disposables != null) disposables.clear();
}
protected View getListHeader(){
if(relatedStreamInfo != null && relatedStreamInfo.getNextStream() != null){
headerRootLayout = activity.getLayoutInflater().inflate(R.layout.related_streams_header, itemsList, false);
aSwitch = headerRootLayout.findViewById(R.id.autoplay_switch);
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(getContext());
Boolean autoplay = pref.getBoolean(getString(R.string.auto_queue_key), false);
aSwitch.setChecked(autoplay);
aSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
SharedPreferences.Editor prefEdit = PreferenceManager.getDefaultSharedPreferences(getContext()).edit();
prefEdit.putBoolean(getString(R.string.auto_queue_key), b);
prefEdit.apply();
}
});
return headerRootLayout;
}else{
return null;
}
}
@Override
protected Single<ListExtractor.InfoItemsPage> loadMoreItemsLogic() {
return Single.fromCallable(() -> ListExtractor.InfoItemsPage.emptyPage());
}
@Override
protected Single<RelatedStreamInfo> loadResult(boolean forceLoad) {
return Single.fromCallable(() -> relatedStreamInfo);
}
/*//////////////////////////////////////////////////////////////////////////
// Contract
//////////////////////////////////////////////////////////////////////////*/
@Override
public void showLoading() {
super.showLoading();
if(null != headerRootLayout) headerRootLayout.setVisibility(View.INVISIBLE);
}
@Override
public void handleResult(@NonNull RelatedStreamInfo result) {
super.handleResult(result);
if(null != headerRootLayout) headerRootLayout.setVisibility(View.VISIBLE);
AnimationUtils.slideUp(getView(),120, 96, 0.06f);
if (!result.getErrors().isEmpty()) {
showSnackBarError(result.getErrors(), UserAction.REQUESTED_STREAM, NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
}
if (disposables != null) disposables.clear();
}
@Override
public void handleNextItems(ListExtractor.InfoItemsPage result) {
super.handleNextItems(result);
if (!result.getErrors().isEmpty()) {
showSnackBarError(result.getErrors(),
UserAction.REQUESTED_STREAM,
NewPipe.getNameOfService(serviceId),
"Get next page of: " + url,
R.string.general_error);
}
}
/*//////////////////////////////////////////////////////////////////////////
// OnError
//////////////////////////////////////////////////////////////////////////*/
@Override
protected boolean onError(Throwable exception) {
if (super.onError(exception)) return true;
hideLoading();
showSnackBarError(exception, UserAction.REQUESTED_STREAM, NewPipe.getNameOfService(serviceId), url, R.string.general_error);
return true;
}
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
@Override
public void setTitle(String title) {
return;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
return;
}
private void setInitialData(StreamInfo info) {
super.setInitialData(info.getServiceId(), info.getUrl(), info.getName());
if(this.relatedStreamInfo == null) this.relatedStreamInfo = RelatedStreamInfo.getInfo(info);
}
private static final String INFO_KEY = "related_info_key";
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable(INFO_KEY, relatedStreamInfo);
}
@Override
protected void onRestoreInstanceState(@NonNull Bundle savedState) {
super.onRestoreInstanceState(savedState);
if (savedState != null) {
Serializable serializable = savedState.getSerializable(INFO_KEY);
if(serializable instanceof RelatedStreamInfo){
this.relatedStreamInfo = (RelatedStreamInfo) serializable;
}
}
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(getContext());
Boolean autoplay = pref.getBoolean(getString(R.string.auto_queue_key), false);
if(null != aSwitch) aSwitch.setChecked(autoplay);
}
@Override
protected boolean isGridLayout() {
return false;
}
}

View File

@@ -1,21 +0,0 @@
package org.schabi.newpipe.fragments.local.bookmark;
import org.schabi.newpipe.R;
import org.schabi.newpipe.database.stream.StreamStatisticsEntry;
import java.util.Collections;
import java.util.List;
public final class LastPlayedFragment extends StatisticsPlaylistFragment {
@Override
protected String getName() {
return getString(R.string.title_last_played);
}
@Override
protected List<StreamStatisticsEntry> processResult(List<StreamStatisticsEntry> results) {
Collections.sort(results, (left, right) ->
right.latestAccessDate.compareTo(left.latestAccessDate));
return results;
}
}

View File

@@ -1,22 +0,0 @@
package org.schabi.newpipe.fragments.local.bookmark;
import org.schabi.newpipe.R;
import org.schabi.newpipe.database.stream.StreamStatisticsEntry;
import java.util.Collections;
import java.util.List;
public final class MostPlayedFragment extends StatisticsPlaylistFragment {
@Override
protected String getName() {
return getString(R.string.title_most_played);
}
@Override
protected List<StreamStatisticsEntry> processResult(List<StreamStatisticsEntry> results) {
Collections.sort(results, (left, right) ->
((Long) right.watchCount).compareTo(left.watchCount));
return results;
}
}

View File

@@ -1,141 +0,0 @@
package org.schabi.newpipe.history;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import com.jakewharton.rxbinding2.view.RxView;
import org.schabi.newpipe.R;
import org.schabi.newpipe.settings.SettingsActivity;
import org.schabi.newpipe.util.ThemeHelper;
import io.reactivex.android.schedulers.AndroidSchedulers;
public class HistoryActivity extends AppCompatActivity {
private static final String TAG = "HistoryActivity";
/**
* The {@link android.support.v4.view.PagerAdapter} that will provide
* fragments for each of the sections. We use a
* {@link FragmentPagerAdapter} derivative, which will keep every
* loaded fragment in memory. If this becomes too memory intensive, it
* may be best to switch to a
* {@link android.support.v4.app.FragmentStatePagerAdapter}.
*/
private SectionsPagerAdapter mSectionsPagerAdapter;
/**
* The {@link ViewPager} that will host the section contents.
*/
private ViewPager mViewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ThemeHelper.setTheme(this);
setContentView(R.layout.activity_history);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setTitle(R.string.title_activity_history);
}
// Create the adapter that will return a fragment for each of the three
// primary sections of the activity.
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// Set up the ViewPager with the sections adapter.
mViewPager = findViewById(R.id.container);
mViewPager.setAdapter(mSectionsPagerAdapter);
TabLayout tabLayout = findViewById(R.id.tabs);
tabLayout.setupWithViewPager(mViewPager);
final FloatingActionButton fab = findViewById(R.id.fab);
RxView.clicks(fab)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(ignored -> {
int currentItem = mViewPager.getCurrentItem();
HistoryFragment fragment = (HistoryFragment) mSectionsPagerAdapter
.instantiateItem(mViewPager, currentItem);
fragment.onHistoryCleared();
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_history, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
case R.id.action_settings:
Intent intent = new Intent(this, SettingsActivity.class);
startActivity(intent);
return true;
}
return super.onOptionsItemSelected(item);
}
/**
* A {@link FragmentPagerAdapter} that returns a fragment corresponding to
* one of the sections/tabs/pages.
*/
public class SectionsPagerAdapter extends FragmentStatePagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
Fragment fragment;
switch (position) {
case 0:
fragment = SearchHistoryFragment.newInstance();
break;
case 1:
fragment = WatchHistoryFragment.newInstance();
break;
default:
throw new IllegalArgumentException("position: " + position);
}
return fragment;
}
@Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return getString(R.string.title_history_search);
case 1:
return getString(R.string.title_history_view);
}
throw new IllegalArgumentException("position: " + position);
}
@Override
public int getCount() {
// Show 3 total pages.
return 2;
}
}
}

View File

@@ -1,286 +0,0 @@
package org.schabi.newpipe.history;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Parcelable;
import android.preference.PreferenceManager;
import android.support.annotation.CallSuper;
import android.support.annotation.MainThread;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.schabi.newpipe.BaseFragment;
import org.schabi.newpipe.R;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import icepick.State;
import io.reactivex.Flowable;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import static org.schabi.newpipe.util.AnimationUtils.animateView;
public abstract class HistoryFragment<E> extends BaseFragment
implements HistoryEntryAdapter.OnHistoryItemClickListener<E> {
private SharedPreferences mSharedPreferences;
private String mHistoryIsEnabledKey;
private boolean mHistoryIsEnabled;
private HistoryIsEnabledChangeListener mHistoryIsEnabledChangeListener;
private View mDisabledView;
private View mEmptyHistoryView;
@State
Parcelable mRecyclerViewState;
private RecyclerView mRecyclerView;
private HistoryEntryAdapter<E, ? extends RecyclerView.ViewHolder> mHistoryAdapter;
private Subscription historySubscription;
protected HistoryRecordManager historyRecordManager;
protected CompositeDisposable disposables;
@StringRes
abstract int getEnabledConfigKey();
@CallSuper
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHistoryIsEnabledKey = getString(getEnabledConfigKey());
mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
// Read history enabled from preferences
mHistoryIsEnabled = isHistoryEnabled();
// Register history enabled listener
mSharedPreferences.registerOnSharedPreferenceChangeListener(mHistoryIsEnabledChangeListener);
historyRecordManager = new HistoryRecordManager(getContext());
disposables = new CompositeDisposable();
}
@NonNull
protected abstract HistoryEntryAdapter<E, ? extends RecyclerView.ViewHolder> createAdapter();
protected abstract Single<List<Long>> insert(final Collection<E> entries);
protected abstract Single<Integer> delete(final Collection<E> entries);
@NonNull
protected abstract Flowable<List<E>> getAll();
@Override
public void onResume() {
super.onResume();
getAll().observeOn(AndroidSchedulers.mainThread()).subscribe(getHistorySubscriber());
final boolean newEnabled = isHistoryEnabled();
if (newEnabled != mHistoryIsEnabled) {
onHistoryIsEnabledChanged(newEnabled);
}
}
@NonNull
private Subscriber<List<E>> getHistorySubscriber() {
return new Subscriber<List<E>>() {
@Override
public void onSubscribe(Subscription s) {
if (historySubscription != null) historySubscription.cancel();
historySubscription = s;
historySubscription.request(1);
}
@Override
public void onNext(List<E> entries) {
if (!entries.isEmpty()) {
mHistoryAdapter.setEntries(entries);
animateView(mEmptyHistoryView, false, 200);
if (mRecyclerViewState != null) {
mRecyclerView.getLayoutManager().onRestoreInstanceState(mRecyclerViewState);
mRecyclerViewState = null;
}
} else {
mHistoryAdapter.clear();
showEmptyHistory();
}
if (historySubscription != null) historySubscription.request(1);
}
@Override
public void onError(Throwable t) {
}
@Override
public void onComplete() {
}
};
}
private boolean isHistoryEnabled() {
return mSharedPreferences.getBoolean(mHistoryIsEnabledKey, false);
}
/**
* Called when the history is cleared to update the views
*/
@MainThread
public void onHistoryCleared() {
if (getContext() == null) return;
new AlertDialog.Builder(getContext())
.setTitle(R.string.delete_all)
.setMessage(R.string.delete_all_history_prompt)
.setCancelable(true)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.delete_all, (dialog, i) -> clearHistory())
.show();
}
protected void makeSnackbar(@StringRes final int text) {
if (getActivity() == null) return;
View view = getActivity().findViewById(R.id.main_content);
if (view == null) view = mRecyclerView.getRootView();
Snackbar.make(view, text, Snackbar.LENGTH_LONG).show();
}
private void clearHistory() {
final Collection<E> itemsToDelete = new ArrayList<>(mHistoryAdapter.getItems());
final Disposable deletion = delete(itemsToDelete)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
ignored -> Log.d(TAG, "Clear history deleted [" +
itemsToDelete.size() + "] items."),
error -> Log.e(TAG, "Clear history delete step failed", error)
);
final Disposable cleanUp = historyRecordManager.removeOrphanedRecords()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
ignored -> Log.d(TAG, "Clear history deleted orphaned stream records"),
error -> Log.e(TAG, "Clear history remove orphaned records failed", error)
);
disposables.addAll(deletion, cleanUp);
makeSnackbar(R.string.history_cleared);
mHistoryAdapter.clear();
showEmptyHistory();
}
private void showEmptyHistory() {
if (mHistoryIsEnabled) {
animateView(mEmptyHistoryView, true, 200);
}
}
@Nullable
@CallSuper
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_history, container, false);
mRecyclerView = rootView.findViewById(R.id.history_view);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getContext(),
LinearLayoutManager.VERTICAL, false);
mRecyclerView.setLayoutManager(layoutManager);
mHistoryAdapter = createAdapter();
mHistoryAdapter.setOnHistoryItemClickListener(this);
mRecyclerView.setAdapter(mHistoryAdapter);
mDisabledView = rootView.findViewById(R.id.history_disabled_view);
mEmptyHistoryView = rootView.findViewById(R.id.history_empty);
if (mHistoryIsEnabled) {
mRecyclerView.setVisibility(View.VISIBLE);
} else {
mRecyclerView.setVisibility(View.GONE);
mDisabledView.setVisibility(View.VISIBLE);
}
return rootView;
}
@CallSuper
@Override
public void onDestroy() {
super.onDestroy();
if (disposables != null) disposables.dispose();
if (historySubscription != null) historySubscription.cancel();
mSharedPreferences.unregisterOnSharedPreferenceChangeListener(mHistoryIsEnabledChangeListener);
mSharedPreferences = null;
mHistoryIsEnabledChangeListener = null;
mHistoryIsEnabledKey = null;
historySubscription = null;
disposables = null;
}
@Override
public void onPause() {
super.onPause();
mRecyclerViewState = mRecyclerView.getLayoutManager().onSaveInstanceState();
}
/**
* Called when history enabled flag is changed.
*
* @param historyIsEnabled the new value
*/
@CallSuper
public void onHistoryIsEnabledChanged(boolean historyIsEnabled) {
mHistoryIsEnabled = historyIsEnabled;
if (historyIsEnabled) {
animateView(mRecyclerView, true, 300);
animateView(mDisabledView, false, 300);
if (mHistoryAdapter.isEmpty()) {
animateView(mEmptyHistoryView, true, 300);
}
} else {
animateView(mRecyclerView, false, 300);
animateView(mDisabledView, true, 300);
animateView(mEmptyHistoryView, false, 300);
}
}
private class HistoryIsEnabledChangeListener
implements SharedPreferences.OnSharedPreferenceChangeListener {
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals(mHistoryIsEnabledKey)) {
boolean enabled = sharedPreferences.getBoolean(key, false);
if (mHistoryIsEnabled != enabled) {
onHistoryIsEnabledChanged(enabled);
}
}
}
}
}

View File

@@ -1,145 +0,0 @@
package org.schabi.newpipe.history;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import org.schabi.newpipe.R;
import org.schabi.newpipe.database.history.model.SearchHistoryEntry;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.NavigationHelper;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import io.reactivex.Flowable;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
public class SearchHistoryFragment extends HistoryFragment<SearchHistoryEntry> {
@NonNull
public static SearchHistoryFragment newInstance() {
return new SearchHistoryFragment();
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@NonNull
@Override
protected SearchHistoryAdapter createAdapter() {
return new SearchHistoryAdapter(getContext());
}
@Override
protected Single<List<Long>> insert(Collection<SearchHistoryEntry> entries) {
return historyRecordManager.insertSearches(entries);
}
@Override
protected Single<Integer> delete(Collection<SearchHistoryEntry> entries) {
return historyRecordManager.deleteSearches(entries);
}
@NonNull
@Override
protected Flowable<List<SearchHistoryEntry>> getAll() {
return historyRecordManager.getSearchHistory();
}
@StringRes
@Override
int getEnabledConfigKey() {
return R.string.enable_search_history_key;
}
@Override
public void onHistoryItemClick(final SearchHistoryEntry historyItem) {
NavigationHelper.openSearch(getContext(), historyItem.getServiceId(),
historyItem.getSearch());
}
@Override
public void onHistoryItemLongClick(final SearchHistoryEntry item) {
if (activity == null) return;
new AlertDialog.Builder(activity)
.setTitle(item.getSearch())
.setMessage(R.string.delete_item_search_history)
.setCancelable(true)
.setNeutralButton(R.string.cancel, null)
.setPositiveButton(R.string.delete_one, (dialog, i) -> {
final Disposable onDelete = historyRecordManager
.deleteSearches(Collections.singleton(item))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
ignored -> {/*successful*/},
error -> Log.e(TAG, "Search history Delete One failed:", error)
);
disposables.add(onDelete);
makeSnackbar(R.string.item_deleted);
})
.setNegativeButton(R.string.delete_all, (dialog, i) -> {
final Disposable onDeleteAll = historyRecordManager
.deleteSearchHistory(item.getSearch())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
ignored -> {/*successful*/},
error -> Log.e(TAG, "Search history Delete All failed:", error)
);
disposables.add(onDeleteAll);
makeSnackbar(R.string.item_deleted);
})
.show();
}
private static class ViewHolder extends RecyclerView.ViewHolder {
private final TextView search;
private final TextView info;
public ViewHolder(View itemView) {
super(itemView);
search = itemView.findViewById(R.id.search);
info = itemView.findViewById(R.id.info);
}
}
protected class SearchHistoryAdapter extends HistoryEntryAdapter<SearchHistoryEntry, ViewHolder> {
SearchHistoryAdapter(Context context) {
super(context);
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View rootView = inflater.inflate(R.layout.item_search_history, parent, false);
return new ViewHolder(rootView);
}
@Override
void onBindViewHolder(ViewHolder holder, SearchHistoryEntry entry, int position) {
holder.search.setText(entry.getSearch());
final String info = Localization.concatenateStrings(
getFormattedDate(entry.getCreationDate()),
NewPipe.getNameOfService(entry.getServiceId()));
holder.info.setText(info);
}
}
}

View File

@@ -1,171 +0,0 @@
package org.schabi.newpipe.history;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.nostra13.universalimageloader.core.ImageLoader;
import org.schabi.newpipe.R;
import org.schabi.newpipe.database.history.model.StreamHistoryEntry;
import org.schabi.newpipe.info_list.holder.StreamInfoItemHolder;
import org.schabi.newpipe.util.ImageDisplayConstants;
import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.NavigationHelper;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import io.reactivex.Flowable;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
public class WatchHistoryFragment extends HistoryFragment<StreamHistoryEntry> {
@NonNull
public static WatchHistoryFragment newInstance() {
return new WatchHistoryFragment();
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@StringRes
@Override
int getEnabledConfigKey() {
return R.string.enable_watch_history_key;
}
@NonNull
@Override
protected StreamHistoryAdapter createAdapter() {
return new StreamHistoryAdapter(getContext());
}
@Override
protected Single<List<Long>> insert(Collection<StreamHistoryEntry> entries) {
return historyRecordManager.insertStreamHistory(entries);
}
@Override
protected Single<Integer> delete(Collection<StreamHistoryEntry> entries) {
return historyRecordManager.deleteStreamHistory(entries);
}
@NonNull
@Override
protected Flowable<List<StreamHistoryEntry>> getAll() {
return historyRecordManager.getStreamHistory();
}
@Override
public void onHistoryItemClick(StreamHistoryEntry historyItem) {
NavigationHelper.openVideoDetail(getContext(), historyItem.serviceId, historyItem.url,
historyItem.title);
}
@Override
public void onHistoryItemLongClick(StreamHistoryEntry item) {
new AlertDialog.Builder(activity)
.setTitle(item.title)
.setMessage(R.string.delete_stream_history_prompt)
.setCancelable(true)
.setNeutralButton(R.string.cancel, null)
.setPositiveButton(R.string.delete_one, (dialog, i) -> {
final Disposable onDelete = historyRecordManager
.deleteStreamHistory(Collections.singleton(item))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
ignored -> {/*successful*/},
error -> Log.e(TAG, "Watch history Delete One failed:", error)
);
disposables.add(onDelete);
makeSnackbar(R.string.item_deleted);
})
.setNegativeButton(R.string.delete_all, (dialog, i) -> {
final Disposable onDeleteAll = historyRecordManager
.deleteStreamHistory(item.streamId)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
ignored -> {/*successful*/},
error -> Log.e(TAG, "Watch history Delete All failed:", error)
);
disposables.add(onDeleteAll);
makeSnackbar(R.string.item_deleted);
})
.show();
}
private static class StreamHistoryAdapter extends HistoryEntryAdapter<StreamHistoryEntry, ViewHolder> {
StreamHistoryAdapter(Context context) {
super(context);
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View itemView = inflater.inflate(R.layout.list_stream_item, parent, false);
return new ViewHolder(itemView);
}
@Override
public void onViewRecycled(ViewHolder holder) {
holder.itemView.setOnClickListener(null);
ImageLoader.getInstance()
.cancelDisplayTask(holder.thumbnailView);
}
@Override
void onBindViewHolder(ViewHolder holder, StreamHistoryEntry entry, int position) {
final String formattedDate = getFormattedDate(entry.accessDate);
final String info;
if (entry.repeatCount > 1) {
info = Localization.concatenateStrings(formattedDate,
getFormattedViewString(entry.repeatCount));
} else {
info = formattedDate;
}
holder.info.setText(info);
holder.streamTitle.setText(entry.title);
holder.uploader.setText(entry.uploader);
holder.duration.setText(Localization.getDurationString(entry.duration));
ImageLoader.getInstance().displayImage(entry.thumbnailUrl, holder.thumbnailView,
ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS);
}
}
private static class ViewHolder extends RecyclerView.ViewHolder {
private final TextView info;
private final TextView streamTitle;
private final ImageView thumbnailView;
private final TextView uploader;
private final TextView duration;
public ViewHolder(View itemView) {
super(itemView);
thumbnailView = itemView.findViewById(R.id.itemThumbnailView);
info = itemView.findViewById(R.id.itemAdditionalDetails);
streamTitle = itemView.findViewById(R.id.itemVideoTitleView);
uploader = itemView.findViewById(R.id.itemUploaderView);
duration = itemView.findViewById(R.id.itemDurationView);
}
}
}

View File

@@ -10,10 +10,13 @@ import com.nostra13.universalimageloader.core.ImageLoader;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.info_list.holder.ChannelInfoItemHolder;
import org.schabi.newpipe.info_list.holder.ChannelMiniInfoItemHolder;
import org.schabi.newpipe.info_list.holder.CommentsInfoItemHolder;
import org.schabi.newpipe.info_list.holder.CommentsMiniInfoItemHolder;
import org.schabi.newpipe.info_list.holder.InfoItemHolder;
import org.schabi.newpipe.info_list.holder.PlaylistInfoItemHolder;
import org.schabi.newpipe.info_list.holder.PlaylistMiniInfoItemHolder;
@@ -45,11 +48,12 @@ public class InfoItemBuilder {
private static final String TAG = InfoItemBuilder.class.toString();
private final Context context;
private ImageLoader imageLoader = ImageLoader.getInstance();
private final ImageLoader imageLoader = ImageLoader.getInstance();
private OnClickGesture<StreamInfoItem> onStreamSelectedListener;
private OnClickGesture<ChannelInfoItem> onChannelSelectedListener;
private OnClickGesture<PlaylistInfoItem> onPlaylistSelectedListener;
private OnClickGesture<CommentsInfoItem> onCommentsSelectedListener;
public InfoItemBuilder(Context context) {
this.context = context;
@@ -73,6 +77,8 @@ public class InfoItemBuilder {
return useMiniVariant ? new ChannelMiniInfoItemHolder(this, parent) : new ChannelInfoItemHolder(this, parent);
case PLAYLIST:
return useMiniVariant ? new PlaylistMiniInfoItemHolder(this, parent) : new PlaylistInfoItemHolder(this, parent);
case COMMENT:
return useMiniVariant ? new CommentsMiniInfoItemHolder(this, parent) : new CommentsInfoItemHolder(this, parent);
default:
Log.e(TAG, "Trollolo");
throw new RuntimeException("InfoType not expected = " + infoType.name());
@@ -111,4 +117,12 @@ public class InfoItemBuilder {
this.onPlaylistSelectedListener = listener;
}
public OnClickGesture<CommentsInfoItem> getOnCommentsSelectedListener() {
return onCommentsSelectedListener;
}
public void setOnCommentsSelectedListener(OnClickGesture<CommentsInfoItem> onCommentsSelectedListener) {
this.onCommentsSelectedListener = onCommentsSelectedListener;
}
}

View File

@@ -5,7 +5,6 @@ import android.app.AlertDialog;
import android.content.DialogInterface;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;

View File

@@ -1,6 +1,7 @@
package org.schabi.newpipe.info_list;
import android.app.Activity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
@@ -8,15 +9,22 @@ import android.view.ViewGroup;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.info_list.holder.ChannelInfoItemHolder;
import org.schabi.newpipe.info_list.holder.ChannelMiniInfoItemHolder;
import org.schabi.newpipe.info_list.holder.CommentsInfoItemHolder;
import org.schabi.newpipe.info_list.holder.CommentsMiniInfoItemHolder;
import org.schabi.newpipe.info_list.holder.ChannelGridInfoItemHolder;
import org.schabi.newpipe.info_list.holder.InfoItemHolder;
import org.schabi.newpipe.info_list.holder.PlaylistGridInfoItemHolder;
import org.schabi.newpipe.info_list.holder.PlaylistInfoItemHolder;
import org.schabi.newpipe.info_list.holder.PlaylistMiniInfoItemHolder;
import org.schabi.newpipe.info_list.holder.StreamGridInfoItemHolder;
import org.schabi.newpipe.info_list.holder.StreamInfoItemHolder;
import org.schabi.newpipe.info_list.holder.StreamMiniInfoItemHolder;
import org.schabi.newpipe.util.FallbackViewHolder;
import org.schabi.newpipe.util.OnClickGesture;
import java.util.ArrayList;
@@ -51,14 +59,20 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
private static final int MINI_STREAM_HOLDER_TYPE = 0x100;
private static final int STREAM_HOLDER_TYPE = 0x101;
private static final int GRID_STREAM_HOLDER_TYPE = 0x102;
private static final int MINI_CHANNEL_HOLDER_TYPE = 0x200;
private static final int CHANNEL_HOLDER_TYPE = 0x201;
private static final int GRID_CHANNEL_HOLDER_TYPE = 0x202;
private static final int MINI_PLAYLIST_HOLDER_TYPE = 0x300;
private static final int PLAYLIST_HOLDER_TYPE = 0x301;
private static final int GRID_PLAYLIST_HOLDER_TYPE = 0x302;
private static final int MINI_COMMENT_HOLDER_TYPE = 0x400;
private static final int COMMENT_HOLDER_TYPE = 0x401;
private final InfoItemBuilder infoItemBuilder;
private final ArrayList<InfoItem> infoItemList;
private boolean useMiniVariant = false;
private boolean useGridVariant = false;
private boolean showFooter = false;
private View header = null;
private View footer = null;
@@ -89,10 +103,18 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
infoItemBuilder.setOnPlaylistSelectedListener(listener);
}
public void setOnCommentsSelectedListener(OnClickGesture<CommentsInfoItem> listener) {
infoItemBuilder.setOnCommentsSelectedListener(listener);
}
public void useMiniItemVariants(boolean useMiniVariant) {
this.useMiniVariant = useMiniVariant;
}
public void setGridItemVariants(boolean useGridVariant) {
this.useGridVariant = useGridVariant;
}
public void addInfoItemList(List<InfoItem> data) {
if (data != null) {
if (DEBUG) {
@@ -205,11 +227,13 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
final InfoItem item = infoItemList.get(position);
switch (item.getInfoType()) {
case STREAM:
return useMiniVariant ? MINI_STREAM_HOLDER_TYPE : STREAM_HOLDER_TYPE;
return useGridVariant ? GRID_STREAM_HOLDER_TYPE : useMiniVariant ? MINI_STREAM_HOLDER_TYPE : STREAM_HOLDER_TYPE;
case CHANNEL:
return useMiniVariant ? MINI_CHANNEL_HOLDER_TYPE : CHANNEL_HOLDER_TYPE;
return useGridVariant ? GRID_CHANNEL_HOLDER_TYPE : useMiniVariant ? MINI_CHANNEL_HOLDER_TYPE : CHANNEL_HOLDER_TYPE;
case PLAYLIST:
return useMiniVariant ? MINI_PLAYLIST_HOLDER_TYPE : PLAYLIST_HOLDER_TYPE;
return useGridVariant ? GRID_PLAYLIST_HOLDER_TYPE : useMiniVariant ? MINI_PLAYLIST_HOLDER_TYPE : PLAYLIST_HOLDER_TYPE;
case COMMENT:
return useMiniVariant ? MINI_COMMENT_HOLDER_TYPE : COMMENT_HOLDER_TYPE;
default:
Log.e(TAG, "Trollolo");
return -1;
@@ -218,7 +242,8 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int type) {
if (DEBUG) Log.d(TAG, "onCreateViewHolder() called with: parent = [" + parent + "], type = [" + type + "]");
if (DEBUG)
Log.d(TAG, "onCreateViewHolder() called with: parent = [" + parent + "], type = [" + type + "]");
switch (type) {
case HEADER_TYPE:
return new HFHolder(header);
@@ -228,17 +253,27 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
return new StreamMiniInfoItemHolder(infoItemBuilder, parent);
case STREAM_HOLDER_TYPE:
return new StreamInfoItemHolder(infoItemBuilder, parent);
case GRID_STREAM_HOLDER_TYPE:
return new StreamGridInfoItemHolder(infoItemBuilder, parent);
case MINI_CHANNEL_HOLDER_TYPE:
return new ChannelMiniInfoItemHolder(infoItemBuilder, parent);
case CHANNEL_HOLDER_TYPE:
return new ChannelInfoItemHolder(infoItemBuilder, parent);
case GRID_CHANNEL_HOLDER_TYPE:
return new ChannelGridInfoItemHolder(infoItemBuilder, parent);
case MINI_PLAYLIST_HOLDER_TYPE:
return new PlaylistMiniInfoItemHolder(infoItemBuilder, parent);
case PLAYLIST_HOLDER_TYPE:
return new PlaylistInfoItemHolder(infoItemBuilder, parent);
case GRID_PLAYLIST_HOLDER_TYPE:
return new PlaylistGridInfoItemHolder(infoItemBuilder, parent);
case MINI_COMMENT_HOLDER_TYPE:
return new CommentsMiniInfoItemHolder(infoItemBuilder, parent);
case COMMENT_HOLDER_TYPE:
return new CommentsInfoItemHolder(infoItemBuilder, parent);
default:
Log.e(TAG, "Trollolo");
return null;
return new FallbackViewHolder(new View(parent.getContext()));
}
}
@@ -256,4 +291,14 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
((HFHolder) holder).view = footer;
}
}
public GridLayoutManager.SpanSizeLookup getSpanSizeLookup(final int spanCount) {
return new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
final int type = getItemViewType(position);
return type == HEADER_TYPE || type == FOOTER_TYPE ? spanCount : 1;
}
};
}
}

View File

@@ -0,0 +1,13 @@
package org.schabi.newpipe.info_list.holder;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.info_list.InfoItemBuilder;
public class ChannelGridInfoItemHolder extends ChannelMiniInfoItemHolder {
public ChannelGridInfoItemHolder(InfoItemBuilder infoItemBuilder, ViewGroup parent) {
super(infoItemBuilder, R.layout.list_channel_grid_item, parent);
}
}

View File

@@ -47,6 +47,13 @@ public class ChannelMiniInfoItemHolder extends InfoItemHolder {
itemBuilder.getOnChannelSelectedListener().selected(item);
}
});
itemView.setOnLongClickListener(view -> {
if (itemBuilder.getOnChannelSelectedListener() != null) {
itemBuilder.getOnChannelSelectedListener().held(item);
}
return true;
});
}
protected String getDetailLine(final ChannelInfoItem item) {

View File

@@ -0,0 +1,53 @@
package org.schabi.newpipe.info_list.holder;
import android.view.ViewGroup;
import android.widget.TextView;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
import org.schabi.newpipe.info_list.InfoItemBuilder;
import org.schabi.newpipe.util.Localization;
/*
* Created by Christian Schabesberger on 12.02.17.
*
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
* ChannelInfoItemHolder .java is part of NewPipe.
*
* NewPipe is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* NewPipe is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
public class CommentsInfoItemHolder extends CommentsMiniInfoItemHolder {
public final TextView itemTitleView;
public CommentsInfoItemHolder(InfoItemBuilder infoItemBuilder, ViewGroup parent) {
super(infoItemBuilder, R.layout.list_comments_item, parent);
itemTitleView = itemView.findViewById(R.id.itemTitleView);
}
@Override
public void updateFromItem(final InfoItem infoItem) {
super.updateFromItem(infoItem);
if (!(infoItem instanceof CommentsInfoItem)) return;
final CommentsInfoItem item = (CommentsInfoItem) infoItem;
itemTitleView.setText(item.getAuthorName());
}
}

View File

@@ -0,0 +1,99 @@
package org.schabi.newpipe.info_list.holder;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
import org.schabi.newpipe.info_list.InfoItemBuilder;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.util.ImageDisplayConstants;
import org.schabi.newpipe.util.NavigationHelper;
import de.hdodenhof.circleimageview.CircleImageView;
public class CommentsMiniInfoItemHolder extends InfoItemHolder {
public final CircleImageView itemThumbnailView;
private final TextView itemContentView;
private final TextView itemLikesCountView;
private final TextView itemDislikesCountView;
private final TextView itemPublishedTime;
private static final int commentDefaultLines = 2;
private static final int commentExpandedLines = 1000;
CommentsMiniInfoItemHolder(InfoItemBuilder infoItemBuilder, int layoutId, ViewGroup parent) {
super(infoItemBuilder, layoutId, parent);
itemThumbnailView = itemView.findViewById(R.id.itemThumbnailView);
itemLikesCountView = itemView.findViewById(R.id.detail_thumbs_up_count_view);
itemDislikesCountView = itemView.findViewById(R.id.detail_thumbs_down_count_view);
itemPublishedTime = itemView.findViewById(R.id.itemPublishedTime);
itemContentView = itemView.findViewById(R.id.itemCommentContentView);
}
public CommentsMiniInfoItemHolder(InfoItemBuilder infoItemBuilder, ViewGroup parent) {
this(infoItemBuilder, R.layout.list_comments_mini_item, parent);
}
@Override
public void updateFromItem(final InfoItem infoItem) {
if (!(infoItem instanceof CommentsInfoItem)) return;
final CommentsInfoItem item = (CommentsInfoItem) infoItem;
itemBuilder.getImageLoader()
.displayImage(item.getAuthorThumbnail(),
itemThumbnailView,
ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS);
itemThumbnailView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
try {
final AppCompatActivity activity = (AppCompatActivity) itemBuilder.getContext();
NavigationHelper.openChannelFragment(
activity.getSupportFragmentManager(),
item.getServiceId(),
item.getAuthorEndpoint(),
item.getAuthorName());
} catch (Exception e) {
ErrorActivity.reportUiError((AppCompatActivity) itemBuilder.getContext(), e);
}
}
});
// ellipsize if not already ellipsized
if (null == itemContentView.getEllipsize()) {
itemContentView.setEllipsize(TextUtils.TruncateAt.END);
itemContentView.setMaxLines(commentDefaultLines);
}
itemContentView.setText(item.getCommentText());
if (null != item.getLikeCount()) {
itemLikesCountView.setText(String.valueOf(item.getLikeCount()));
}
itemPublishedTime.setText(item.getPublishedTime());
itemView.setOnClickListener(view -> {
toggleEllipsize(item.getCommentText());
if (itemBuilder.getOnCommentsSelectedListener() != null) {
itemBuilder.getOnCommentsSelectedListener().selected(item);
}
});
}
private void toggleEllipsize(String text) {
// toggle ellipsize
if (null == itemContentView.getEllipsize()) {
itemContentView.setEllipsize(TextUtils.TruncateAt.END);
itemContentView.setMaxLines(commentDefaultLines);
} else {
itemContentView.setEllipsize(null);
itemContentView.setMaxLines(commentExpandedLines);
}
}
}

View File

@@ -0,0 +1,13 @@
package org.schabi.newpipe.info_list.holder;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.info_list.InfoItemBuilder;
public class PlaylistGridInfoItemHolder extends PlaylistMiniInfoItemHolder {
public PlaylistGridInfoItemHolder(InfoItemBuilder infoItemBuilder, ViewGroup parent) {
super(infoItemBuilder, R.layout.list_playlist_grid_item, parent);
}
}

View File

@@ -0,0 +1,13 @@
package org.schabi.newpipe.info_list.holder;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.info_list.InfoItemBuilder;
public class StreamGridInfoItemHolder extends StreamMiniInfoItemHolder {
public StreamGridInfoItemHolder(InfoItemBuilder infoItemBuilder, ViewGroup parent) {
super(infoItemBuilder, R.layout.list_stream_grid_item, parent);
}
}

View File

@@ -1,8 +1,13 @@
package org.schabi.newpipe.fragments.local.bookmark;
package org.schabi.newpipe.local;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
@@ -13,7 +18,6 @@ import android.view.View;
import org.schabi.newpipe.R;
import org.schabi.newpipe.fragments.BaseStateFragment;
import org.schabi.newpipe.fragments.list.ListViewContract;
import org.schabi.newpipe.fragments.local.LocalItemListAdapter;
import static org.schabi.newpipe.util.AnimationUtils.animateView;
@@ -26,7 +30,7 @@ import static org.schabi.newpipe.util.AnimationUtils.animateView;
* called and is memory efficient when in backstack.
* */
public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
implements ListViewContract<I, N> {
implements ListViewContract<I, N>, SharedPreferences.OnSharedPreferenceChangeListener {
/*//////////////////////////////////////////////////////////////////////////
// Views
@@ -37,6 +41,9 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
protected LocalItemListAdapter itemListAdapter;
protected RecyclerView itemsList;
private int updateFlags = 0;
private static final int LIST_MODE_UPDATE_FLAG = 0x32;
/*//////////////////////////////////////////////////////////////////////////
// Lifecycle - Creation
@@ -46,6 +53,29 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
PreferenceManager.getDefaultSharedPreferences(activity)
.registerOnSharedPreferenceChangeListener(this);
}
@Override
public void onDestroy() {
super.onDestroy();
PreferenceManager.getDefaultSharedPreferences(activity)
.unregisterOnSharedPreferenceChangeListener(this);
}
@Override
public void onResume() {
super.onResume();
if (updateFlags != 0) {
if ((updateFlags & LIST_MODE_UPDATE_FLAG) != 0) {
final boolean useGrid = isGridLayout();
itemsList.setLayoutManager(useGrid ? getGridLayoutManager() : getListLayoutManager());
itemListAdapter.setGridItemVariants(useGrid);
itemListAdapter.notifyDataSetChanged();
}
updateFlags = 0;
}
}
/*//////////////////////////////////////////////////////////////////////////
@@ -60,6 +90,16 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
return activity.getLayoutInflater().inflate(R.layout.pignate_footer, itemsList, false);
}
protected RecyclerView.LayoutManager getGridLayoutManager() {
final Resources resources = activity.getResources();
int width = resources.getDimensionPixelSize(R.dimen.video_item_grid_thumbnail_image_width);
width += (24 * resources.getDisplayMetrics().density);
final int spanCount = (int) Math.floor(resources.getDisplayMetrics().widthPixels / (double)width);
final GridLayoutManager lm = new GridLayoutManager(activity, spanCount);
lm.setSpanSizeLookup(itemListAdapter.getSpanSizeLookup(spanCount));
return lm;
}
protected RecyclerView.LayoutManager getListLayoutManager() {
return new LinearLayoutManager(activity);
}
@@ -68,10 +108,13 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
protected void initViews(View rootView, Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState);
itemsList = rootView.findViewById(R.id.items_list);
itemsList.setLayoutManager(getListLayoutManager());
itemListAdapter = new LocalItemListAdapter(activity);
final boolean useGrid = isGridLayout();
itemsList = rootView.findViewById(R.id.items_list);
itemsList.setLayoutManager(useGrid ? getGridLayoutManager() : getListLayoutManager());
itemListAdapter.setGridItemVariants(useGrid);
itemListAdapter.setHeader(headerRootView = getListHeader());
itemListAdapter.setFooter(footerRootView = getListFooter());
@@ -175,4 +218,22 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
resetFragment();
return super.onError(exception);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals(getString(R.string.list_view_mode_key))) {
updateFlags |= LIST_MODE_UPDATE_FLAG;
}
}
protected boolean isGridLayout() {
final String list_mode = PreferenceManager.getDefaultSharedPreferences(activity).getString(getString(R.string.list_view_mode_key), getString(R.string.list_view_mode_value));
if ("auto".equals(list_mode)) {
final Configuration configuration = getResources().getConfiguration();
return configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
&& configuration.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE);
} else {
return "grid".equals(list_mode);
}
}
}

View File

@@ -1,4 +1,4 @@
package org.schabi.newpipe.fragments.local;
package org.schabi.newpipe.local;
import android.support.v7.widget.RecyclerView;
import android.view.View;

View File

@@ -1,4 +1,4 @@
package org.schabi.newpipe.fragments.local;
package org.schabi.newpipe.local;
import android.content.Context;
import android.widget.ImageView;
@@ -33,7 +33,7 @@ public class LocalItemBuilder {
private static final String TAG = LocalItemBuilder.class.toString();
private final Context context;
private ImageLoader imageLoader = ImageLoader.getInstance();
private final ImageLoader imageLoader = ImageLoader.getInstance();
private OnClickGesture<LocalItem> onSelectedListener;

View File

@@ -1,17 +1,23 @@
package org.schabi.newpipe.fragments.local;
package org.schabi.newpipe.local;
import android.app.Activity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.fragments.local.holder.LocalItemHolder;
import org.schabi.newpipe.fragments.local.holder.LocalPlaylistItemHolder;
import org.schabi.newpipe.fragments.local.holder.LocalPlaylistStreamItemHolder;
import org.schabi.newpipe.fragments.local.holder.LocalStatisticStreamItemHolder;
import org.schabi.newpipe.fragments.local.holder.RemotePlaylistItemHolder;
import org.schabi.newpipe.local.holder.LocalItemHolder;
import org.schabi.newpipe.local.holder.LocalPlaylistGridItemHolder;
import org.schabi.newpipe.local.holder.LocalPlaylistItemHolder;
import org.schabi.newpipe.local.holder.LocalPlaylistStreamGridItemHolder;
import org.schabi.newpipe.local.holder.LocalPlaylistStreamItemHolder;
import org.schabi.newpipe.local.holder.LocalStatisticStreamGridItemHolder;
import org.schabi.newpipe.local.holder.LocalStatisticStreamItemHolder;
import org.schabi.newpipe.local.holder.RemotePlaylistGridItemHolder;
import org.schabi.newpipe.local.holder.RemotePlaylistItemHolder;
import org.schabi.newpipe.util.FallbackViewHolder;
import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.OnClickGesture;
@@ -49,14 +55,19 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
private static final int STREAM_STATISTICS_HOLDER_TYPE = 0x1000;
private static final int STREAM_PLAYLIST_HOLDER_TYPE = 0x1001;
private static final int STREAM_STATISTICS_GRID_HOLDER_TYPE = 0x1002;
private static final int STREAM_PLAYLIST_GRID_HOLDER_TYPE = 0x1004;
private static final int LOCAL_PLAYLIST_HOLDER_TYPE = 0x2000;
private static final int REMOTE_PLAYLIST_HOLDER_TYPE = 0x2001;
private static final int LOCAL_PLAYLIST_GRID_HOLDER_TYPE = 0x2002;
private static final int REMOTE_PLAYLIST_GRID_HOLDER_TYPE = 0x2004;
private final LocalItemBuilder localItemBuilder;
private final ArrayList<LocalItem> localItems;
private final DateFormat dateFormat;
private boolean showFooter = false;
private boolean useGridVariant = false;
private View header = null;
private View footer = null;
@@ -131,6 +142,10 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
notifyDataSetChanged();
}
public void setGridItemVariants(boolean useGridVariant) {
this.useGridVariant = useGridVariant;
}
public void setHeader(View header) {
boolean changed = header != this.header;
this.header = header;
@@ -192,11 +207,11 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
final LocalItem item = localItems.get(position);
switch (item.getLocalItemType()) {
case PLAYLIST_LOCAL_ITEM: return LOCAL_PLAYLIST_HOLDER_TYPE;
case PLAYLIST_REMOTE_ITEM: return REMOTE_PLAYLIST_HOLDER_TYPE;
case PLAYLIST_LOCAL_ITEM: return useGridVariant ? LOCAL_PLAYLIST_GRID_HOLDER_TYPE : LOCAL_PLAYLIST_HOLDER_TYPE;
case PLAYLIST_REMOTE_ITEM: return useGridVariant ? REMOTE_PLAYLIST_GRID_HOLDER_TYPE : REMOTE_PLAYLIST_HOLDER_TYPE;
case PLAYLIST_STREAM_ITEM: return STREAM_PLAYLIST_HOLDER_TYPE;
case STATISTIC_STREAM_ITEM: return STREAM_STATISTICS_HOLDER_TYPE;
case PLAYLIST_STREAM_ITEM: return useGridVariant ? STREAM_PLAYLIST_GRID_HOLDER_TYPE : STREAM_PLAYLIST_HOLDER_TYPE;
case STATISTIC_STREAM_ITEM: return useGridVariant ? STREAM_STATISTICS_GRID_HOLDER_TYPE : STREAM_STATISTICS_HOLDER_TYPE;
default:
Log.e(TAG, "No holder type has been considered for item: [" +
item.getLocalItemType() + "]");
@@ -215,15 +230,23 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
return new HeaderFooterHolder(footer);
case LOCAL_PLAYLIST_HOLDER_TYPE:
return new LocalPlaylistItemHolder(localItemBuilder, parent);
case LOCAL_PLAYLIST_GRID_HOLDER_TYPE:
return new LocalPlaylistGridItemHolder(localItemBuilder, parent);
case REMOTE_PLAYLIST_HOLDER_TYPE:
return new RemotePlaylistItemHolder(localItemBuilder, parent);
case REMOTE_PLAYLIST_GRID_HOLDER_TYPE:
return new RemotePlaylistGridItemHolder(localItemBuilder, parent);
case STREAM_PLAYLIST_HOLDER_TYPE:
return new LocalPlaylistStreamItemHolder(localItemBuilder, parent);
case STREAM_PLAYLIST_GRID_HOLDER_TYPE:
return new LocalPlaylistStreamGridItemHolder(localItemBuilder, parent);
case STREAM_STATISTICS_HOLDER_TYPE:
return new LocalStatisticStreamItemHolder(localItemBuilder, parent);
case STREAM_STATISTICS_GRID_HOLDER_TYPE:
return new LocalStatisticStreamGridItemHolder(localItemBuilder, parent);
default:
Log.e(TAG, "No view type has been considered for holder: [" + type + "]");
return null;
return new FallbackViewHolder(new View(parent.getContext()));
}
}
@@ -244,4 +267,14 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
((HeaderFooterHolder) holder).view = footer;
}
}
public GridLayoutManager.SpanSizeLookup getSpanSizeLookup(final int spanCount) {
return new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
final int type = getItemViewType(position);
return type == HEADER_TYPE || type == FOOTER_TYPE ? spanCount : 1;
}
};
}
}

View File

@@ -1,4 +1,4 @@
package org.schabi.newpipe.fragments.local.bookmark;
package org.schabi.newpipe.local.bookmark;
import android.app.AlertDialog;
import android.os.Bundle;
@@ -19,8 +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.fragments.local.LocalPlaylistManager;
import org.schabi.newpipe.fragments.local.RemotePlaylistManager;
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.UserAction;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.OnClickGesture;
@@ -38,9 +39,6 @@ import io.reactivex.disposables.CompositeDisposable;
public final class BookmarkFragment
extends BaseLocalListFragment<List<PlaylistLocalItem>, Void> {
private View lastPlayedButton;
private View mostPlayedButton;
@State
protected Parcelable itemsListState;
@@ -56,7 +54,8 @@ public final class BookmarkFragment
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final AppDatabase database = NewPipeDatabase.getInstance(getContext());
if (activity == null) return;
final AppDatabase database = NewPipeDatabase.getInstance(activity);
localPlaylistManager = new LocalPlaylistManager(database);
remotePlaylistManager = new RemotePlaylistManager(database);
disposables = new CompositeDisposable();
@@ -67,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);
}
@@ -93,15 +91,6 @@ public final class BookmarkFragment
super.initViews(rootView, savedInstanceState);
}
@Override
protected View getListHeader() {
final View headerRootLayout = activity.getLayoutInflater()
.inflate(R.layout.bookmark_header, itemsList, false);
lastPlayedButton = headerRootLayout.findViewById(R.id.lastPlayed);
mostPlayedButton = headerRootLayout.findViewById(R.id.mostPlayed);
return headerRootLayout;
}
@Override
protected void initListeners() {
super.initListeners();
@@ -109,9 +98,7 @@ public final class BookmarkFragment
itemListAdapter.setSelectedListener(new OnClickGesture<LocalItem>() {
@Override
public void selected(LocalItem selectedItem) {
// 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);
@@ -120,8 +107,11 @@ public final class BookmarkFragment
} else if (selectedItem instanceof PlaylistRemoteEntity) {
final PlaylistRemoteEntity entry = ((PlaylistRemoteEntity) selectedItem);
NavigationHelper.openPlaylistFragment(fragmentManager, entry.getServiceId(),
entry.getUrl(), entry.getName());
NavigationHelper.openPlaylistFragment(
fragmentManager,
entry.getServiceId(),
entry.getUrl(),
entry.getName());
}
}
@@ -135,18 +125,6 @@ public final class BookmarkFragment
}
}
});
lastPlayedButton.setOnClickListener(view -> {
if (getParentFragment() != null) {
NavigationHelper.openLastPlayedFragment(getParentFragment().getFragmentManager());
}
});
mostPlayedButton.setOnClickListener(view -> {
if (getParentFragment() != null) {
NavigationHelper.openMostPlayedFragment(getParentFragment().getFragmentManager());
}
});
}
///////////////////////////////////////////////////////////////////////////
@@ -179,8 +157,6 @@ public final class BookmarkFragment
@Override
public void onDestroyView() {
super.onDestroyView();
if (mostPlayedButton != null) mostPlayedButton.setOnClickListener(null);
if (lastPlayedButton != null) lastPlayedButton.setOnClickListener(null);
if (disposables != null) disposables.clear();
if (databaseSubscription != null) databaseSubscription.cancel();

View File

@@ -1,4 +1,4 @@
package org.schabi.newpipe.fragments.local.dialog;
package org.schabi.newpipe.local.dialog;
import android.annotation.SuppressLint;
import android.os.Bundle;
@@ -18,9 +18,9 @@ import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
import org.schabi.newpipe.database.stream.model.StreamEntity;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.fragments.local.LocalItemListAdapter;
import org.schabi.newpipe.fragments.local.LocalPlaylistManager;
import org.schabi.newpipe.playlist.PlayQueueItem;
import org.schabi.newpipe.local.LocalItemListAdapter;
import org.schabi.newpipe.local.playlist.LocalPlaylistManager;
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import org.schabi.newpipe.util.OnClickGesture;
import java.util.ArrayList;

View File

@@ -1,4 +1,4 @@
package org.schabi.newpipe.fragments.local.dialog;
package org.schabi.newpipe.local.dialog;
import android.app.AlertDialog;
import android.app.Dialog;
@@ -12,7 +12,7 @@ import android.widget.Toast;
import org.schabi.newpipe.NewPipeDatabase;
import org.schabi.newpipe.R;
import org.schabi.newpipe.database.stream.model.StreamEntity;
import org.schabi.newpipe.fragments.local.LocalPlaylistManager;
import org.schabi.newpipe.local.playlist.LocalPlaylistManager;
import java.util.List;

View File

@@ -1,9 +1,11 @@
package org.schabi.newpipe.fragments.local.dialog;
package org.schabi.newpipe.local.dialog;
import android.app.Dialog;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.view.Window;
import org.schabi.newpipe.database.stream.model.StreamEntity;
import org.schabi.newpipe.util.StateSaver;
@@ -41,6 +43,18 @@ public abstract class PlaylistDialog extends DialogFragment implements StateSave
StateSaver.onDestroy(savedState);
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Dialog dialog = super.onCreateDialog(savedInstanceState);
//remove title
final Window window = dialog.getWindow();
if (window != null) {
window.requestFeature(Window.FEATURE_NO_TITLE);
}
return dialog;
}
/*//////////////////////////////////////////////////////////////////////////
// State Saving
//////////////////////////////////////////////////////////////////////////*/
@@ -58,7 +72,7 @@ public abstract class PlaylistDialog extends DialogFragment implements StateSave
@Override
@SuppressWarnings("unchecked")
public void readFrom(@NonNull Queue<Object> savedObjects) throws Exception {
public void readFrom(@NonNull Queue<Object> savedObjects) {
streamEntities = (List<StreamEntity>) savedObjects.poll();
}

View File

@@ -1,4 +1,4 @@
package org.schabi.newpipe.fragments.list.feed;
package org.schabi.newpipe.local.feed;
import android.os.Bundle;
import android.os.Handler;
@@ -22,7 +22,7 @@ import org.schabi.newpipe.extractor.channel.ChannelInfo;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.fragments.list.BaseListFragment;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.subscription.SubscriptionService;
import org.schabi.newpipe.local.subscription.SubscriptionService;
import java.util.Collections;
import java.util.HashSet;
@@ -36,8 +36,6 @@ import io.reactivex.MaybeObserver;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Predicate;
public class FeedFragment extends BaseListFragment<List<SubscriptionEntity>, Void> {
@@ -71,6 +69,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 +107,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 +177,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 +230,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 +306,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 +354,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 +411,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 +431,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

@@ -1,7 +1,6 @@
package org.schabi.newpipe.history;
package org.schabi.newpipe.local.history;
import android.content.Context;
import android.content.res.Resources;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;

View File

@@ -1,4 +1,4 @@
package org.schabi.newpipe.history;
package org.schabi.newpipe.local.history;
import android.support.annotation.Nullable;

View File

@@ -1,4 +1,22 @@
package org.schabi.newpipe.history;
package org.schabi.newpipe.local.history;
/*
* Copyright (C) Mauricio Colli 2018
* HistoryRecordManager.java is part of NewPipe.
*
* NewPipe is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* NewPipe is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
import android.content.Context;
import android.content.SharedPreferences;
@@ -80,6 +98,11 @@ public class HistoryRecordManager {
.subscribeOn(Schedulers.io());
}
public Single<Integer> deleteWholeStreamHistory() {
return Single.fromCallable(() -> streamHistoryTable.deleteAll())
.subscribeOn(Schedulers.io());
}
public Flowable<List<StreamHistoryEntry>> getStreamHistory() {
return streamHistoryTable.getHistory().subscribeOn(Schedulers.io());
}
@@ -114,20 +137,6 @@ public class HistoryRecordManager {
// Search History
///////////////////////////////////////////////////////
public Single<List<Long>> insertSearches(final Collection<SearchHistoryEntry> entries) {
return Single.fromCallable(() -> searchHistoryTable.insertAll(entries))
.subscribeOn(Schedulers.io());
}
public Single<Integer> deleteSearches(final Collection<SearchHistoryEntry> entries) {
return Single.fromCallable(() -> searchHistoryTable.delete(entries))
.subscribeOn(Schedulers.io());
}
public Flowable<List<SearchHistoryEntry>> getSearchHistory() {
return searchHistoryTable.getAll();
}
public Maybe<Long> onSearched(final int serviceId, final String search) {
if (!isSearchHistoryEnabled()) return Maybe.empty();
@@ -150,6 +159,11 @@ public class HistoryRecordManager {
.subscribeOn(Schedulers.io());
}
public Single<Integer> deleteWholeSearchHistory() {
return Single.fromCallable(() -> searchHistoryTable.deleteAll())
.subscribeOn(Schedulers.io());
}
public Flowable<List<SearchHistoryEntry>> getRelatedSearches(final String query,
final int similarQueryLimit,
final int uniqueQueryLimit) {

View File

@@ -1,4 +1,4 @@
package org.schabi.newpipe.fragments.local.bookmark;
package org.schabi.newpipe.local.history;
import android.app.Activity;
import android.content.Context;
@@ -7,9 +7,13 @@ import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
@@ -17,13 +21,14 @@ 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.history.HistoryRecordManager;
import org.schabi.newpipe.local.BaseLocalListFragment;
import org.schabi.newpipe.info_list.InfoItemDialog;
import org.schabi.newpipe.playlist.PlayQueue;
import org.schabi.newpipe.playlist.SinglePlayQueue;
import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.OnClickGesture;
import org.schabi.newpipe.util.ThemeHelper;
import java.util.ArrayList;
import java.util.Collections;
@@ -31,13 +36,19 @@ import java.util.List;
import icepick.State;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
public abstract class StatisticsPlaylistFragment
public class StatisticsPlaylistFragment
extends BaseLocalListFragment<List<StreamStatisticsEntry>, Void> {
private View headerPlayAllButton;
private View headerPopupButton;
private View headerBackgroundButton;
private View playlistCtrl;
private View sortButton;
private ImageView sortButtonIcon;
private TextView sortButtonText;
@State
protected Parcelable itemsListState;
@@ -45,14 +56,28 @@ public abstract class StatisticsPlaylistFragment
/* Used for independent events */
private Subscription databaseSubscription;
private HistoryRecordManager recordManager;
private final CompositeDisposable disposables = new CompositeDisposable();
///////////////////////////////////////////////////////////////////////////
// Abstracts
///////////////////////////////////////////////////////////////////////////
private enum StatisticSortMode {
LAST_PLAYED,
MOST_PLAYED,
}
protected abstract String getName();
StatisticSortMode sortMode = StatisticSortMode.LAST_PLAYED;
protected abstract List<StreamStatisticsEntry> processResult(final List<StreamStatisticsEntry> results);
protected List<StreamStatisticsEntry> processResult(final List<StreamStatisticsEntry> results) {
switch (sortMode) {
case LAST_PLAYED:
Collections.sort(results, (left, right) ->
right.latestAccessDate.compareTo(left.latestAccessDate));
return results;
case MOST_PLAYED:
Collections.sort(results, (left, right) ->
Long.compare(right.watchCount, left.watchCount));
return results;
default: return null;
}
}
///////////////////////////////////////////////////////////////////////////
// Fragment LifeCycle - Creation
@@ -71,6 +96,14 @@ public abstract 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
///////////////////////////////////////////////////////////////////////////
@@ -78,16 +111,22 @@ public abstract class StatisticsPlaylistFragment
@Override
protected void initViews(View rootView, Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState);
setTitle(getName());
if(!useAsFrontPage) {
setTitle(getString(R.string.title_last_played));
}
}
@Override
protected View getListHeader() {
final View headerRootLayout = activity.getLayoutInflater().inflate(R.layout.playlist_control,
final View headerRootLayout = activity.getLayoutInflater().inflate(R.layout.statistic_playlist_control,
itemsList, false);
playlistCtrl = headerRootLayout.findViewById(R.id.playlist_control);
headerPlayAllButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_all_button);
headerPopupButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_popup_button);
headerBackgroundButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_bg_button);
sortButton = headerRootLayout.findViewById(R.id.sortButton);
sortButtonIcon = headerRootLayout.findViewById(R.id.sortButtonIcon);
sortButtonText = headerRootLayout.findViewById(R.id.sortButtonText);
return headerRootLayout;
}
@@ -100,8 +139,10 @@ public abstract 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);
}
}
@@ -193,6 +234,8 @@ public abstract class StatisticsPlaylistFragment
super.handleResult(result);
if (itemListAdapter == null) return;
playlistCtrl.setVisibility(View.VISIBLE);
itemListAdapter.clearStreamItemList();
if (result.isEmpty()) {
@@ -212,6 +255,7 @@ public abstract class StatisticsPlaylistFragment
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue()));
headerBackgroundButton.setOnClickListener(view ->
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue()));
sortButton.setOnClickListener(view -> toggleSortMode());
hideLoading();
}
@@ -238,6 +282,21 @@ public abstract class StatisticsPlaylistFragment
// Utils
//////////////////////////////////////////////////////////////////////////*/
private void toggleSortMode() {
if(sortMode == StatisticSortMode.LAST_PLAYED) {
sortMode = StatisticSortMode.MOST_PLAYED;
setTitle(getString(R.string.title_most_played));
sortButtonIcon.setImageResource(ThemeHelper.getIconByAttr(R.attr.history, getContext()));
sortButtonText.setText(R.string.title_last_played);
} else {
sortMode = StatisticSortMode.LAST_PLAYED;
setTitle(getString(R.string.title_last_played));
sortButtonIcon.setImageResource(ThemeHelper.getIconByAttr(R.attr.filter, getContext()));
sortButtonText.setText(R.string.title_most_played);
}
startLoading(true);
}
private void showStreamDialog(final StreamStatisticsEntry item) {
final Context context = getContext();
final Activity activity = getActivity();
@@ -250,6 +309,8 @@ public abstract class StatisticsPlaylistFragment
context.getResources().getString(R.string.start_here_on_main),
context.getResources().getString(R.string.start_here_on_background),
context.getResources().getString(R.string.start_here_on_popup),
context.getResources().getString(R.string.delete),
context.getResources().getString(R.string.share)
};
final DialogInterface.OnClickListener actions = (dialogInterface, i) -> {
@@ -270,6 +331,12 @@ public abstract class StatisticsPlaylistFragment
case 4:
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index));
break;
case 5:
deleteEntry(index);
break;
case 6:
shareUrl(item.toStreamInfoItem().getName(), item.toStreamInfoItem().getUrl());
break;
default:
break;
}
@@ -278,6 +345,32 @@ public abstract class StatisticsPlaylistFragment
new InfoItemDialog(getActivity(), infoItem, commands, actions).show();
}
private void deleteEntry(final int index) {
final LocalItem infoItem = itemListAdapter.getItemsList()
.get(index);
if(infoItem instanceof StreamStatisticsEntry) {
final StreamStatisticsEntry entry = (StreamStatisticsEntry) infoItem;
final Disposable onDelete = recordManager.deleteStreamHistory(entry.streamId)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
howManyDeleted -> {
if(getView() != null) {
Snackbar.make(getView(), R.string.one_item_deleted,
Snackbar.LENGTH_SHORT).show();
} else {
Toast.makeText(getContext(),
R.string.one_item_deleted,
Toast.LENGTH_SHORT).show();
}
},
throwable -> showSnackBarError(throwable,
UserAction.DELETE_FROM_HISTORY, "none",
"Deleting item failed", R.string.general_error));
disposables.add(onDelete);
}
}
private PlayQueue getPlayQueue() {
return getPlayQueue(0);
}

View File

@@ -1,11 +1,11 @@
package org.schabi.newpipe.fragments.local.holder;
package org.schabi.newpipe.local.holder;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.fragments.local.LocalItemBuilder;
import org.schabi.newpipe.local.LocalItemBuilder;
import java.text.DateFormat;

View File

@@ -0,0 +1,13 @@
package org.schabi.newpipe.local.holder;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.local.LocalItemBuilder;
public class LocalPlaylistGridItemHolder extends LocalPlaylistItemHolder {
public LocalPlaylistGridItemHolder(LocalItemBuilder infoItemBuilder, ViewGroup parent) {
super(infoItemBuilder, R.layout.list_playlist_grid_item, parent);
}
}

View File

@@ -1,11 +1,11 @@
package org.schabi.newpipe.fragments.local.holder;
package org.schabi.newpipe.local.holder;
import android.view.View;
import android.view.ViewGroup;
import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
import org.schabi.newpipe.fragments.local.LocalItemBuilder;
import org.schabi.newpipe.local.LocalItemBuilder;
import org.schabi.newpipe.util.ImageDisplayConstants;
import java.text.DateFormat;
@@ -16,6 +16,10 @@ public class LocalPlaylistItemHolder extends PlaylistItemHolder {
super(infoItemBuilder, parent);
}
LocalPlaylistItemHolder(LocalItemBuilder infoItemBuilder, int layoutId, ViewGroup parent) {
super(infoItemBuilder, layoutId, parent);
}
@Override
public void updateFromItem(final LocalItem localItem, final DateFormat dateFormat) {
if (!(localItem instanceof PlaylistMetadataEntry)) return;

View File

@@ -0,0 +1,13 @@
package org.schabi.newpipe.local.holder;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.local.LocalItemBuilder;
public class LocalPlaylistStreamGridItemHolder extends LocalPlaylistStreamItemHolder {
public LocalPlaylistStreamGridItemHolder(LocalItemBuilder infoItemBuilder, ViewGroup parent) {
super(infoItemBuilder, R.layout.list_stream_playlist_grid_item, parent); //TODO
}
}

View File

@@ -1,4 +1,4 @@
package org.schabi.newpipe.fragments.local.holder;
package org.schabi.newpipe.local.holder;
import android.support.v4.content.ContextCompat;
import android.view.MotionEvent;
@@ -11,7 +11,7 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.database.playlist.PlaylistStreamEntry;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.fragments.local.LocalItemBuilder;
import org.schabi.newpipe.local.LocalItemBuilder;
import org.schabi.newpipe.util.ImageDisplayConstants;
import org.schabi.newpipe.util.Localization;

View File

@@ -0,0 +1,13 @@
package org.schabi.newpipe.local.holder;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.local.LocalItemBuilder;
public class LocalStatisticStreamGridItemHolder extends LocalStatisticStreamItemHolder {
public LocalStatisticStreamGridItemHolder(LocalItemBuilder infoItemBuilder, ViewGroup parent) {
super(infoItemBuilder, R.layout.list_stream_grid_item, parent);
}
}

View File

@@ -1,5 +1,6 @@
package org.schabi.newpipe.fragments.local.holder;
package org.schabi.newpipe.local.holder;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.view.View;
import android.view.ViewGroup;
@@ -10,7 +11,7 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.database.stream.StreamStatisticsEntry;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.fragments.local.LocalItemBuilder;
import org.schabi.newpipe.local.LocalItemBuilder;
import org.schabi.newpipe.util.ImageDisplayConstants;
import org.schabi.newpipe.util.Localization;
@@ -42,10 +43,15 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder {
public final TextView itemVideoTitleView;
public final TextView itemUploaderView;
public final TextView itemDurationView;
@Nullable
public final TextView itemAdditionalDetails;
public LocalStatisticStreamItemHolder(LocalItemBuilder infoItemBuilder, ViewGroup parent) {
super(infoItemBuilder, R.layout.list_stream_item, parent);
public LocalStatisticStreamItemHolder(LocalItemBuilder itemBuilder, ViewGroup parent) {
this(itemBuilder, R.layout.list_stream_item, parent);
}
LocalStatisticStreamItemHolder(LocalItemBuilder infoItemBuilder, int layoutId, ViewGroup parent) {
super(infoItemBuilder, layoutId, parent);
itemThumbnailView = itemView.findViewById(R.id.itemThumbnailView);
itemVideoTitleView = itemView.findViewById(R.id.itemVideoTitleView);
@@ -80,7 +86,9 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder {
itemDurationView.setVisibility(View.GONE);
}
itemAdditionalDetails.setText(getStreamInfoDetailLine(item, dateFormat));
if (itemAdditionalDetails != null) {
itemAdditionalDetails.setText(getStreamInfoDetailLine(item, dateFormat));
}
// Default thumbnail is shown on error, while loading and if the url is empty
itemBuilder.displayImage(item.thumbnailUrl, itemThumbnailView,

View File

@@ -1,4 +1,4 @@
package org.schabi.newpipe.fragments.local.holder;
package org.schabi.newpipe.local.holder;
import android.view.ViewGroup;
import android.widget.ImageView;
@@ -6,7 +6,7 @@ import android.widget.TextView;
import org.schabi.newpipe.R;
import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.fragments.local.LocalItemBuilder;
import org.schabi.newpipe.local.LocalItemBuilder;
import java.text.DateFormat;

View File

@@ -0,0 +1,13 @@
package org.schabi.newpipe.local.holder;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.local.LocalItemBuilder;
public class RemotePlaylistGridItemHolder extends RemotePlaylistItemHolder {
public RemotePlaylistGridItemHolder(LocalItemBuilder infoItemBuilder, ViewGroup parent) {
super(infoItemBuilder, R.layout.list_playlist_grid_item, parent);
}
}

View File

@@ -1,11 +1,11 @@
package org.schabi.newpipe.fragments.local.holder;
package org.schabi.newpipe.local.holder;
import android.view.ViewGroup;
import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.fragments.local.LocalItemBuilder;
import org.schabi.newpipe.local.LocalItemBuilder;
import org.schabi.newpipe.util.ImageDisplayConstants;
import org.schabi.newpipe.util.Localization;
@@ -16,6 +16,10 @@ public class RemotePlaylistItemHolder extends PlaylistItemHolder {
super(infoItemBuilder, parent);
}
RemotePlaylistItemHolder(LocalItemBuilder infoItemBuilder, int layoutId, ViewGroup parent) {
super(infoItemBuilder, layoutId, parent);
}
@Override
public void updateFromItem(final LocalItem localItem, final DateFormat dateFormat) {
if (!(localItem instanceof PlaylistRemoteEntity)) return;

View File

@@ -1,4 +1,4 @@
package org.schabi.newpipe.fragments.local.bookmark;
package org.schabi.newpipe.local.playlist;
import android.app.Activity;
import android.content.Context;
@@ -26,10 +26,10 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.database.playlist.PlaylistStreamEntry;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.fragments.local.LocalPlaylistManager;
import org.schabi.newpipe.local.BaseLocalListFragment;
import org.schabi.newpipe.info_list.InfoItemDialog;
import org.schabi.newpipe.playlist.PlayQueue;
import org.schabi.newpipe.playlist.SinglePlayQueue;
import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.NavigationHelper;
@@ -173,7 +173,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
@Override
public void held(LocalItem selectedItem) {
if (selectedItem instanceof PlaylistStreamEntry) {
showStreamDialog((PlaylistStreamEntry) selectedItem);
showStreamItemDialog((PlaylistStreamEntry) selectedItem);
}
}
@@ -459,7 +459,11 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
private ItemTouchHelper.SimpleCallback getItemTouchCallback() {
return new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN,
int directions = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
if (isGridLayout()) {
directions |= ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
}
return new ItemTouchHelper.SimpleCallback(directions,
ItemTouchHelper.ACTION_STATE_IDLE) {
@Override
public int interpolateOutOfBoundsScroll(RecyclerView recyclerView, int viewSize,
@@ -506,7 +510,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
// Utils
//////////////////////////////////////////////////////////////////////////*/
protected void showStreamDialog(final PlaylistStreamEntry item) {
protected void showStreamItemDialog(final PlaylistStreamEntry item) {
final Context context = getContext();
final Activity activity = getActivity();
if (context == null || context.getResources() == null || getActivity() == null) return;
@@ -520,7 +524,8 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
context.getResources().getString(R.string.start_here_on_background),
context.getResources().getString(R.string.start_here_on_popup),
context.getResources().getString(R.string.set_as_playlist_thumbnail),
context.getResources().getString(R.string.delete)
context.getResources().getString(R.string.delete),
context.getResources().getString(R.string.share)
};
final DialogInterface.OnClickListener actions = (dialogInterface, i) -> {
@@ -549,6 +554,9 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
case 6:
deleteItem(item);
break;
case 7:
shareUrl(item.toStreamInfoItem().getName(), item.toStreamInfoItem().getUrl());
break;
default:
break;
}

View File

@@ -1,4 +1,4 @@
package org.schabi.newpipe.fragments.local;
package org.schabi.newpipe.local.playlist;
import android.support.annotation.Nullable;

View File

@@ -1,4 +1,4 @@
package org.schabi.newpipe.fragments.local;
package org.schabi.newpipe.local.playlist;
import org.schabi.newpipe.database.AppDatabase;
import org.schabi.newpipe.database.playlist.dao.PlaylistRemoteDAO;
@@ -13,11 +13,9 @@ import io.reactivex.schedulers.Schedulers;
public class RemotePlaylistManager {
private final AppDatabase database;
private final PlaylistRemoteDAO playlistRemoteTable;
public RemotePlaylistManager(final AppDatabase db) {
database = db;
playlistRemoteTable = db.playlistRemoteDAO();
}
@@ -42,8 +40,11 @@ public class RemotePlaylistManager {
}).subscribeOn(Schedulers.io());
}
public Single<Integer> onUpdate(final PlaylistInfo playlistInfo) {
return Single.fromCallable(() -> playlistRemoteTable.update(new PlaylistRemoteEntity(playlistInfo)))
.subscribeOn(Schedulers.io());
public Single<Integer> onUpdate(final long playlistId, final PlaylistInfo playlistInfo) {
return Single.fromCallable(() -> {
PlaylistRemoteEntity playlist = new PlaylistRemoteEntity(playlistInfo);
playlist.setUid(playlistId);
return playlistRemoteTable.update(playlist);
}).subscribeOn(Schedulers.io());
}
}

View File

@@ -1,4 +1,4 @@
package org.schabi.newpipe.fragments.subscription;
package org.schabi.newpipe.local.subscription;
import android.app.AlertDialog;
import android.app.Dialog;

View File

@@ -1,4 +1,4 @@
package org.schabi.newpipe.subscription;
package org.schabi.newpipe.local.subscription;
public interface ImportExportEventListener {
/**

View File

@@ -17,7 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.schabi.newpipe.subscription;
package org.schabi.newpipe.local.subscription;
import android.support.annotation.Nullable;

View File

@@ -1,20 +1,30 @@
package org.schabi.newpipe.fragments.subscription;
package org.schabi.newpipe.local.subscription;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.os.Bundle;
import android.os.Environment;
import android.os.Parcelable;
import android.preference.PreferenceManager;
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;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
@@ -39,9 +49,10 @@ import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor;
import org.schabi.newpipe.fragments.BaseStateFragment;
import org.schabi.newpipe.info_list.InfoListAdapter;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.subscription.SubscriptionService;
import org.schabi.newpipe.subscription.services.SubscriptionsExportService;
import org.schabi.newpipe.subscription.services.SubscriptionsImportService;
import org.schabi.newpipe.local.subscription.services.SubscriptionsExportService;
import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.FilePickerActivityHelper;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.OnClickGesture;
@@ -53,7 +64,6 @@ import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Locale;
@@ -65,13 +75,13 @@ import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import static org.schabi.newpipe.subscription.services.SubscriptionsImportService.KEY_MODE;
import static org.schabi.newpipe.subscription.services.SubscriptionsImportService.KEY_VALUE;
import static org.schabi.newpipe.subscription.services.SubscriptionsImportService.PREVIOUS_EXPORT_MODE;
import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.KEY_MODE;
import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.KEY_VALUE;
import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.PREVIOUS_EXPORT_MODE;
import static org.schabi.newpipe.util.AnimationUtils.animateRotation;
import static org.schabi.newpipe.util.AnimationUtils.animateView;
public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEntity>> {
public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEntity>> implements SharedPreferences.OnSharedPreferenceChangeListener {
private static final int REQUEST_EXPORT_CODE = 666;
private static final int REQUEST_IMPORT_CODE = 667;
@@ -79,8 +89,10 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
@State
protected Parcelable itemsListState;
private InfoListAdapter infoListAdapter;
private int updateFlags = 0;
private static final int LIST_MODE_UPDATE_FLAG = 0x32;
private View headerRootLayout;
private View whatsNewItemListHeader;
private View importExportListHeader;
@@ -99,6 +111,8 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
PreferenceManager.getDefaultSharedPreferences(activity)
.registerOnSharedPreferenceChangeListener(this);
}
@Override
@@ -126,6 +140,15 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
public void onResume() {
super.onResume();
setupBroadcastReceiver();
if (updateFlags != 0) {
if ((updateFlags & LIST_MODE_UPDATE_FLAG) != 0) {
final boolean useGrid = isGridLayout();
itemsList.setLayoutManager(useGrid ? getGridLayoutManager() : getListLayoutManager());
infoListAdapter.setGridItemVariants(useGrid);
infoListAdapter.notifyDataSetChanged();
}
updateFlags = 0;
}
}
@Override
@@ -152,9 +175,25 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
disposables = null;
subscriptionService = null;
PreferenceManager.getDefaultSharedPreferences(activity)
.unregisterOnSharedPreferenceChangeListener(this);
super.onDestroy();
}
protected RecyclerView.LayoutManager getListLayoutManager() {
return new LinearLayoutManager(activity);
}
protected RecyclerView.LayoutManager getGridLayoutManager() {
final Resources resources = activity.getResources();
int width = resources.getDimensionPixelSize(R.dimen.video_item_grid_thumbnail_image_width);
width += (24 * resources.getDisplayMetrics().density);
final int spanCount = (int) Math.floor(resources.getDisplayMetrics().widthPixels / (double)width);
final GridLayoutManager lm = new GridLayoutManager(activity, spanCount);
lm.setSpanSizeLookup(infoListAdapter.getSpanSizeLookup(spanCount));
return lm;
}
/*/////////////////////////////////////////////////////////////////////////
// Menu
/////////////////////////////////////////////////////////////////////////*/
@@ -209,7 +248,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 +281,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() {
@@ -285,16 +325,19 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
protected void initViews(View rootView, Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState);
final boolean useGrid = isGridLayout();
infoListAdapter = new InfoListAdapter(getActivity());
itemsList = rootView.findViewById(R.id.items_list);
itemsList.setLayoutManager(new LinearLayoutManager(activity));
itemsList.setLayoutManager(useGrid ? getGridLayoutManager() : getListLayoutManager());
View headerRootLayout;
infoListAdapter.setHeader(headerRootLayout = activity.getLayoutInflater().inflate(R.layout.subscription_header, itemsList, false));
whatsNewItemListHeader = headerRootLayout.findViewById(R.id.whats_new);
importExportListHeader = headerRootLayout.findViewById(R.id.import_export);
importExportOptions = headerRootLayout.findViewById(R.id.import_export_options);
infoListAdapter.useMiniItemVariants(true);
infoListAdapter.setGridItemVariants(useGrid);
itemsList.setAdapter(infoListAdapter);
setupImportFromItems(headerRootLayout.findViewById(R.id.import_from_options));
@@ -318,20 +361,108 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
super.initListeners();
infoListAdapter.setOnChannelSelectedListener(new OnClickGesture<ChannelInfoItem>() {
@Override
public void selected(ChannelInfoItem selectedItem) {
// Requires the parent fragment to find holder for fragment replacement
NavigationHelper.openChannelFragment(getParentFragment().getFragmentManager(),
selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());
final FragmentManager fragmentManager = getFM();
NavigationHelper.openChannelFragment(fragmentManager,
selectedItem.getServiceId(),
selectedItem.getUrl(),
selectedItem.getName());
}
public void held(ChannelInfoItem selectedItem) {
showLongTapDialog(selectedItem);
}
});
//noinspection ConstantConditions
whatsNewItemListHeader.setOnClickListener(v ->
NavigationHelper.openWhatsNewFragment(getParentFragment().getFragmentManager()));
whatsNewItemListHeader.setOnClickListener(v -> {
FragmentManager fragmentManager = getFM();
NavigationHelper.openWhatsNewFragment(fragmentManager);
});
importExportListHeader.setOnClickListener(v -> importExportOptions.switchState());
}
private void showLongTapDialog(ChannelInfoItem selectedItem) {
final Context context = getContext();
final Activity activity = getActivity();
if (context == null || context.getResources() == null || getActivity() == null) return;
final String[] commands = new String[]{
context.getResources().getString(R.string.share),
context.getResources().getString(R.string.unsubscribe)
};
final DialogInterface.OnClickListener actions = (dialogInterface, i) -> {
switch (i) {
case 0:
shareChannel(selectedItem);
break;
case 1:
deleteChannel(selectedItem);
break;
default:
break;
}
};
final View bannerView = View.inflate(activity, R.layout.dialog_title, null);
bannerView.setSelected(true);
TextView titleView = bannerView.findViewById(R.id.itemTitleView);
titleView.setText(selectedItem.getName());
TextView detailsView = bannerView.findViewById(R.id.itemAdditionalDetails);
detailsView.setVisibility(View.GONE);
new AlertDialog.Builder(activity)
.setCustomTitle(bannerView)
.setItems(commands, actions)
.create()
.show();
}
private void shareChannel (ChannelInfoItem selectedItem) {
shareUrl(selectedItem.getName(), selectedItem.getUrl());
}
@SuppressLint("CheckResult")
private void deleteChannel (ChannelInfoItem selectedItem) {
subscriptionService.subscriptionTable()
.getSubscription(selectedItem.getServiceId(), selectedItem.getUrl())
.toObservable()
.observeOn(Schedulers.io())
.subscribe(getDeleteObserver());
Toast.makeText(activity, getString(R.string.channel_unsubscribed), Toast.LENGTH_SHORT).show();
}
private Observer<List<SubscriptionEntity>> getDeleteObserver(){
return new Observer<List<SubscriptionEntity>>() {
@Override
public void onSubscribe(Disposable d) {
disposables.add(d);
}
@Override
public void onNext(List<SubscriptionEntity> subscriptionEntities) {
subscriptionService.subscriptionTable().delete(subscriptionEntities);
}
@Override
public void onError(Throwable exception) {
SubscriptionFragment.this.onError(exception);
}
@Override
public void onComplete() { }
};
}
private void resetFragment() {
if (disposables != null) disposables.clear();
if (infoListAdapter != null) infoListAdapter.clearStreamItemList();
@@ -399,10 +530,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;
}
@@ -431,7 +565,29 @@ 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;
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals(getString(R.string.list_view_mode_key))) {
updateFlags |= LIST_MODE_UPDATE_FLAG;
}
}
protected boolean isGridLayout() {
final String list_mode = PreferenceManager.getDefaultSharedPreferences(activity).getString(getString(R.string.list_view_mode_key), getString(R.string.list_view_mode_value));
if ("auto".equals(list_mode)) {
final Configuration configuration = getResources().getConfiguration();
return configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
&& configuration.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE);
} else {
return "grid".equals(list_mode);
}
}
}

View File

@@ -1,4 +1,4 @@
package org.schabi.newpipe.subscription;
package org.schabi.newpipe.local.subscription;
import android.content.Context;
import android.support.annotation.NonNull;
@@ -55,10 +55,10 @@ public class SubscriptionService {
private static final int SUBSCRIPTION_DEBOUNCE_INTERVAL = 500;
private static final int SUBSCRIPTION_THREAD_POOL_SIZE = 4;
private AppDatabase db;
private Flowable<List<SubscriptionEntity>> subscription;
private final AppDatabase db;
private final Flowable<List<SubscriptionEntity>> subscription;
private Scheduler subscriptionScheduler;
private final Scheduler subscriptionScheduler;
private SubscriptionService(Context context) {
db = NewPipeDatabase.getInstance(context.getApplicationContext());
@@ -116,7 +116,7 @@ public class SubscriptionService {
public Completable updateChannelInfo(final ChannelInfo info) {
final Function<List<SubscriptionEntity>, CompletableSource> update = new Function<List<SubscriptionEntity>, CompletableSource>() {
@Override
public CompletableSource apply(@NonNull List<SubscriptionEntity> subscriptionEntities) throws Exception {
public CompletableSource apply(@NonNull List<SubscriptionEntity> subscriptionEntities) {
if (DEBUG) Log.d(TAG, "updateChannelInfo() called with: subscriptionEntities = [" + subscriptionEntities + "]");
if (subscriptionEntities.size() == 1) {
SubscriptionEntity subscription = subscriptionEntities.get(0);
@@ -147,11 +147,16 @@ public class SubscriptionService {
}
private boolean isSubscriptionUpToDate(final ChannelInfo info, final SubscriptionEntity entity) {
return info.getUrl().equals(entity.getUrl()) &&
return equalsAndNotNull(info.getUrl(), entity.getUrl()) &&
info.getServiceId() == entity.getServiceId() &&
info.getName().equals(entity.getName()) &&
info.getAvatarUrl().equals(entity.getAvatarUrl()) &&
info.getDescription().equals(entity.getDescription()) &&
equalsAndNotNull(info.getAvatarUrl(), entity.getAvatarUrl()) &&
equalsAndNotNull(info.getDescription(), entity.getDescription()) &&
info.getSubscriberCount() == entity.getSubscriberCount();
}
private boolean equalsAndNotNull(final Object o1, final Object o2) {
return (o1 != null && o2 != null)
&& o1.equals(o2);
}
}

View File

@@ -1,4 +1,4 @@
package org.schabi.newpipe.fragments.subscription;
package org.schabi.newpipe.local.subscription;
import android.app.Activity;
import android.content.Intent;
@@ -26,7 +26,7 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.subscription.services.SubscriptionsImportService;
import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.FilePickerActivityHelper;
import org.schabi.newpipe.util.ServiceHelper;
@@ -37,10 +37,10 @@ import java.util.List;
import icepick.State;
import static org.schabi.newpipe.extractor.subscription.SubscriptionExtractor.ContentSource.CHANNEL_URL;
import static org.schabi.newpipe.subscription.services.SubscriptionsImportService.CHANNEL_URL_MODE;
import static org.schabi.newpipe.subscription.services.SubscriptionsImportService.INPUT_STREAM_MODE;
import static org.schabi.newpipe.subscription.services.SubscriptionsImportService.KEY_MODE;
import static org.schabi.newpipe.subscription.services.SubscriptionsImportService.KEY_VALUE;
import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.CHANNEL_URL_MODE;
import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.INPUT_STREAM_MODE;
import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.KEY_MODE;
import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.KEY_VALUE;
public class SubscriptionsImportFragment extends BaseFragment {
private static final int REQUEST_IMPORT_FILE_CODE = 666;

View File

@@ -17,7 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.schabi.newpipe.subscription.services;
package org.schabi.newpipe.local.subscription.services;
import android.app.Service;
import android.content.Intent;
@@ -36,8 +36,8 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.subscription.ImportExportEventListener;
import org.schabi.newpipe.subscription.SubscriptionService;
import org.schabi.newpipe.local.subscription.ImportExportEventListener;
import org.schabi.newpipe.local.subscription.SubscriptionService;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -58,8 +58,8 @@ public abstract class BaseImportExportService extends Service {
protected NotificationCompat.Builder notificationBuilder;
protected SubscriptionService subscriptionService;
protected CompositeDisposable disposables = new CompositeDisposable();
protected PublishProcessor<String> notificationUpdater = PublishProcessor.create();
protected final CompositeDisposable disposables = new CompositeDisposable();
protected final PublishProcessor<String> notificationUpdater = PublishProcessor.create();
@Nullable
@Override
@@ -90,9 +90,9 @@ public abstract class BaseImportExportService extends Service {
private static final int NOTIFICATION_SAMPLING_PERIOD = 2500;
protected AtomicInteger currentProgress = new AtomicInteger(-1);
protected AtomicInteger maxProgress = new AtomicInteger(-1);
protected ImportExportEventListener eventListener = new ImportExportEventListener() {
protected final AtomicInteger currentProgress = new AtomicInteger(-1);
protected final AtomicInteger maxProgress = new AtomicInteger(-1);
protected final ImportExportEventListener eventListener = new ImportExportEventListener() {
@Override
public void onSizeReceived(int size) {
maxProgress.set(size);
@@ -187,13 +187,13 @@ public abstract class BaseImportExportService extends Service {
protected Toast toast;
protected void showToast(@StringRes int message) {
showToast(getString(message), Toast.LENGTH_SHORT);
showToast(getString(message));
}
protected void showToast(String message, int duration) {
protected void showToast(String message) {
if (toast != null) toast.cancel();
toast = Toast.makeText(this, message, duration);
toast = Toast.makeText(this, message, Toast.LENGTH_SHORT);
toast.show();
}

View File

@@ -17,7 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.schabi.newpipe.subscription.services;
package org.schabi.newpipe.local.subscription.services;
import android.content.Intent;
import android.support.v4.content.LocalBroadcastManager;
@@ -29,7 +29,7 @@ import org.reactivestreams.Subscription;
import org.schabi.newpipe.R;
import org.schabi.newpipe.database.subscription.SubscriptionEntity;
import org.schabi.newpipe.extractor.subscription.SubscriptionItem;
import org.schabi.newpipe.subscription.ImportExportJsonHelper;
import org.schabi.newpipe.local.subscription.ImportExportJsonHelper;
import java.io.File;
import java.io.FileNotFoundException;
@@ -49,7 +49,7 @@ public class SubscriptionsExportService extends BaseImportExportService {
/**
* A {@link LocalBroadcastManager local broadcast} will be made with this action when the export is successfully completed.
*/
public static final String EXPORT_COMPLETE_ACTION = "org.schabi.newpipe.subscription.services.SubscriptionsExportService.EXPORT_COMPLETE";
public static final String EXPORT_COMPLETE_ACTION = "org.schabi.newpipe.local.subscription.services.SubscriptionsExportService.EXPORT_COMPLETE";
private Subscription subscription;
private File outFile;

View File

@@ -17,7 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.schabi.newpipe.subscription.services;
package org.schabi.newpipe.local.subscription.services;
import android.content.Intent;
import android.support.annotation.NonNull;
@@ -33,7 +33,7 @@ import org.schabi.newpipe.database.subscription.SubscriptionEntity;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.channel.ChannelInfo;
import org.schabi.newpipe.extractor.subscription.SubscriptionItem;
import org.schabi.newpipe.subscription.ImportExportJsonHelper;
import org.schabi.newpipe.local.subscription.ImportExportJsonHelper;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.ExtractorHelper;
@@ -64,7 +64,7 @@ public class SubscriptionsImportService extends BaseImportExportService {
/**
* A {@link LocalBroadcastManager local broadcast} will be made with this action when the import is successfully completed.
*/
public static final String IMPORT_COMPLETE_ACTION = "org.schabi.newpipe.subscription.services.SubscriptionsImportService.IMPORT_COMPLETE";
public static final String IMPORT_COMPLETE_ACTION = "org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.IMPORT_COMPLETE";
private Subscription subscription;
private int currentMode;
@@ -144,12 +144,16 @@ public class SubscriptionsImportService extends BaseImportExportService {
showToast(R.string.import_ongoing);
Flowable<List<SubscriptionItem>> flowable = null;
if (currentMode == CHANNEL_URL_MODE) {
flowable = importFromChannelUrl();
} else if (currentMode == INPUT_STREAM_MODE) {
flowable = importFromInputStream();
} else if (currentMode == PREVIOUS_EXPORT_MODE) {
flowable = importFromPreviousExport();
switch (currentMode) {
case CHANNEL_URL_MODE:
flowable = importFromChannelUrl();
break;
case INPUT_STREAM_MODE:
flowable = importFromInputStream();
break;
case PREVIOUS_EXPORT_MODE:
flowable = importFromPreviousExport();
break;
}
if (flowable == null) {

View File

@@ -28,7 +28,6 @@ import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.IBinder;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
@@ -39,17 +38,16 @@ import android.widget.RemoteViews;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.source.MediaSource;
import com.nostra13.universalimageloader.core.assist.FailReason;
import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.player.event.PlayerEventListener;
import org.schabi.newpipe.player.helper.LockManager;
import org.schabi.newpipe.player.helper.PlayerHelper;
import org.schabi.newpipe.playlist.PlayQueueItem;
import org.schabi.newpipe.util.ListHelper;
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import org.schabi.newpipe.player.resolver.AudioPlaybackResolver;
import org.schabi.newpipe.player.resolver.MediaSourceTag;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.ThemeHelper;
@@ -94,7 +92,6 @@ public final class BackgroundPlayer extends Service {
private NotificationCompat.Builder notBuilder;
private RemoteViews notRemoteView;
private RemoteViews bigNotRemoteView;
private final String setAlphaMethodName = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) ? "setImageAlpha" : "setAlpha";
private boolean shouldUpdateOnProgress;
@@ -118,8 +115,12 @@ public final class BackgroundPlayer extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (DEBUG) Log.d(TAG, "onStartCommand() called with: intent = [" + intent + "], flags = [" + flags + "], startId = [" + startId + "]");
if (DEBUG) Log.d(TAG, "onStartCommand() called with: intent = [" + intent +
"], flags = [" + flags + "], startId = [" + startId + "]");
basePlayerImpl.handleIntent(intent);
if (basePlayerImpl.mediaSessionManager != null) {
basePlayerImpl.mediaSessionManager.handleMediaButtonIntent(intent);
}
return START_NOT_STICKY;
}
@@ -160,6 +161,11 @@ public final class BackgroundPlayer extends Service {
if (DEBUG) Log.d(TAG, "onScreenOnOff() called with: on = [" + on + "]");
shouldUpdateOnProgress = on;
basePlayerImpl.triggerProgressUpdate();
if (on) {
basePlayerImpl.startProgressLoop();
} else {
basePlayerImpl.stopProgressLoop();
}
}
/*//////////////////////////////////////////////////////////////////////////
@@ -183,7 +189,9 @@ public final class BackgroundPlayer extends Service {
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setCustomContentView(notRemoteView)
.setCustomBigContentView(bigNotRemoteView);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) builder.setPriority(NotificationCompat.PRIORITY_MAX);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
builder.setPriority(NotificationCompat.PRIORITY_MAX);
}
return builder;
}
@@ -240,15 +248,6 @@ public final class BackgroundPlayer extends Service {
notificationManager.notify(NOTIFICATION_ID, notBuilder.build());
}
private void setControlsOpacity(@IntRange(from = 0, to = 255) int opacity) {
if (notRemoteView != null) notRemoteView.setInt(R.id.notificationPlayPause, setAlphaMethodName, opacity);
if (bigNotRemoteView != null) bigNotRemoteView.setInt(R.id.notificationPlayPause, setAlphaMethodName, opacity);
if (notRemoteView != null) notRemoteView.setInt(R.id.notificationFForward, setAlphaMethodName, opacity);
if (bigNotRemoteView != null) bigNotRemoteView.setInt(R.id.notificationFForward, setAlphaMethodName, opacity);
if (notRemoteView != null) notRemoteView.setInt(R.id.notificationFRewind, setAlphaMethodName, opacity);
if (bigNotRemoteView != null) bigNotRemoteView.setInt(R.id.notificationFRewind, setAlphaMethodName, opacity);
}
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
@@ -270,8 +269,16 @@ public final class BackgroundPlayer extends Service {
protected class BasePlayerImpl extends BasePlayer {
@NonNull final private AudioPlaybackResolver resolver;
BasePlayerImpl(Context context) {
super(context);
this.resolver = new AudioPlaybackResolver(context, dataSource);
}
@Override
public void initPlayer(boolean playOnReady) {
super.initPlayer(playOnReady);
}
@Override
@@ -284,30 +291,41 @@ public final class BackgroundPlayer extends Service {
startForeground(NOTIFICATION_ID, notBuilder.build());
}
@Override
public void initThumbnail(final String url) {
resetNotification();
if (notRemoteView != null) notRemoteView.setImageViewResource(R.id.notificationCover, R.drawable.dummy_thumbnail);
if (bigNotRemoteView != null) bigNotRemoteView.setImageViewResource(R.id.notificationCover, R.drawable.dummy_thumbnail);
updateNotification(-1);
super.initThumbnail(url);
/*//////////////////////////////////////////////////////////////////////////
// Thumbnail Loading
//////////////////////////////////////////////////////////////////////////*/
private void updateNotificationThumbnail() {
if (basePlayerImpl == null) return;
if (notRemoteView != null) {
notRemoteView.setImageViewBitmap(R.id.notificationCover,
basePlayerImpl.getThumbnail());
}
if (bigNotRemoteView != null) {
bigNotRemoteView.setImageViewBitmap(R.id.notificationCover,
basePlayerImpl.getThumbnail());
}
}
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
super.onLoadingComplete(imageUri, view, loadedImage);
if (loadedImage != null) {
// rebuild notification here since remote view does not release bitmaps, causing memory leaks
resetNotification();
if (notRemoteView != null) notRemoteView.setImageViewBitmap(R.id.notificationCover, loadedImage);
if (bigNotRemoteView != null) bigNotRemoteView.setImageViewBitmap(R.id.notificationCover, loadedImage);
updateNotification(-1);
}
resetNotification();
updateNotificationThumbnail();
updateNotification(-1);
}
@Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
super.onLoadingFailed(imageUri, view, failReason);
resetNotification();
updateNotificationThumbnail();
updateNotification(-1);
}
/*//////////////////////////////////////////////////////////////////////////
// States Implementation
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onPrepared(boolean playWhenReady) {
super.onPrepared(playWhenReady);
@@ -326,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));
@@ -381,29 +400,18 @@ public final class BackgroundPlayer extends Service {
// Playback Listener
//////////////////////////////////////////////////////////////////////////*/
protected void onMetadataChanged(@NonNull final PlayQueueItem item,
@Nullable final StreamInfo info,
final int newPlayQueueIndex,
final boolean hasPlayQueueItemChanged) {
if (shouldUpdateOnProgress || hasPlayQueueItemChanged) {
resetNotification();
updateNotification(-1);
updateMetadata();
}
protected void onMetadataChanged(@NonNull final MediaSourceTag tag) {
super.onMetadataChanged(tag);
resetNotification();
updateNotificationThumbnail();
updateNotification(-1);
updateMetadata();
}
@Override
@Nullable
public MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info) {
final MediaSource liveSource = super.sourceOf(item, info);
if (liveSource != null) return liveSource;
final int index = ListHelper.getDefaultAudioFormat(context, info.getAudioStreams());
if (index < 0 || index >= info.getAudioStreams().size()) return null;
final AudioStream audio = info.getAudioStreams().get(index);
return buildMediaSource(audio.getUrl(), PlayerHelper.cacheKeyOf(info, audio),
MediaFormat.getSuffixById(audio.getFormatId()));
return resolver.resolve(info);
}
@Override
@@ -430,8 +438,8 @@ public final class BackgroundPlayer extends Service {
}
private void updateMetadata() {
if (activityListener != null && currentInfo != null) {
activityListener.onMetadataUpdate(currentInfo);
if (activityListener != null && getCurrentMetadata() != null) {
activityListener.onMetadataUpdate(getCurrentMetadata().getMetadata());
}
}
@@ -522,45 +530,36 @@ public final class BackgroundPlayer extends Service {
updatePlayback();
}
@Override
public void onBlocked() {
super.onBlocked();
setControlsOpacity(77);
updateNotification(-1);
}
@Override
public void onPlaying() {
super.onPlaying();
setControlsOpacity(255);
resetNotification();
updateNotificationThumbnail();
updateNotification(R.drawable.ic_pause_white);
lockManager.acquireWifiAndCpu();
}
@Override
public void onPaused() {
super.onPaused();
resetNotification();
updateNotificationThumbnail();
updateNotification(R.drawable.ic_play_arrow_white);
if (isProgressLoopRunning()) stopProgressLoop();
lockManager.releaseWifiAndCpu();
}
@Override
public void onCompleted() {
super.onCompleted();
setControlsOpacity(255);
resetNotification();
if (bigNotRemoteView != null) bigNotRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 100, false);
if (notRemoteView != null) notRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 100, false);
if (bigNotRemoteView != null) {
bigNotRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 100, false);
}
if (notRemoteView != null) {
notRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 100, false);
}
updateNotificationThumbnail();
updateNotification(R.drawable.ic_replay_white);
lockManager.releaseWifiAndCpu();
}
}

File diff suppressed because it is too large Load Diff

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