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

Compare commits

...

97 Commits

Author SHA1 Message Date
Christian Schabesberger
3d1cc348c8 fix getUploaderName() and move on to v0.10.2 2017-10-31 19:47:46 +01:00
Christian Schabesberger
2756db6601 downgrade gralde wrapper to 4.2 2017-10-23 14:59:30 +02:00
Coin
7b56aaad53 Translated using Weblate (Chinese (Hong Kong))
Currently translated at 68.9% (142 of 206 strings)
2017-10-22 09:44:44 +02:00
Joona Mattila
b73677fa5e Translated using Weblate (Finnish)
Currently translated at 98.0% (202 of 206 strings)
2017-10-21 01:46:12 +02:00
Weblate
f391574113 Merge remote-tracking branch 'origin/dev' into dev 2017-10-19 22:34:40 +02:00
Joona Mattila
ea863b0c24 Added translation using Weblate (Finnish) 2017-10-19 22:34:36 +02:00
Christian Schabesberger
4506c90e59 Merge pull request #771 from TeamNewPipe/master
Release v0.10.1
2017-10-18 11:59:53 +02:00
Christian Schabesberger
894a63ed82 Merge branch 'dev' into master 2017-10-18 11:56:11 +02:00
Christian Schabesberger
0155454526 moved on to version v0.10.1 2017-10-18 11:44:24 +02:00
Weblate
f7aafb87a8 Merge remote-tracking branch 'origin/dev' into dev 2017-10-17 14:51:16 +02:00
Anton Shestakov
227001ec32 Translated using Weblate (Russian)
Currently translated at 99.0% (204 of 206 strings)
2017-10-17 14:51:15 +02:00
Sérgio Marques
d765364915 Translated using Weblate (Portuguese)
Currently translated at 100.0% (206 of 206 strings)
2017-10-17 14:51:15 +02:00
developerchan1
3d47e63d6f Translated using Weblate (Indonesian)
Currently translated at 81.0% (167 of 206 strings)
2017-10-17 14:51:12 +02:00
Allan Nordhøy
79c5c3cc57 Translated using Weblate (Norwegian Bokmål)
Currently translated at 99.5% (205 of 206 strings)
2017-10-17 14:51:02 +02:00
Christian Schabesberger
8babbddcf9 Merge pull request #766 from jlelse/dev
Updated Gradle wrapper
2017-10-16 15:25:11 +02:00
Christian Schabesberger
13756508d3 Merge pull request #757 from coffeemakr/fix-service-id-not-initialized
Fix service id not initialized
2017-10-16 15:22:26 +02:00
Bruno Guerreiro
b7fe001b13 Translated using Weblate (Portuguese)
Currently translated at 100.0% (206 of 206 strings)
2017-10-15 19:10:11 +02:00
TheAssassin
35b4110a7a Fix half finished paragraph 2017-10-15 18:42:49 +02:00
Jan-Lk Else
cdca0c6325 Updated Gradle wrapper 2017-10-14 11:42:00 +02:00
Weblate
094851cc7d Merge remote-tracking branch 'origin/dev' into dev 2017-10-13 02:33:58 +02:00
Enol P
f55612be40 Translated using Weblate (Asturian)
Currently translated at 100.0% (206 of 206 strings)
2017-10-13 02:33:51 +02:00
Tryton Van Meer
b70fd826e7 Fix crash when returning to player.
When switching apps or locking the phone, destroyPlayer is called which sets audioManager to null. So upon returning to the player and pressing play, the app crashes.
So now initPlayer checks if audioManager is null and sets it if needed.
2017-10-11 14:27:32 +02:00
Coin
994559b39b Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (206 of 206 strings)
2017-10-10 18:06:26 +02:00
Nathan Follens
907842c672 Translated using Weblate (Dutch)
Currently translated at 100.0% (206 of 206 strings)
2017-10-09 11:50:02 +02:00
Cyril Müller
c890ab44d6 Merge branch 'dev' into fix-service-id-not-initialized 2017-10-09 11:23:19 +02:00
Coffeemakr
89b11ff71c Fail-fast for service id == -1 2017-10-08 22:11:38 +02:00
Tobias Groza
3e34eeeed7 Translated using Weblate (German)
Currently translated at 99.5% (205 of 206 strings)
2017-10-08 21:45:38 +02:00
Coffeemakr
69302fcbd0 Add icepicker proguard rules 2017-10-08 17:52:12 +02:00
Coffeemakr
60879351a9 Code improvement and logging 2017-10-08 17:52:07 +02:00
Matej U
f4433ac508 Translated using Weblate (Slovenian)
Currently translated at 100.0% (206 of 206 strings)
2017-10-07 21:12:32 +02:00
E T
7b7d0d6171 Translated using Weblate (Turkish)
Currently translated at 100.0% (206 of 206 strings)
2017-10-07 19:51:07 +02:00
Freddy Morán Jr
a6e0ed09a8 Translated using Weblate (Spanish)
Currently translated at 100.0% (206 of 206 strings)
2017-10-07 19:16:42 +02:00
Emanuele Petriglia
19ed6ebbaf Translated using Weblate (Italian)
Currently translated at 100.0% (206 of 206 strings)
2017-10-07 14:21:02 +02:00
Andrea Troiano
dfeee17d39 Translated using Weblate (Italian)
Currently translated at 100.0% (206 of 206 strings)
2017-10-07 13:28:50 +02:00
Jonas
4c429c869c Translated using Weblate (French)
Currently translated at 100.0% (206 of 206 strings)
2017-10-06 12:15:39 +02:00
Eduardo Caron
825de1b6ee Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (206 of 206 strings)
2017-10-05 13:19:37 +02:00
Weblate
124461f587 Merge remote-tracking branch 'origin/dev' into dev 2017-10-05 13:12:38 +02:00
CaptainCrumble
a570fa6110 Translated using Weblate (Portuguese)
Currently translated at 98.5% (202 of 205 strings)
2017-10-05 13:12:37 +02:00
CaptainCrumble
0e8df83bbd Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (205 of 205 strings)
2017-10-05 13:11:14 +02:00
Christian Schabesberger
952c8428d8 Merge pull request #748 from mauriciocolli/improve-search
Improve search
2017-10-04 11:03:53 +02:00
Matej U
5fe2c10aa1 Translated using Weblate (Slovenian)
Currently translated at 100.0% (205 of 205 strings)
2017-10-02 21:11:57 +02:00
Andrea Troiano
1f4aa2506b Translated using Weblate (Italian)
Currently translated at 100.0% (205 of 205 strings)
2017-10-02 15:46:44 +02:00
Andrea Troiano
9f8844fa5f Translated using Weblate (Italian)
Currently translated at 100.0% (205 of 205 strings)
2017-10-01 15:31:58 +02:00
E T
990aa88e00 Translated using Weblate (Turkish)
Currently translated at 100.0% (205 of 205 strings)
2017-09-30 09:19:13 +02:00
Nathan Follens
9e335c1894 Translated using Weblate (Dutch)
Currently translated at 100.0% (205 of 205 strings)
2017-09-29 23:27:32 +02:00
Jonas
8f2b9a6bb7 Translated using Weblate (French)
Currently translated at 100.0% (205 of 205 strings)
2017-09-29 18:41:33 +02:00
Anton Shestakov
448f3e8918 Translated using Weblate (Russian)
Currently translated at 98.5% (202 of 205 strings)
2017-09-29 12:47:49 +02:00
Mladen Pejaković
62b2ab7571 Translated using Weblate (Serbian)
Currently translated at 100.0% (205 of 205 strings)
2017-09-29 11:27:25 +02:00
Mauricio Colli
78cbfa20d9 Improve search
- Use a list instead of a popup
- Show search history entries
2017-09-28 10:06:48 -03:00
Freddy Morán Jr
ccd42d42ab Translated using Weblate (Spanish)
Currently translated at 100.0% (205 of 205 strings)
2017-09-27 18:47:50 +02:00
Tobias Groza
abe03ef9a3 Translated using Weblate (German)
Currently translated at 99.5% (204 of 205 strings)
2017-09-27 18:45:48 +02:00
Anton Shestakov
9813ffb83b Translated using Weblate (Russian)
Currently translated at 97.5% (200 of 205 strings)
2017-09-27 15:47:52 +02:00
Mikas
b4eefa3eed Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (205 of 205 strings)
2017-09-26 18:52:53 +02:00
Anton Shestakov
6a166b798a Translated using Weblate (Russian)
Currently translated at 97.5% (200 of 205 strings)

(cherry picked from commit efa262480a)
2017-09-26 09:04:38 -03:00
Jona Abdinghoff
0686aec606 Translated using Weblate (German)
Currently translated at 99.5% (204 of 205 strings)

(cherry picked from commit 2c8dd9ce2a)
2017-09-26 09:04:38 -03:00
Mikas
54b3c62803 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (205 of 205 strings)

(cherry picked from commit 4a4b1e0e49)
2017-09-26 09:04:38 -03:00
nailyk
40d83ba7e7 Translated using Weblate (French)
Currently translated at 100.0% (205 of 205 strings)

(cherry picked from commit cb5e37184a)
2017-09-26 09:04:38 -03:00
trmdi
ea3c379d88 Translated using Weblate (Vietnamese)
Currently translated at 68.2% (140 of 205 strings)

(cherry picked from commit 094a3af8ae)
2017-09-26 09:04:38 -03:00
Yann Hodiesne
a6ee3e99ea Translated using Weblate (French)
Currently translated at 100.0% (205 of 205 strings)

(cherry picked from commit 283d33aa27)
2017-09-26 09:04:38 -03:00
nailyk
b2cab4aea0 Translated using Weblate (French)
Currently translated at 100.0% (205 of 205 strings)

(cherry picked from commit 6c445c0833)
2017-09-26 09:04:38 -03:00
Krysa Czech
2a2e532acc Translated using Weblate (Czech)
Currently translated at 100.0% (205 of 205 strings)

(cherry picked from commit dd10c1756f)
2017-09-26 09:04:38 -03:00
pawelkw
0954a494e7 Translated using Weblate (Polish)
Currently translated at 80.9% (166 of 205 strings)

(cherry picked from commit 5ecad47d6e)
2017-09-26 09:04:38 -03:00
Krysa Czech
bc32c946ff Translated using Weblate (Czech)
Currently translated at 76.0% (156 of 205 strings)

(cherry picked from commit edfdabb691)
2017-09-26 09:04:38 -03:00
Ivan Krušlin
95debf66e5 Translated using Weblate (Croatian)
Currently translated at 100.0% (205 of 205 strings)

(cherry picked from commit 4653e94c57)
2017-09-26 09:04:38 -03:00
Bruno Tendler
3b166f82a8 Translated using Weblate (Spanish)
Currently translated at 100.0% (205 of 205 strings)

(cherry picked from commit 54df50f84d)
2017-09-26 09:04:38 -03:00
Ivan Krušlin
7d19250565 Translated using Weblate (Croatian)
Currently translated at 100.0% (205 of 205 strings)

(cherry picked from commit 6341ad88e8)
2017-09-26 09:04:38 -03:00
E T
c510a4149d Translated using Weblate (Turkish)
Currently translated at 100.0% (205 of 205 strings)

(cherry picked from commit f893edeb82)
2017-09-26 09:04:38 -03:00
E T
33e473c509 Translated using Weblate (Turkish)
Currently translated at 100.0% (205 of 205 strings)

(cherry picked from commit c1fe03aab6)
2017-09-26 09:04:38 -03:00
Christian Schabesberger
e3dfdb0b06 Merge pull request #738 from comradekingu/patch-4
Spelling: View → Play
2017-09-26 14:01:23 +02:00
Anton Shestakov
efa262480a Translated using Weblate (Russian)
Currently translated at 97.5% (200 of 205 strings)
2017-09-26 13:32:59 +02:00
Jona Abdinghoff
2c8dd9ce2a Translated using Weblate (German)
Currently translated at 99.5% (204 of 205 strings)
2017-09-26 09:46:23 +02:00
Allan Nordhøy
ead1399e7b Spelling: View → Play 2017-09-25 16:12:47 +02:00
Mikas
4a4b1e0e49 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (205 of 205 strings)
2017-09-25 00:47:09 +02:00
nailyk
cb5e37184a Translated using Weblate (French)
Currently translated at 100.0% (205 of 205 strings)
2017-09-23 21:45:34 +02:00
Fablab user
4b78a9366d s/TEAMPLATE/TEMPLATE/g
remove old teamplate
2017-09-23 21:17:08 +02:00
trmdi
094a3af8ae Translated using Weblate (Vietnamese)
Currently translated at 68.2% (140 of 205 strings)
2017-09-23 18:49:19 +02:00
Coffeemakr
d6fffc7e55 Use correct long comparison (fixes #726) 2017-09-23 08:35:06 -03:00
Yann Hodiesne
283d33aa27 Translated using Weblate (French)
Currently translated at 100.0% (205 of 205 strings)
2017-09-22 19:31:22 +02:00
nailyk
6c445c0833 Translated using Weblate (French)
Currently translated at 100.0% (205 of 205 strings)
2017-09-22 19:30:28 +02:00
Krysa Czech
dd10c1756f Translated using Weblate (Czech)
Currently translated at 100.0% (205 of 205 strings)
2017-09-22 19:04:04 +02:00
Adrian Campos
ced75a9b60 Added Adaptive launcher Icon 2017-09-21 22:46:36 -03:00
pawelkw
5ecad47d6e Translated using Weblate (Polish)
Currently translated at 80.9% (166 of 205 strings)
2017-09-21 21:47:16 +02:00
Christian Schabesberger
6e11ca1ac4 Merge pull request #722 from TeamNewPipe/beta
add everything for beta release
2017-09-21 10:08:11 +02:00
Krysa Czech
edfdabb691 Translated using Weblate (Czech)
Currently translated at 76.0% (156 of 205 strings)
2017-09-21 00:45:00 +02:00
Ivan Krušlin
4653e94c57 Translated using Weblate (Croatian)
Currently translated at 100.0% (205 of 205 strings)
2017-09-21 00:44:47 +02:00
Bruno Tendler
54df50f84d Translated using Weblate (Spanish)
Currently translated at 100.0% (205 of 205 strings)
2017-09-20 21:49:00 +02:00
Christian Schabesberger
7588c8de5a Update screenshots 2017-09-19 20:50:01 -03:00
Christian Schabesberger
d564199e88 add sigle issue signle thread rule 2017-09-19 20:41:45 -03:00
TobiGr
e458adb342 Update README.md
- add Bountysource to donate section
- add navigation
- add Github badges
2017-09-19 20:40:51 -03:00
John Carlson
b43a7354cf Use google() in buildscript 2017-09-19 20:40:31 -03:00
Ivan Krušlin
6341ad88e8 Translated using Weblate (Croatian)
Currently translated at 100.0% (205 of 205 strings)
2017-09-19 20:00:50 +02:00
Allan Nordhøy
314b2fb14f Spelling of file formats (#703) 2017-09-19 18:38:27 +02:00
Tobias Groza
795ba89dc4 Support dark theme in file picker (#699) 2017-09-19 18:38:27 +02:00
Felix Ableitner
5b8ff28556 Open youtube links directly from search (fixes #35) (#692)
* Open youtube links directly from search (fixes #35)
2017-09-19 18:38:27 +02:00
Christian Schabesberger
5e66a66111 enable minify 2017-09-19 18:38:27 +02:00
Mauricio Colli
3e6bed538a Update extractor version 2017-09-19 18:38:27 +02:00
Christian Schabesberger
020322df0b add everything for beta release
add AndroidManifest.xml for beta
2017-09-09 20:46:58 +02:00
94 changed files with 2446 additions and 640 deletions

View File

@@ -13,6 +13,7 @@ Do not report crashes in the GitHub issue tracker. NewPipe has an automated cras
* Check whether your issue/feature is already fixed/implemented
* 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.
## 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.
@@ -26,7 +27,7 @@ Do not report crashes in the GitHub issue tracker. NewPipe has an automated cras
* 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.
* 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
* 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!
* Try to figure out yourself why builds on our CI fail.

View File

@@ -10,9 +10,6 @@ android:
# The SDK version used to compile NewPipe
- android-26
# Additional components
- extra-android-m2repository
script: ./gradlew -Dorg.gradle.jvmargs=-Xmx1536m assembleDebug lintDebug testDebugUnitTest
licenses:

View File

@@ -1,34 +1,32 @@
<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>
<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>
</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 />
WARNING: PUTTING NEWPIPE OR ANY FORK OF IT INTO GOOGLE PLAYSTORE VIOLATES THEIR TERMS OF CONDITIONS.
# NewPipe
NewPipe: A free lightweight YouTube frontend for Android.
[![NewPipe](app/src/main/res/mipmap-xhdpi/ic_launcher.png)](https://newpipe.schabi.org)
[![F-Droid](https://f-droid.org/wiki/images/0/06/F-Droid-button_get-it-on.png)](https://f-droid.org/packages/org.schabi.newpipe/)
Project status:
[![Translation Status](https://hosted.weblate.org/widgets/NewPipe/-/svg-badge.svg)](https://hosted.weblate.org/engage/NewPipe/)
[![Build Status](https://travis-ci.org/TeamNewPipe/NewPipe.svg)](https://travis-ci.org/TeamNewPipe/NewPipe)
## Donate
![Bitcoin](https://bitcoin.org/img/icons/logotop.svg)
![BitcoinQR](assets/16A9J59ahMRqkLSZjhYj33n9j3fMztFxnh.png)
`16A9J59ahMRqkLSZjhYj33n9j3fMztFxnh`
## Screenshots
[<img src="screenshots/screenshot_1.png" width=160>](screenshots/screenshot_1.png)
[<img src="screenshots/screenshot_2.png" width=160>](screenshots/screenshot_2.png)
[<img src="screenshots/screenshot_3.png" width=160>](screenshots/screenshot_3.png)
[<img src="screenshots/screenshot_4.png" width=160>](screenshots/screenshot_4.png)
[<img src="screenshots/screenshot_5.png" width=160>](screenshots/screenshot_5.png)
[<img src="screenshots/screenshot_6.png" width=160>](screenshots/screenshot_6.png)
[<img src="screenshots/screenshot_7.png" width=160>](screenshots/screenshot_7.png)
[<img src="screenshots/screenshot_8.png" width=160>](screenshots/screenshot_8.png)
[<img src="screenshots/screenshot_9.png" width=160>](screenshots/screenshot_9.png)
[<img src="screenshots/shot_1.png" width=160>](screenshots/shot_1.png)
[<img src="screenshots/shot_2.png" width=160>](screenshots/shot_2.png)
[<img src="screenshots/shot_3.png" width=160>](screenshots/shot_3.png)
[<img src="screenshots/shot_4.png" width=160>](screenshots/shot_4.png)
[<img src="screenshots/shot_5.png" width=160>](screenshots/shot_5.png)
[<img src="screenshots/shot_6.png" width=160>](screenshots/shot_6.png)
[<img src="screenshots/shot_7.png" width=160>](screenshots/shot_7.png)
[<img src="screenshots/shot_8.png" width=160>](screenshots/shot_8.png)
[<img src="screenshots/shot_9.png" width=160>](screenshots/shot_9.png)
[<img src="screenshots/shot_10.png" width=160>](screenshots/shot_10.png)
## Description
@@ -39,7 +37,7 @@ NewPipe does not use any Google framework libraries, or the YouTube API. It only
* Search videos
* Display general information about a video
* Watch YouTube videos
* Listen to YouTube videos (experimental)
* Listen to YouTube videos
* Popup mode (floating player)
* Select the streaming player to watch the video with
* Download videos
@@ -47,21 +45,23 @@ NewPipe does not use any Google framework libraries, or the YouTube API. It only
* Open a video in Kodi
* Show Next/Related videos
* Search YouTube in a specific language
* Watch age restricted material
* Watch/Block age restricted material
* Display general information about channels
* Search channels
* Watch videos from a channel
* Orbot/Tor support (not yet directly)
* 1080p/2k/4k support
* View history
* Subscribe to channels
* Search history
* Search/Watch Playlists
### Coming Features
* Multiservice support (eg. SoundCloud)
* Bookmarks
* View history
* Search history
* Subscribe to channels
* Search/Watch Playlists
* Queeing videos
* Watch as queues Playlists
* Queuing videos
* Subtitles support
* livestream support
* ... and many more
@@ -75,6 +75,22 @@ 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 or BountySource. For further information about 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><samp>16A9J59ahMRqkLSZjhYj33n9j3fMztFxnh</samp></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" alz="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>
## License
[![GNU GPLv3 Image](https://www.gnu.org/graphics/gplv3-127x51.png)](http://www.gnu.org/licenses/gpl-3.0.en.html)

View File

@@ -8,8 +8,8 @@ android {
applicationId "org.schabi.newpipe"
minSdkVersion 15
targetSdkVersion 26
versionCode 38
versionName "0.10.0"
versionCode 40
versionName "0.10.2"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
@@ -26,6 +26,9 @@ android {
debuggable true
applicationIdSuffix ".debug"
}
beta {
applicationIdSuffix ".beta"
}
}
lintOptions {
@@ -45,7 +48,8 @@ dependencies {
exclude module: 'support-annotations'
}
compile 'com.github.TeamNewPipe:NewPipeExtractor:7ae274b'
compile 'com.github.TeamNewPipe:NewPipeExtractor:b9d0941'
testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:1.10.19'
@@ -62,7 +66,7 @@ dependencies {
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
compile 'de.hdodenhof:circleimageview:2.1.0'
compile 'com.github.nirhart:parallaxscroll:1.0'
compile 'com.nononsenseapps:filepicker:3.0.0'
compile 'com.nononsenseapps:filepicker:3.0.1'
compile 'com.google.android.exoplayer:exoplayer:r2.5.1'
debugCompile 'com.facebook.stetho:stetho:1.5.0'

View File

@@ -24,4 +24,14 @@
-dontwarn org.mozilla.javascript.tools.**
-dontwarn android.arch.util.paging.CountedDataSource
-dontwarn android.arch.persistence.room.paging.LimitOffsetDataSource
-dontwarn android.arch.persistence.room.paging.LimitOffsetDataSource
# Rules for icepick. Copy paste from https://github.com/frankiesardo/icepick
-dontwarn icepick.**
-keep class icepick.** { *; }
-keep class **$$Icepick { *; }
-keepclasseswithmembernames class * {
@icepick.* <fields>;
}
-keepnames class * { @icepick.State *;}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:label="NewPipe Beta"
tools:replace="android:label">
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -88,10 +88,14 @@
<service android:name="us.shandian.giga.service.DownloadManagerService"/>
<activity
android:name="com.nononsenseapps.filepicker.FilePickerActivity"
android:name=".util.FilePickerActivityHelper"
android:label="@string/app_name"
android:launchMode="singleTop"
android:theme="@style/FilePickerTheme"/>
android:theme="@style/FilePickerThemeDark">
<intent-filter>
<action android:name="android.intent.action.GET_CONTENT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name=".ReCaptchaActivity"

View File

@@ -12,7 +12,6 @@ import java.io.InterruptedIOException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.net.ssl.HttpsURLConnection;
@@ -135,11 +134,8 @@ public class Downloader implements org.schabi.newpipe.extractor.Downloader {
}
in = new BufferedReader(new InputStreamReader(con.getInputStream()));
for (Map.Entry<String, List<String>> entry : con.getHeaderFields().entrySet()) {
System.err.println(entry.getKey() + ": " + entry.getValue());
}
String inputLine;
String inputLine;
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}

View File

@@ -43,9 +43,8 @@ public class RouterActivity extends AppCompatActivity {
}
protected void handleUrl(String url) {
try {
NavigationHelper.openByLink(this, url);
} catch (Exception e) {
boolean success = NavigationHelper.openByLink(this, url);
if (!success) {
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
}

View File

@@ -11,6 +11,7 @@ import io.reactivex.Flowable;
import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.CREATION_DATE;
import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.ID;
import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.SEARCH;
import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.SERVICE_ID;
import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.TABLE_NAME;
@@ -27,11 +28,20 @@ public interface SearchHistoryDAO extends HistoryDAO<SearchHistoryEntry> {
@Override
int deleteAll();
@Query("DELETE FROM " + TABLE_NAME + " WHERE " + SEARCH + " = :query")
int deleteAllWhereQuery(String query);
@Query("SELECT * FROM " + TABLE_NAME + ORDER_BY_CREATION_DATE)
@Override
Flowable<List<SearchHistoryEntry>> getAll();
@Query("SELECT * FROM " + TABLE_NAME + " GROUP BY " + SEARCH + ORDER_BY_CREATION_DATE + " LIMIT :limit")
Flowable<List<SearchHistoryEntry>> getUniqueEntries(int limit);
@Query("SELECT * FROM " + TABLE_NAME + " WHERE " + SERVICE_ID + " = :serviceId" + ORDER_BY_CREATION_DATE)
@Override
Flowable<List<SearchHistoryEntry>> listByService(int serviceId);
@Query("SELECT * FROM " + TABLE_NAME + " WHERE " + SEARCH + " LIKE :query || '%' GROUP BY " + SEARCH + " LIMIT :limit")
Flowable<List<SearchHistoryEntry>> getSimilarEntries(String query, int limit);
}

View File

@@ -7,6 +7,7 @@ import android.arch.persistence.room.Index;
import android.arch.persistence.room.PrimaryKey;
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
import org.schabi.newpipe.util.Constants;
import static org.schabi.newpipe.database.subscription.SubscriptionEntity.SUBSCRIPTION_SERVICE_ID;
import static org.schabi.newpipe.database.subscription.SubscriptionEntity.SUBSCRIPTION_TABLE;
@@ -28,7 +29,7 @@ public class SubscriptionEntity {
private long uid = 0;
@ColumnInfo(name = SUBSCRIPTION_SERVICE_ID)
private int serviceId = -1;
private int serviceId = Constants.NO_SERVICE_ID;
@ColumnInfo(name = SUBSCRIPTION_URL)
private String url;

View File

@@ -65,6 +65,7 @@ import org.schabi.newpipe.player.PopupVideoPlayer;
import org.schabi.newpipe.player.old.PlayVideoActivity;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.InfoCache;
import org.schabi.newpipe.util.ListHelper;
@@ -110,7 +111,7 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
private boolean wasRelatedStreamsExpanded = false;
@State
protected int serviceId = -1;
protected int serviceId = Constants.NO_SERVICE_ID;
@State
protected String name;
@State

View File

@@ -8,6 +8,7 @@ import android.view.View;
import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.ListInfo;
import org.schabi.newpipe.util.Constants;
import java.util.Queue;
@@ -21,7 +22,7 @@ import io.reactivex.schedulers.Schedulers;
public abstract class BaseListInfoFragment<I extends ListInfo> extends BaseListFragment<I, ListExtractor.NextItemsResult> {
@State
protected int serviceId = -1;
protected int serviceId = Constants.NO_SERVICE_ID;
@State
protected String name;
@State

View File

@@ -0,0 +1,16 @@
package org.schabi.newpipe.fragments.list.search;
public class SuggestionItem {
public final boolean fromHistory;
public final String query;
public SuggestionItem(boolean fromHistory, String query) {
this.fromHistory = fromHistory;
this.query = query;
}
@Override
public String toString() {
return "[" + fromHistory + "→" + query + "]";
}
}

View File

@@ -1,89 +1,108 @@
package org.schabi.newpipe.fragments.list.search;
import android.content.Context;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.support.v4.widget.ResourceCursorAdapter;
import android.content.res.TypedArray;
import android.support.annotation.AttrRes;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import org.schabi.newpipe.R;
import java.util.ArrayList;
import java.util.List;
/*
* Created by Christian Schabesberger on 02.08.16.
*
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
* SuggestionListAdapter.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/>.
*/
/**
* {@link ResourceCursorAdapter} to display suggestions.
*/
public class SuggestionListAdapter extends ResourceCursorAdapter {
private static final String[] columns = new String[]{"_id", "title"};
private static final int INDEX_ID = 0;
private static final int INDEX_TITLE = 1;
public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAdapter.SuggestionItemHolder> {
private final ArrayList<SuggestionItem> items = new ArrayList<>();
private final Context context;
private OnSuggestionItemSelected listener;
public interface OnSuggestionItemSelected {
void onSuggestionItemSelected(SuggestionItem item);
void onSuggestionItemLongClick(SuggestionItem item);
}
public SuggestionListAdapter(Context context) {
super(context, android.R.layout.simple_list_item_1, null, 0);
this.context = context;
}
public void setItems(List<SuggestionItem> items) {
this.items.clear();
this.items.addAll(items);
notifyDataSetChanged();
}
public void setListener(OnSuggestionItemSelected listener) {
this.listener = listener;
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
ViewHolder viewHolder = new ViewHolder(view);
viewHolder.suggestionTitle.setText(cursor.getString(INDEX_TITLE));
}
/**
* Update the suggestion list
* @param suggestions the list of suggestions
*/
public void updateAdapter(List<String> suggestions) {
MatrixCursor cursor = new MatrixCursor(columns, suggestions.size());
int i = 0;
for (String suggestion : suggestions) {
String[] columnValues = new String[columns.length];
columnValues[INDEX_TITLE] = suggestion;
columnValues[INDEX_ID] = Integer.toString(i);
cursor.addRow(columnValues);
i++;
}
changeCursor(cursor);
}
/**
* Get the suggestion for a position
* @param position the position of the suggestion
* @return the suggestion
*/
public String getSuggestion(int position) {
return ((Cursor) getItem(position)).getString(INDEX_TITLE);
public SuggestionItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new SuggestionItemHolder(LayoutInflater.from(context).inflate(R.layout.item_search_suggestion, parent, false));
}
@Override
public CharSequence convertToString(Cursor cursor) {
return cursor.getString(INDEX_TITLE);
public void onBindViewHolder(SuggestionItemHolder holder, int position) {
final SuggestionItem currentItem = getItem(position);
holder.updateFrom(currentItem);
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null) listener.onSuggestionItemSelected(currentItem);
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (listener != null) listener.onSuggestionItemLongClick(currentItem);
return true;
}
});
}
private class ViewHolder {
private final TextView suggestionTitle;
private ViewHolder(View view) {
this.suggestionTitle = view.findViewById(android.R.id.text1);
private SuggestionItem getItem(int position) {
return items.get(position);
}
@Override
public int getItemCount() {
return items.size();
}
public boolean isEmpty() {
return getItemCount() == 0;
}
public static class SuggestionItemHolder extends RecyclerView.ViewHolder {
private final TextView itemSuggestionQuery;
private final ImageView suggestionIcon;
// Cache some ids, as they can potentially be constantly updated/recycled
private final int historyResId;
private final int searchResId;
private SuggestionItemHolder(View rootView) {
super(rootView);
suggestionIcon = rootView.findViewById(R.id.item_suggestion_icon);
itemSuggestionQuery = rootView.findViewById(R.id.item_suggestion_query);
historyResId = resolveResourceIdFromAttr(rootView.getContext(), R.attr.history);
searchResId = resolveResourceIdFromAttr(rootView.getContext(), R.attr.search);
}
private void updateFrom(SuggestionItem item) {
suggestionIcon.setImageResource(item.fromHistory ? historyResId : searchResId);
itemSuggestionQuery.setText(item.query);
}
private static int resolveResourceIdFromAttr(Context context, @AttrRes int attr) {
TypedArray a = context.getTheme().obtainStyledAttributes(new int[]{attr});
int attributeResourceId = a.getResourceId(0, 0);
a.recycle();
return attributeResourceId;
}
}
}
}

View File

@@ -178,6 +178,10 @@ public abstract class BasePlayer implements Player.EventListener, AudioManager.O
if (DEBUG) Log.d(TAG, "initPlayer() called with: context = [" + context + "]");
initExoPlayerCache();
if (audioManager == null) {
this.audioManager = ((AudioManager) context.getSystemService(Context.AUDIO_SERVICE));
}
AdaptiveTrackSelection.Factory trackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter);
DefaultTrackSelector defaultTrackSelector = new DefaultTrackSelector(trackSelectionFactory);
DefaultLoadControl loadControl = new DefaultLoadControl();

View File

@@ -7,9 +7,8 @@ import android.support.annotation.Nullable;
import android.support.v7.preference.Preference;
import android.util.Log;
import com.nononsenseapps.filepicker.FilePickerActivity;
import org.schabi.newpipe.R;
import org.schabi.newpipe.util.FilePickerActivityHelper;
public class DownloadSettingsFragment extends BasePreferenceFragment {
private static final int REQUEST_DOWNLOAD_PATH = 0x1235;
@@ -48,10 +47,10 @@ public class DownloadSettingsFragment extends BasePreferenceFragment {
}
if (preference.getKey().equals(DOWNLOAD_PATH_PREFERENCE) || preference.getKey().equals(DOWNLOAD_PATH_AUDIO_PREFERENCE)) {
Intent i = new Intent(getActivity(), FilePickerActivity.class)
.putExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, false)
.putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR, true)
.putExtra(FilePickerActivity.EXTRA_MODE, FilePickerActivity.MODE_DIR);
Intent i = new Intent(getActivity(), FilePickerActivityHelper.class)
.putExtra(FilePickerActivityHelper.EXTRA_ALLOW_MULTIPLE, false)
.putExtra(FilePickerActivityHelper.EXTRA_ALLOW_CREATE_DIR, true)
.putExtra(FilePickerActivityHelper.EXTRA_MODE, FilePickerActivityHelper.MODE_DIR);
if (preference.getKey().equals(DOWNLOAD_PATH_PREFERENCE)) {
startActivityForResult(i, REQUEST_DOWNLOAD_PATH);
} else if (preference.getKey().equals(DOWNLOAD_PATH_AUDIO_PREFERENCE)) {

View File

@@ -19,7 +19,7 @@ public class AnimationUtils {
private static final boolean DEBUG = MainActivity.DEBUG;
public enum Type {
ALPHA, SCALE_AND_ALPHA, LIGHT_SCALE_AND_ALPHA
ALPHA, SCALE_AND_ALPHA, LIGHT_SCALE_AND_ALPHA, SLIDE_AND_ALPHA, LIGHT_SLIDE_AND_ALPHA
}
public static void animateView(View view, boolean enterOrExit, long duration) {
@@ -95,9 +95,16 @@ public class AnimationUtils {
case LIGHT_SCALE_AND_ALPHA:
animateLightScaleAndAlpha(view, enterOrExit, duration, delay, execOnEnd);
break;
case SLIDE_AND_ALPHA:
animateSlideAndAlpha(view, enterOrExit, duration, delay, execOnEnd);
break;
case LIGHT_SLIDE_AND_ALPHA:
animateLightSlideAndAlpha(view, enterOrExit, duration, delay, execOnEnd);
break;
}
}
/**
* Animate the background color of a view
*/
@@ -237,4 +244,50 @@ public class AnimationUtils {
}).start();
}
}
private static void animateSlideAndAlpha(final View view, boolean enterOrExit, long duration, long delay, final Runnable execOnEnd) {
if (enterOrExit) {
view.setTranslationY(-view.getHeight());
view.setAlpha(0f);
view.animate().setInterpolator(new FastOutSlowInInterpolator()).alpha(1f).translationY(0)
.setDuration(duration).setStartDelay(delay).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (execOnEnd != null) execOnEnd.run();
}
}).start();
} else {
view.animate().setInterpolator(new FastOutSlowInInterpolator()).alpha(0f).translationY(-view.getHeight())
.setDuration(duration).setStartDelay(delay).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
view.setVisibility(View.GONE);
if (execOnEnd != null) execOnEnd.run();
}
}).start();
}
}
private static void animateLightSlideAndAlpha(final View view, boolean enterOrExit, long duration, long delay, final Runnable execOnEnd) {
if (enterOrExit) {
view.setTranslationY(-view.getHeight() / 2);
view.setAlpha(0f);
view.animate().setInterpolator(new FastOutSlowInInterpolator()).alpha(1f).translationY(0)
.setDuration(duration).setStartDelay(delay).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (execOnEnd != null) execOnEnd.run();
}
}).start();
} else {
view.animate().setInterpolator(new FastOutSlowInInterpolator()).alpha(0f).translationY(-view.getHeight() / 2)
.setDuration(duration).setStartDelay(delay).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
view.setVisibility(View.GONE);
if (execOnEnd != null) execOnEnd.run();
}
}).start();
}
}
}

View File

@@ -9,4 +9,6 @@ public class Constants {
public static final String KEY_QUERY = "key_query";
public static final String KEY_THEME_CHANGE = "key_theme_change";
public static final int NO_SERVICE_ID = -1;
}

View File

@@ -50,7 +50,14 @@ public final class ExtractorHelper {
//no instance
}
private static void checkServiceId(int serviceId) {
if(serviceId == Constants.NO_SERVICE_ID) {
throw new IllegalArgumentException("serviceId is NO_SERVICE_ID");
}
}
public static Single<SearchResult> searchFor(final int serviceId, final String query, final int pageNumber, final String searchLanguage, final SearchEngine.Filter filter) {
checkServiceId(serviceId);
return Single.fromCallable(new Callable<SearchResult>() {
@Override
public SearchResult call() throws Exception {
@@ -61,6 +68,7 @@ public final class ExtractorHelper {
}
public static Single<NextItemsResult> getMoreSearchItems(final int serviceId, final String query, final int nextPageNumber, final String searchLanguage, final SearchEngine.Filter filter) {
checkServiceId(serviceId);
return searchFor(serviceId, query, nextPageNumber, searchLanguage, filter)
.map(new Function<SearchResult, NextItemsResult>() {
@Override
@@ -71,6 +79,7 @@ public final class ExtractorHelper {
}
public static Single<List<String>> suggestionsFor(final int serviceId, final String query, final String searchLanguage) {
checkServiceId(serviceId);
return Single.fromCallable(new Callable<List<String>>() {
@Override
public List<String> call() throws Exception {
@@ -80,6 +89,7 @@ public final class ExtractorHelper {
}
public static Single<StreamInfo> getStreamInfo(final int serviceId, final String url, boolean forceLoad) {
checkServiceId(serviceId);
return checkCache(forceLoad, serviceId, url, Single.fromCallable(new Callable<StreamInfo>() {
@Override
public StreamInfo call() throws Exception {
@@ -89,6 +99,7 @@ public final class ExtractorHelper {
}
public static Single<ChannelInfo> getChannelInfo(final int serviceId, final String url, boolean forceLoad) {
checkServiceId(serviceId);
return checkCache(forceLoad, serviceId, url, Single.fromCallable(new Callable<ChannelInfo>() {
@Override
public ChannelInfo call() throws Exception {
@@ -98,6 +109,7 @@ public final class ExtractorHelper {
}
public static Single<NextItemsResult> getMoreChannelItems(final int serviceId, final String url, final String nextStreamsUrl) {
checkServiceId(serviceId);
return Single.fromCallable(new Callable<NextItemsResult>() {
@Override
public NextItemsResult call() throws Exception {
@@ -107,6 +119,7 @@ public final class ExtractorHelper {
}
public static Single<PlaylistInfo> getPlaylistInfo(final int serviceId, final String url, boolean forceLoad) {
checkServiceId(serviceId);
return checkCache(forceLoad, serviceId, url, Single.fromCallable(new Callable<PlaylistInfo>() {
@Override
public PlaylistInfo call() throws Exception {
@@ -116,6 +129,7 @@ public final class ExtractorHelper {
}
public static Single<NextItemsResult> getMorePlaylistItems(final int serviceId, final String url, final String nextStreamsUrl) {
checkServiceId(serviceId);
return Single.fromCallable(new Callable<NextItemsResult>() {
@Override
public NextItemsResult call() throws Exception {
@@ -133,6 +147,7 @@ public final class ExtractorHelper {
* and put the results in the cache.
*/
private static <I extends Info> Single<I> checkCache(boolean forceLoad, int serviceId, String url, Single<I> loadFromNetwork) {
checkServiceId(serviceId);
loadFromNetwork = loadFromNetwork.doOnSuccess(new Consumer<I>() {
@Override
public void accept(@NonNull I i) throws Exception {
@@ -157,6 +172,7 @@ public final class ExtractorHelper {
* Default implementation uses the {@link InfoCache} to get cached results
*/
public static <I extends Info> Maybe<I> loadFromCache(final int serviceId, final String url) {
checkServiceId(serviceId);
return Maybe.defer(new Callable<MaybeSource<? extends I>>() {
@Override
public MaybeSource<? extends I> call() throws Exception {

View File

@@ -0,0 +1,17 @@
package org.schabi.newpipe.util;
import android.os.Bundle;
import org.schabi.newpipe.R;
public class FilePickerActivityHelper extends com.nononsenseapps.filepicker.FilePickerActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
if(ThemeHelper.isLightThemeSelected(this)) {
this.setTheme(R.style.FilePickerThemeLight);
} else {
this.setTheme(R.style.FilePickerThemeDark);
}
super.onCreate(savedInstanceState);
}
}

View File

@@ -0,0 +1,43 @@
package org.schabi.newpipe.util;
import android.content.Context;
import android.graphics.PointF;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.LinearSmoothScroller;
import android.support.v7.widget.RecyclerView;
public class LayoutManagerSmoothScroller extends LinearLayoutManager {
public LayoutManagerSmoothScroller(Context context) {
super(context, VERTICAL, false);
}
public LayoutManagerSmoothScroller(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
RecyclerView.SmoothScroller smoothScroller = new TopSnappedSmoothScroller(recyclerView.getContext());
smoothScroller.setTargetPosition(position);
startSmoothScroll(smoothScroller);
}
private class TopSnappedSmoothScroller extends LinearSmoothScroller {
public TopSnappedSmoothScroller(Context context) {
super(context);
}
@Override
public PointF computeScrollVectorForPosition(int targetPosition) {
return LayoutManagerSmoothScroller.this
.computeScrollVectorForPosition(targetPosition);
}
@Override
protected int getVerticalSnapPreference() {
return SNAP_TO_START;
}
}
}

View File

@@ -14,7 +14,9 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.about.AboutActivity;
import org.schabi.newpipe.download.DownloadActivity;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.ServiceList;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.fragments.MainFragment;
@@ -228,13 +230,17 @@ public class NavigationHelper {
// Link handling
//////////////////////////////////////////////////////////////////////////*/
public static void openByLink(Context context, String url) throws Exception {
Intent intentByLink = getIntentByLink(context, url);
if (intentByLink == null)
throw new NullPointerException("getIntentByLink(context = [" + context + "], url = [" + url + "]) returned null");
public static boolean openByLink(Context context, String url) {
Intent intentByLink;
try {
intentByLink = getIntentByLink(context, url);
} catch (ExtractionException e) {
return false;
}
intentByLink.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intentByLink.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
context.startActivity(intentByLink);
return true;
}
private static Intent getOpenIntent(Context context, String url, int serviceId, StreamingService.LinkType type) {
@@ -245,14 +251,20 @@ public class NavigationHelper {
return mIntent;
}
private static Intent getIntentByLink(Context context, String url) throws Exception {
StreamingService service = NewPipe.getServiceByUrl(url);
public static Intent getIntentByLink(Context context, String url) throws ExtractionException {
return getIntentByLink(context, NewPipe.getServiceByUrl(url), url);
}
public static Intent getIntentByLink(Context context, StreamingService service, String url) throws ExtractionException {
if (service != ServiceList.YouTube.getService()) {
throw new ExtractionException("Service not supported at the moment");
}
int serviceId = service.getServiceId();
StreamingService.LinkType linkType = service.getLinkTypeByUrl(url);
if (linkType == StreamingService.LinkType.NONE) {
throw new Exception("Url not known to service. service=" + serviceId + " url=" + url);
throw new ExtractionException("Url not known to service. service=" + serviceId + " url=" + url);
}
url = getCleanUrl(service, url, linkType);
@@ -268,7 +280,7 @@ public class NavigationHelper {
return rIntent;
}
private static String getCleanUrl(StreamingService service, String dirtyUrl, StreamingService.LinkType linkType) throws Exception {
private static String getCleanUrl(StreamingService service, String dirtyUrl, StreamingService.LinkType linkType) throws ExtractionException {
switch (linkType) {
case STREAM:
return service.getStreamUrlIdHandler().cleanUrl(dirtyUrl);

View File

@@ -21,6 +21,7 @@ package org.schabi.newpipe.util;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -29,6 +30,7 @@ import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.MainActivity;
import java.io.File;
@@ -110,6 +112,7 @@ public class StateSaver {
/**
* Try to restore the state from memory and disk, using the {@link StateSaver.WriteRead#readFrom(Queue)} from the writeRead.
*/
@Nullable
private static SavedState tryToRestore(@NonNull SavedState savedState, @NonNull WriteRead writeRead) {
if (MainActivity.DEBUG) {
Log.d(TAG, "tryToRestore() called with: savedState = [" + savedState + "], writeRead = [" + writeRead + "]");
@@ -117,7 +120,7 @@ public class StateSaver {
FileInputStream fileInputStream = null;
try {
Queue<Object> savedObjects = stateObjectsHolder.remove(savedState.prefixFileSaved);
Queue<Object> savedObjects = stateObjectsHolder.remove(savedState.getPrefixFileSaved());
if (savedObjects != null) {
writeRead.readFrom(savedObjects);
if (MainActivity.DEBUG) {
@@ -126,8 +129,13 @@ public class StateSaver {
return savedState;
}
File file = new File(savedState.pathFileSaved);
if (!file.exists()) return null;
File file = new File(savedState.getPathFileSaved());
if (!file.exists()) {
if(MainActivity.DEBUG) {
Log.d(TAG, "Cache file doesn't exist: " + file.getAbsolutePath());
}
return null;
}
fileInputStream = new FileInputStream(file);
ObjectInputStream inputStream = new ObjectInputStream(fileInputStream);
@@ -139,7 +147,7 @@ public class StateSaver {
return savedState;
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "Failed to restore state", e);
} finally {
if (fileInputStream != null) {
try {
@@ -154,10 +162,17 @@ public class StateSaver {
/**
* @see #tryToSave(boolean, String, String, WriteRead)
*/
@Nullable
public static SavedState tryToSave(boolean isChangingConfig, @Nullable SavedState savedState, Bundle outState, WriteRead writeRead) {
String currentSavedPrefix = savedState == null || TextUtils.isEmpty(savedState.prefixFileSaved)
? System.nanoTime() - writeRead.hashCode() + ""
: savedState.prefixFileSaved;
@NonNull
String currentSavedPrefix;
if (savedState == null || TextUtils.isEmpty(savedState.getPrefixFileSaved())) {
// Generate unique prefix
currentSavedPrefix = System.nanoTime() - writeRead.hashCode() + "";
} else {
// Reuse prefix
currentSavedPrefix = savedState.getPrefixFileSaved();
}
savedState = tryToSave(isChangingConfig, currentSavedPrefix, writeRead.generateSuffix(), writeRead);
if (savedState != null) {
@@ -173,22 +188,33 @@ public class StateSaver {
* to the file with the name of prefixFileName + suffixFileName, in a cache folder got from the {@link #init(Context)}.
* <p>
* It checks if the file already exists and if it does, just return the path, so a good way to save is:
* <li> A fixed prefix for the file
* <li> A changing suffix
* <ul>
* <li> A fixed prefix for the file</li>
* <li> A changing suffix</li>
* </ul>
*
* @param isChangingConfig
* @param prefixFileName
* @param suffixFileName
* @param writeRead
*/
@Nullable
private static SavedState tryToSave(boolean isChangingConfig, final String prefixFileName, String suffixFileName, WriteRead writeRead) {
if (MainActivity.DEBUG) {
Log.d(TAG, "tryToSave() called with: isChangingConfig = [" + isChangingConfig + "], prefixFileName = [" + prefixFileName + "], suffixFileName = [" + suffixFileName + "], writeRead = [" + writeRead + "]");
}
Queue<Object> savedObjects = new LinkedList<>();
LinkedList<Object> savedObjects = new LinkedList<>();
writeRead.writeTo(savedObjects);
if (isChangingConfig) {
if (savedObjects.size() > 0) {
stateObjectsHolder.put(prefixFileName, savedObjects);
return new SavedState(prefixFileName, "");
} else return null;
} else {
if(MainActivity.DEBUG) Log.d(TAG, "Nothing to save");
return null;
}
}
FileOutputStream fileOutputStream = null;
@@ -197,8 +223,12 @@ public class StateSaver {
if (!cacheDir.exists()) throw new RuntimeException("Cache dir does not exist > " + cacheDirPath);
cacheDir = new File(cacheDir, CACHE_DIR_NAME);
if (!cacheDir.exists()) {
boolean mkdirResult = cacheDir.mkdir();
if (!mkdirResult) return null;
if(!cacheDir.mkdir()) {
if(BuildConfig.DEBUG) {
Log.e(TAG, "Failed to create cache directory " + cacheDir.getAbsolutePath());
}
return null;
}
}
if (TextUtils.isEmpty(suffixFileName)) suffixFileName = ".cache";
@@ -214,7 +244,9 @@ public class StateSaver {
return name.contains(prefixFileName);
}
});
for (File file1 : files) file1.delete();
for (File fileToDelete : files) {
fileToDelete.delete();
}
}
fileOutputStream = new FileOutputStream(file);
@@ -223,7 +255,7 @@ public class StateSaver {
return new SavedState(prefixFileName, file.getAbsolutePath());
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "Failed to save state", e);
} finally {
if (fileOutputStream != null) {
try {
@@ -241,11 +273,11 @@ public class StateSaver {
public static void onDestroy(SavedState savedState) {
if (MainActivity.DEBUG) Log.d(TAG, "onDestroy() called with: savedState = [" + savedState + "]");
if (savedState != null && !TextUtils.isEmpty(savedState.pathFileSaved)) {
stateObjectsHolder.remove(savedState.prefixFileSaved);
if (savedState != null && !TextUtils.isEmpty(savedState.getPathFileSaved())) {
stateObjectsHolder.remove(savedState.getPrefixFileSaved());
try {
//noinspection ResultOfMethodCallIgnored
new File(savedState.pathFileSaved).delete();
new File(savedState.getPathFileSaved()).delete();
} catch (Exception ignored) {
}
}
@@ -271,9 +303,12 @@ public class StateSaver {
// Inner
//////////////////////////////////////////////////////////////////////////*/
/**
* Information about the saved state on the disk
*/
public static class SavedState implements Parcelable {
public String prefixFileSaved;
public String pathFileSaved;
private final String prefixFileSaved;
private final String pathFileSaved;
public SavedState(String prefixFileSaved, String pathFileSaved) {
this.prefixFileSaved = prefixFileSaved;
@@ -287,7 +322,7 @@ public class StateSaver {
@Override
public String toString() {
return prefixFileSaved + " > " + pathFileSaved;
return getPrefixFileSaved() + " > " + getPathFileSaved();
}
@Override
@@ -313,6 +348,22 @@ public class StateSaver {
return new SavedState[size];
}
};
/**
* Get the prefix of the saved file
* @return the file prefix
*/
public String getPrefixFileSaved() {
return prefixFileSaved;
}
/**
* Get the path to the saved file
* @return the path to the saved file
*/
public String getPathFileSaved() {
return pathFileSaved;
}
}

View File

@@ -101,6 +101,18 @@ public class DownloadManagerImpl implements DownloadManager {
}
/**
* Sort a list of mission by its timestamp. Oldest first
* @param missions the missions to sort
*/
static void sortByTimestamp(List<DownloadMission> missions) {
Collections.sort(missions, new Comparator<DownloadMission>() {
@Override
public int compare(DownloadMission o1, DownloadMission o2) {
return Long.valueOf(o1.timestamp).compareTo(o2.timestamp);
}
});
}
/**
* Loads finished missions from the data source
@@ -111,12 +123,8 @@ public class DownloadManagerImpl implements DownloadManager {
finishedMissions = new ArrayList<>();
}
// Ensure its sorted
Collections.sort(finishedMissions, new Comparator<DownloadMission>() {
@Override
public int compare(DownloadMission o1, DownloadMission o2) {
return (int) (o1.timestamp - o2.timestamp);
}
});
sortByTimestamp(finishedMissions);
mMissions.ensureCapacity(mMissions.size() + finishedMissions.size());
for (DownloadMission mission : finishedMissions) {
File downloadedFile = mission.getDownloadedFile();

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