mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-09-24 08:40:51 +02:00
Compare commits
315 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
88b29cbbf9 | ||
![]() |
51d4d0d3dc | ||
![]() |
a482aa1e21 | ||
![]() |
040b38689d | ||
![]() |
8e8e53c4d5 | ||
![]() |
d717c6d2f6 | ||
![]() |
9587ce97a8 | ||
![]() |
aaaf573475 | ||
![]() |
6c6f322d90 | ||
![]() |
c03f0ed1fb | ||
![]() |
55287393be | ||
![]() |
9575f92165 | ||
![]() |
a5cbaad804 | ||
![]() |
f492414b5e | ||
![]() |
3e6ddf7176 | ||
![]() |
29ffb05653 | ||
![]() |
50c3ee2e9c | ||
![]() |
6292470677 | ||
![]() |
8d912f2673 | ||
![]() |
227d129c3a | ||
![]() |
d9eeb6afa0 | ||
![]() |
478aecb0f4 | ||
![]() |
abb82154c9 | ||
![]() |
03c8b6b5f5 | ||
![]() |
2281300165 | ||
![]() |
84068d101b | ||
![]() |
62020fa85b | ||
![]() |
3c8bf5ccc9 | ||
![]() |
71d5762be8 | ||
![]() |
65d5358366 | ||
![]() |
6ecdfaf19e | ||
![]() |
32bd6ae1ac | ||
![]() |
92231a1e26 | ||
![]() |
38ad4dc440 | ||
![]() |
6645a47b0e | ||
![]() |
75a2d20b0b | ||
![]() |
8c15d708e6 | ||
![]() |
539b7ad87b | ||
![]() |
fc7c2c9f5a | ||
![]() |
3cd760f654 | ||
![]() |
2ec0a5d003 | ||
![]() |
1a605e814b | ||
![]() |
fe0053a15d | ||
![]() |
014f3e5aff | ||
![]() |
4a25e3b644 | ||
![]() |
e7f59bc436 | ||
![]() |
488d1ccd5a | ||
![]() |
409b36c254 | ||
![]() |
b0e567dbfa | ||
![]() |
c6086ba281 | ||
![]() |
aede925351 | ||
![]() |
9d38c66510 | ||
![]() |
0c516189c3 | ||
![]() |
c2936ea289 | ||
![]() |
dc33460a34 | ||
![]() |
ffacc93b55 | ||
![]() |
74f0ee2718 | ||
![]() |
7189791d9f | ||
![]() |
dc18d53c8f | ||
![]() |
f71ef8e130 | ||
![]() |
6eec9d8993 | ||
![]() |
9560f98359 | ||
![]() |
217433bf69 | ||
![]() |
ff5db1b939 | ||
![]() |
455a46d3ad | ||
![]() |
039a879104 | ||
![]() |
dfba9ea53b | ||
![]() |
177cce5e8f | ||
![]() |
309d36260e | ||
![]() |
bdc73eb755 | ||
![]() |
0786750eb9 | ||
![]() |
9f5d921275 | ||
![]() |
8991b2d8e3 | ||
![]() |
79dffce59b | ||
![]() |
e0301a621b | ||
![]() |
1e4361abdc | ||
![]() |
80c26fd278 | ||
![]() |
3339531086 | ||
![]() |
07ad9fbb11 | ||
![]() |
6ddc581d76 | ||
![]() |
dfe94172aa | ||
![]() |
edb632f9c7 | ||
![]() |
7f73612b9f | ||
![]() |
7f130f18b6 | ||
![]() |
d05a4c53aa | ||
![]() |
d99a9b90a0 | ||
![]() |
f311225312 | ||
![]() |
dee17fc313 | ||
![]() |
0662538d60 | ||
![]() |
5b52ad91ac | ||
![]() |
8552c41bff | ||
![]() |
7ecd298285 | ||
![]() |
2546f515d1 | ||
![]() |
8804924d27 | ||
![]() |
e410cae141 | ||
![]() |
140cfaec90 | ||
![]() |
a0f20aac23 | ||
![]() |
93fafb362f | ||
![]() |
9152df5512 | ||
![]() |
415bcd9f7e | ||
![]() |
2224033a85 | ||
![]() |
ade0498684 | ||
![]() |
211b00fff4 | ||
![]() |
77c72d03a8 | ||
![]() |
f429d93351 | ||
![]() |
e428f8b116 | ||
![]() |
9482b9d638 | ||
![]() |
e019c9f720 | ||
![]() |
e2778366e9 | ||
![]() |
36d2f8339c | ||
![]() |
a4a0c3b9fd | ||
![]() |
1b1b6c8af8 | ||
![]() |
064fd2bc68 | ||
![]() |
13152ab6ea | ||
![]() |
1743d821eb | ||
![]() |
ff16e577ef | ||
![]() |
86062c6e94 | ||
![]() |
8ce2350563 | ||
![]() |
cb8989af7f | ||
![]() |
877fa45eb4 | ||
![]() |
e33942486d | ||
![]() |
9606e080ef | ||
![]() |
d5cd9c55be | ||
![]() |
9c197ced80 | ||
![]() |
f8424599e1 | ||
![]() |
ccee18057a | ||
![]() |
1ad0f342ad | ||
![]() |
7a16ac574b | ||
![]() |
f46738f750 | ||
![]() |
fb8ff3fece | ||
![]() |
7498dd3800 | ||
![]() |
957c31b9c5 | ||
![]() |
b260570e8c | ||
![]() |
46542747b7 | ||
![]() |
d64480fc9b | ||
![]() |
c00e694d40 | ||
![]() |
f0761cc95e | ||
![]() |
068554955c | ||
![]() |
a2f915f556 | ||
![]() |
cffd35c974 | ||
![]() |
2ef2aa98a4 | ||
![]() |
ba98b30aa4 | ||
![]() |
87c0f9c803 | ||
![]() |
ce620b9e28 | ||
![]() |
fcbb5536eb | ||
![]() |
72df6dbd80 | ||
![]() |
3a6b023dbf | ||
![]() |
8b67f1358d | ||
![]() |
2aebb3b8db | ||
![]() |
f2b38be2b0 | ||
![]() |
a43001f30d | ||
![]() |
17abe1ea5c | ||
![]() |
6aaefab618 | ||
![]() |
cd867fb4d1 | ||
![]() |
25988f61a6 | ||
![]() |
be421e580d | ||
![]() |
641ab25470 | ||
![]() |
a508539c2e | ||
![]() |
cac79d9a0d | ||
![]() |
9ede8118da | ||
![]() |
e47761750a | ||
![]() |
2f181ce7c9 | ||
![]() |
3a13d4a1de | ||
![]() |
719de00e0f | ||
![]() |
ddffe99f53 | ||
![]() |
9ade596f06 | ||
![]() |
46f413b7f2 | ||
![]() |
5fe2e7b8ad | ||
![]() |
3008cbb5f4 | ||
![]() |
f7983960e5 | ||
![]() |
bb8007bb7c | ||
![]() |
01751ba97a | ||
![]() |
f41475d11c | ||
![]() |
d8914d9b6d | ||
![]() |
94cc2ad365 | ||
![]() |
e07464b81c | ||
![]() |
9d231b55b5 | ||
![]() |
77d8dac3c1 | ||
![]() |
e160015283 | ||
![]() |
aeb0cac3ee | ||
![]() |
a539f94837 | ||
![]() |
df70751071 | ||
![]() |
e55f1dff78 | ||
![]() |
47646e1c62 | ||
![]() |
80e673f20c | ||
![]() |
9ca8c5480c | ||
![]() |
4d0d3c7ead | ||
![]() |
58137aadc9 | ||
![]() |
82a59ae479 | ||
![]() |
affd23b14e | ||
![]() |
9c7f249756 | ||
![]() |
e2a0502171 | ||
![]() |
3832a4b355 | ||
![]() |
84f059415c | ||
![]() |
bb292e3199 | ||
![]() |
70541bf561 | ||
![]() |
48dd8e88e3 | ||
![]() |
52c2e0899d | ||
![]() |
9955d5b62f | ||
![]() |
8e26247fa1 | ||
![]() |
72924e2692 | ||
![]() |
8c2c8d630f | ||
![]() |
5ccf0baa6b | ||
![]() |
8fff0ccdf2 | ||
![]() |
35264bfb97 | ||
![]() |
4c9b0dc8e9 | ||
![]() |
4deb7caa83 | ||
![]() |
45f1a512b4 | ||
![]() |
ab95fdc087 | ||
![]() |
df608b9ded | ||
![]() |
6a4c81d160 | ||
![]() |
040d658540 | ||
![]() |
cbc9913e9c | ||
![]() |
f007cbd5ee | ||
![]() |
7351591df7 | ||
![]() |
5222d4cbba | ||
![]() |
ef9966815e | ||
![]() |
ad73440ce9 | ||
![]() |
c81cfdc455 | ||
![]() |
134c3804db | ||
![]() |
e355df5eda | ||
![]() |
a68e0a95f4 | ||
![]() |
6fd6facf72 | ||
![]() |
00102d4048 | ||
![]() |
adbeff11d4 | ||
![]() |
a8fe329678 | ||
![]() |
a4b61bf730 | ||
![]() |
ee592def0c | ||
![]() |
c14e117239 | ||
![]() |
b6e94dde0c | ||
![]() |
63a4a44d7e | ||
![]() |
eb8ab5d527 | ||
![]() |
b778d40d65 | ||
![]() |
2f570672dc | ||
![]() |
9290ed490f | ||
![]() |
1bda812c75 | ||
![]() |
ab82406c98 | ||
![]() |
be763349df | ||
![]() |
91764ad601 | ||
![]() |
1c4031aa38 | ||
![]() |
c3bc648dd4 | ||
![]() |
c97d794272 | ||
![]() |
8e7d2e91e9 | ||
![]() |
cf0fbbbd3d | ||
![]() |
7d3ede7946 | ||
![]() |
1aa308eb5d | ||
![]() |
4bfd30e34a | ||
![]() |
0f46c90688 | ||
![]() |
aa2173fc4a | ||
![]() |
932cb1d19c | ||
![]() |
18b038d8e4 | ||
![]() |
2ac71c75c0 | ||
![]() |
4067ba5232 | ||
![]() |
ab47dd5a5b | ||
![]() |
3130910307 | ||
![]() |
22113439a4 | ||
![]() |
2d25a9ad7a | ||
![]() |
8f0a2cc2f0 | ||
![]() |
4ca39258c9 | ||
![]() |
d0c0238b8b | ||
![]() |
f9f08e5169 | ||
![]() |
cfc51b2401 | ||
![]() |
54c2704cb5 | ||
![]() |
9b51c946fe | ||
![]() |
06f38cbcb4 | ||
![]() |
f368d6b257 | ||
![]() |
f4301da14d | ||
![]() |
f34c09f165 | ||
![]() |
6d1db56512 | ||
![]() |
b70c07d004 | ||
![]() |
f9f48a5eb6 | ||
![]() |
14e4e73444 | ||
![]() |
f2ce4d2daf | ||
![]() |
468ebdda87 | ||
![]() |
a1f0fb3b14 | ||
![]() |
3c9f4de234 | ||
![]() |
8ae411619f | ||
![]() |
61e5c9121a | ||
![]() |
8a3cf0d5dc | ||
![]() |
ecb5df65ac | ||
![]() |
5f1e98a0d3 | ||
![]() |
3b9a477499 | ||
![]() |
e5bf98a741 | ||
![]() |
06fafc247e | ||
![]() |
e8bb17b631 | ||
![]() |
363cd07883 | ||
![]() |
2b4a9286c4 | ||
![]() |
34c985026f | ||
![]() |
4a0aa42914 | ||
![]() |
746c2a15bf | ||
![]() |
9318bb5306 | ||
![]() |
2a10ceb74f | ||
![]() |
83f4db59e2 | ||
![]() |
9f66f759ad | ||
![]() |
6f015349e8 | ||
![]() |
a37802c2b9 | ||
![]() |
4fa3baf5e1 | ||
![]() |
ef8c2c81d5 | ||
![]() |
8c80d8c457 | ||
![]() |
1596872c54 | ||
![]() |
da8873fa78 | ||
![]() |
ecd8439b3f | ||
![]() |
5d178532ac | ||
![]() |
08e40a013d | ||
![]() |
c136f7363c | ||
![]() |
a60f10d739 | ||
![]() |
ae46afcb42 | ||
![]() |
bffb9f6800 | ||
![]() |
79aa9ad04b | ||
![]() |
ff5714f04a | ||
![]() |
ce499a9766 | ||
![]() |
d3bb8b7651 | ||
![]() |
6d9c23c4cb | ||
![]() |
b95a9332a9 | ||
![]() |
609715eb5c |
1
.github/CONTRIBUTING.md
vendored
1
.github/CONTRIBUTING.md
vendored
@@ -12,6 +12,7 @@ Do not report crashes in the GitHub issue tracker. NewPipe has an automated cras
|
|||||||
* Search the [existing issues](https://github.com/theScrabi/NewPipe/issues) first to make sure your issue/feature hasn't been reported/requested before
|
* Search the [existing issues](https://github.com/theScrabi/NewPipe/issues) first to make sure your issue/feature hasn't been reported/requested before
|
||||||
* Check if this issue/feature is already fixed/implemented in the repository
|
* Check if this issue/feature is already fixed/implemented in the repository
|
||||||
* If you are an android/java developer you are always welcome to fix/implement an issue/a feature yourself
|
* If you are an android/java developer you are always welcome to fix/implement an issue/a feature yourself
|
||||||
|
* Use english
|
||||||
|
|
||||||
## Bugfixing
|
## Bugfixing
|
||||||
* If you want to help NewPipe getting bug free, you can send me a mail to tnp@newpipe.schabi.org to let me know that you intent to help, and than register at our [sentry](https://support.schabi.org) setup.
|
* If you want to help NewPipe getting bug free, you can send me a mail to tnp@newpipe.schabi.org to let me know that you intent to help, and than register at our [sentry](https://support.schabi.org) setup.
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,3 +9,4 @@
|
|||||||
/*.iml
|
/*.iml
|
||||||
gradle.properties
|
gradle.properties
|
||||||
*~
|
*~
|
||||||
|
.weblate
|
||||||
|
@@ -5,7 +5,7 @@ android:
|
|||||||
components:
|
components:
|
||||||
# The BuildTools version used by NewPipe
|
# The BuildTools version used by NewPipe
|
||||||
- tools
|
- tools
|
||||||
- build-tools-25.0.0
|
- build-tools-25.0.2
|
||||||
|
|
||||||
# The SDK version used to compile NewPipe
|
# The SDK version used to compile NewPipe
|
||||||
- android-25
|
- android-25
|
||||||
|
18
README.md
18
README.md
@@ -19,11 +19,16 @@ Project status:
|
|||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
|
|
||||||
[<img src="screenshots/screenshot_1.png" width=150>](screenshots/screenshot_1.png)
|
[<img src="screenshots/screenshot_1.png" width=160>](screenshots/screenshot_1.png)
|
||||||
[<img src="screenshots/screenshot_2.png" width=150>](screenshots/screenshot_2.png)
|
[<img src="screenshots/screenshot_2.png" width=160>](screenshots/screenshot_2.png)
|
||||||
[<img src="screenshots/screenshot_3.png" width=150>](screenshots/screenshot_3.png)
|
[<img src="screenshots/screenshot_3.png" width=160>](screenshots/screenshot_3.png)
|
||||||
[<img src="screenshots/screenshot_4.png" width=150>](screenshots/screenshot_4.png)
|
[<img src="screenshots/screenshot_4.png" width=160>](screenshots/screenshot_4.png)
|
||||||
[<img src="screenshots/screenshot_5.png" width=150>](screenshots/screenshot_5.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)
|
||||||
|
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
@@ -35,6 +40,7 @@ NewPipe does not use any Google framework libraries, or the YouTube API. It only
|
|||||||
* Display general information about a video
|
* Display general information about a video
|
||||||
* Watch YouTube videos
|
* Watch YouTube videos
|
||||||
* Listen to YouTube videos (experimental)
|
* Listen to YouTube videos (experimental)
|
||||||
|
* Popup mode (floating player)
|
||||||
* Select the streaming player to watch the video with
|
* Select the streaming player to watch the video with
|
||||||
* Download videos
|
* Download videos
|
||||||
* Download audio only
|
* Download audio only
|
||||||
@@ -46,6 +52,7 @@ NewPipe does not use any Google framework libraries, or the YouTube API. It only
|
|||||||
* Search channels
|
* Search channels
|
||||||
* Watch videos from a channel
|
* Watch videos from a channel
|
||||||
* Orbot/Tor support (not yet directly)
|
* Orbot/Tor support (not yet directly)
|
||||||
|
* 1080p/2k/4k support
|
||||||
|
|
||||||
### Coming Features
|
### Coming Features
|
||||||
|
|
||||||
@@ -56,7 +63,6 @@ NewPipe does not use any Google framework libraries, or the YouTube API. It only
|
|||||||
* Search/Watch Playlists
|
* Search/Watch Playlists
|
||||||
* Queeing videos
|
* Queeing videos
|
||||||
* Subtitles support
|
* Subtitles support
|
||||||
* 1080p support
|
|
||||||
* livestream support
|
* livestream support
|
||||||
* ... and many more
|
* ... and many more
|
||||||
|
|
||||||
|
@@ -2,14 +2,14 @@ apply plugin: 'com.android.application'
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 25
|
compileSdkVersion 25
|
||||||
buildToolsVersion '25.0.0'
|
buildToolsVersion '25.0.2'
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "org.schabi.newpipe"
|
applicationId "org.schabi.newpipe"
|
||||||
minSdkVersion 15
|
minSdkVersion 15
|
||||||
targetSdkVersion 25
|
targetSdkVersion 25
|
||||||
versionCode 27
|
versionCode 35
|
||||||
versionName "0.9.0"
|
versionName "0.9.8"
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
@@ -35,18 +35,20 @@ dependencies {
|
|||||||
testCompile 'org.mockito:mockito-core:1.10.19'
|
testCompile 'org.mockito:mockito-core:1.10.19'
|
||||||
testCompile 'org.json:json:20160810'
|
testCompile 'org.json:json:20160810'
|
||||||
|
|
||||||
compile 'com.android.support:appcompat-v7:25.1.0'
|
compile 'com.android.support:appcompat-v7:25.3.1'
|
||||||
compile 'com.android.support:support-v4:25.1.0'
|
compile 'com.android.support:support-v4:25.3.1'
|
||||||
compile 'com.android.support:design:25.1.0'
|
compile 'com.android.support:design:25.3.1'
|
||||||
compile 'com.android.support:recyclerview-v7:25.1.0'
|
compile 'com.android.support:recyclerview-v7:25.3.1'
|
||||||
|
|
||||||
|
compile 'com.google.code.gson:gson:2.7'
|
||||||
compile 'org.jsoup:jsoup:1.8.3'
|
compile 'org.jsoup:jsoup:1.8.3'
|
||||||
compile 'org.mozilla:rhino:1.7.7'
|
compile 'org.mozilla:rhino:1.7.7'
|
||||||
compile 'info.guardianproject.netcipher:netcipher:1.2'
|
|
||||||
compile 'de.hdodenhof:circleimageview:2.0.0'
|
|
||||||
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
|
|
||||||
compile 'com.github.nirhart:parallaxscroll:1.0'
|
|
||||||
compile 'com.google.code.gson:gson:2.4'
|
|
||||||
compile 'com.nononsenseapps:filepicker:3.0.0'
|
|
||||||
compile 'ch.acra:acra:4.9.0'
|
compile 'ch.acra:acra:4.9.0'
|
||||||
|
compile 'info.guardianproject.netcipher:netcipher:1.2'
|
||||||
|
|
||||||
|
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
|
||||||
|
compile 'de.hdodenhof:circleimageview:2.0.0'
|
||||||
|
compile 'com.github.nirhart:parallaxscroll:1.0'
|
||||||
|
compile 'com.nononsenseapps:filepicker:3.0.0'
|
||||||
compile 'com.google.android.exoplayer:exoplayer:r2.3.1'
|
compile 'com.google.android.exoplayer:exoplayer:r2.3.1'
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -20,10 +20,6 @@ package org.schabi.newpipe;
|
|||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Singleton:
|
* Singleton:
|
||||||
* Used to send data between certain Activity/Services within the same process.
|
* Used to send data between certain Activity/Services within the same process.
|
||||||
@@ -39,8 +35,5 @@ public class ActivityCommunicator {
|
|||||||
return activityCommunicator;
|
return activityCommunicator;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Thumbnail send from VideoItemDetailFragment to BackgroundPlayer
|
|
||||||
public volatile Bitmap backgroundPlayerThumbnail;
|
|
||||||
|
|
||||||
public volatile Class returnActivity;
|
public volatile Class returnActivity;
|
||||||
}
|
}
|
||||||
|
@@ -15,6 +15,7 @@ import org.schabi.newpipe.extractor.NewPipe;
|
|||||||
import org.schabi.newpipe.report.AcraReportSenderFactory;
|
import org.schabi.newpipe.report.AcraReportSenderFactory;
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
import org.schabi.newpipe.settings.SettingsActivity;
|
import org.schabi.newpipe.settings.SettingsActivity;
|
||||||
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
import info.guardianproject.netcipher.NetCipher;
|
import info.guardianproject.netcipher.NetCipher;
|
||||||
import info.guardianproject.netcipher.proxy.OrbotHelper;
|
import info.guardianproject.netcipher.proxy.OrbotHelper;
|
||||||
@@ -82,6 +83,8 @@ public class App extends Application {
|
|||||||
// DO NOT REMOVE THIS FUNCTION!!!
|
// DO NOT REMOVE THIS FUNCTION!!!
|
||||||
// Otherwise downloadPathPreference has invalid value.
|
// Otherwise downloadPathPreference has invalid value.
|
||||||
SettingsActivity.initSettings(this);
|
SettingsActivity.initSettings(this);
|
||||||
|
|
||||||
|
ThemeHelper.setTheme(getApplicationContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,14 @@
|
|||||||
package org.schabi.newpipe;
|
package org.schabi.newpipe;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.content.Context;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import com.nostra13.universalimageloader.core.assist.FailReason;
|
import com.nostra13.universalimageloader.core.assist.FailReason;
|
||||||
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
|
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
|
||||||
|
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Christian Schabesberger on 01.08.16.
|
* Created by Christian Schabesberger on 01.08.16.
|
||||||
@@ -33,11 +33,11 @@ import org.schabi.newpipe.extractor.NewPipe;
|
|||||||
public class ImageErrorLoadingListener implements ImageLoadingListener {
|
public class ImageErrorLoadingListener implements ImageLoadingListener {
|
||||||
|
|
||||||
private int serviceId = -1;
|
private int serviceId = -1;
|
||||||
private Activity activity = null;
|
private Context context = null;
|
||||||
private View rootView = null;
|
private View rootView = null;
|
||||||
|
|
||||||
public ImageErrorLoadingListener(Activity activity, View rootView, int serviceId) {
|
public ImageErrorLoadingListener(Context context, View rootView, int serviceId) {
|
||||||
this.activity = activity;
|
this.context = context;
|
||||||
this.serviceId= serviceId;
|
this.serviceId= serviceId;
|
||||||
this.rootView = rootView;
|
this.rootView = rootView;
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,7 @@ public class ImageErrorLoadingListener implements ImageLoadingListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
|
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
|
||||||
ErrorActivity.reportError(activity,
|
ErrorActivity.reportError(context,
|
||||||
failReason.getCause(), null, rootView,
|
failReason.getCause(), null, rootView,
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.LOAD_IMAGE,
|
ErrorActivity.ErrorInfo.make(ErrorActivity.LOAD_IMAGE,
|
||||||
NewPipe.getNameOfService(serviceId), imageUri,
|
NewPipe.getNameOfService(serviceId), imageUri,
|
||||||
|
@@ -1,71 +1,158 @@
|
|||||||
package org.schabi.newpipe;
|
/*
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.media.AudioManager;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.support.v4.app.NavUtils;
|
|
||||||
import android.support.v7.app.AppCompatActivity;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuInflater;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import org.schabi.newpipe.download.DownloadActivity;
|
|
||||||
import org.schabi.newpipe.settings.SettingsActivity;
|
|
||||||
import org.schabi.newpipe.util.PermissionHelper;
|
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by Christian Schabesberger on 02.08.16.
|
* Created by Christian Schabesberger on 02.08.16.
|
||||||
*
|
* <p>
|
||||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||||
* DownloadActivity.java is part of NewPipe.
|
* DownloadActivity.java is part of NewPipe.
|
||||||
*
|
* <p>
|
||||||
* NewPipe is free software: you can redistribute it and/or modify
|
* NewPipe is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
* <p>
|
||||||
* NewPipe is distributed in the hope that it will be useful,
|
* NewPipe is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
* <p>
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
package org.schabi.newpipe;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.support.v4.app.FragmentManager;
|
||||||
|
import android.support.v7.app.ActionBar;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.download.DownloadActivity;
|
||||||
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
|
import org.schabi.newpipe.fragments.MainFragment;
|
||||||
|
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
|
||||||
|
import org.schabi.newpipe.fragments.search.SearchFragment;
|
||||||
|
import org.schabi.newpipe.settings.SettingsActivity;
|
||||||
|
import org.schabi.newpipe.util.Constants;
|
||||||
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
|
import org.schabi.newpipe.util.PermissionHelper;
|
||||||
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity {
|
public class MainActivity extends AppCompatActivity {
|
||||||
private Fragment mainFragment = null;
|
private static final String TAG = "MainActivity";
|
||||||
private static final String TAG = MainActivity.class.toString();
|
public static final boolean DEBUG = false;
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Activity's LifeCycle
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
|
||||||
|
ThemeHelper.setTheme(this);
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
ThemeHelper.setTheme(this, true);
|
|
||||||
setContentView(R.layout.activity_main);
|
setContentView(R.layout.activity_main);
|
||||||
setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
|
||||||
mainFragment = getSupportFragmentManager()
|
if (getSupportFragmentManager() != null && getSupportFragmentManager().getBackStackEntryCount() == 0) {
|
||||||
.findFragmentById(R.id.search_fragment);
|
initFragments();
|
||||||
|
}
|
||||||
|
|
||||||
|
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
|
||||||
|
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
|
if (sharedPreferences.getBoolean(Constants.KEY_THEME_CHANGE, false)) {
|
||||||
|
if (DEBUG) Log.d(TAG, "Theme has changed, recreating activity...");
|
||||||
|
sharedPreferences.edit().putBoolean(Constants.KEY_THEME_CHANGE, false).apply();
|
||||||
|
this.recreate();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onNewIntent(Intent intent) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onNewIntent() called with: intent = [" + intent + "]");
|
||||||
|
if (intent != null) {
|
||||||
|
// Return if launched from a launcher (e.g. Nova Launcher, Pixel Launcher ...)
|
||||||
|
// to not destroy the already created backstack
|
||||||
|
String action = intent.getAction();
|
||||||
|
if ((action != null && action.equals(Intent.ACTION_MAIN)) && intent.hasCategory(Intent.CATEGORY_LAUNCHER)) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
super.onNewIntent(intent);
|
||||||
|
setIntent(intent);
|
||||||
|
handleIntent(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
if (DEBUG) Log.d(TAG, "onBackPressed() called");
|
||||||
|
|
||||||
|
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_holder);
|
||||||
|
if (fragment instanceof VideoDetailFragment) if (((VideoDetailFragment) fragment).onActivityBackPressed()) return;
|
||||||
|
|
||||||
|
super.onBackPressed();
|
||||||
|
|
||||||
|
fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_holder);
|
||||||
|
if (getSupportFragmentManager().getBackStackEntryCount() == 0 && !(fragment instanceof MainFragment)) {
|
||||||
|
super.onBackPressed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Menu
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu + "]");
|
||||||
super.onCreateOptionsMenu(menu);
|
super.onCreateOptionsMenu(menu);
|
||||||
MenuInflater inflater = getMenuInflater();
|
|
||||||
inflater.inflate(R.menu.main_menu, menu);
|
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_holder);
|
||||||
|
if (!(fragment instanceof VideoDetailFragment)) {
|
||||||
|
findViewById(R.id.toolbar).findViewById(R.id.toolbar_spinner).setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(fragment instanceof SearchFragment)) {
|
||||||
|
findViewById(R.id.toolbar).findViewById(R.id.toolbar_search_container).setVisibility(View.GONE);
|
||||||
|
|
||||||
|
MenuInflater inflater = getMenuInflater();
|
||||||
|
inflater.inflate(R.menu.main_menu, menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionBar actionBar = getSupportActionBar();
|
||||||
|
if (actionBar != null) {
|
||||||
|
actionBar.setDisplayShowTitleEnabled(false);
|
||||||
|
actionBar.setDisplayHomeAsUpEnabled(false);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onOptionsItemSelected() called with: item = [" + item + "]");
|
||||||
int id = item.getItemId();
|
int id = item.getItemId();
|
||||||
|
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case android.R.id.home: {
|
case android.R.id.home: {
|
||||||
Intent intent = new Intent(this, MainActivity.class);
|
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_holder);
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
if (fragment instanceof VideoDetailFragment) ((VideoDetailFragment) fragment).clearHistory();
|
||||||
NavUtils.navigateUpTo(this, intent);
|
|
||||||
|
getSupportFragmentManager().popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
|
||||||
|
NavigationHelper.openMainFragment(getSupportFragmentManager());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case R.id.action_settings: {
|
case R.id.action_settings: {
|
||||||
@@ -85,4 +172,45 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Init
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
private void initFragments() {
|
||||||
|
if (getIntent() != null && getIntent().hasExtra(Constants.KEY_URL)) {
|
||||||
|
handleIntent(getIntent());
|
||||||
|
} else NavigationHelper.openMainFragment(getSupportFragmentManager());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Utils
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
private void handleIntent(Intent intent) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
} 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 {
|
||||||
|
getSupportFragmentManager().popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
|
||||||
|
NavigationHelper.openMainFragment(getSupportFragmentManager());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,136 +0,0 @@
|
|||||||
package org.schabi.newpipe;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
|
||||||
import org.schabi.newpipe.player.PopupVideoPlayer;
|
|
||||||
import org.schabi.newpipe.util.NavStack;
|
|
||||||
import org.schabi.newpipe.util.PermissionHelper;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashSet;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This activity is thought to open video streams form an external app using the popup playser.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class PopupActivity extends Activity {
|
|
||||||
private static final String TAG = RouterActivity.class.toString();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes invisible separators (\p{Z}) and punctuation characters including
|
|
||||||
* brackets (\p{P}). See http://www.regular-expressions.info/unicode.html for
|
|
||||||
* more details.
|
|
||||||
*/
|
|
||||||
private final static String REGEX_REMOVE_FROM_URL = "[\\p{Z}\\p{P}]";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
handleIntent(getIntent());
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static String removeHeadingGibberish(final String input) {
|
|
||||||
int start = 0;
|
|
||||||
for (int i = input.indexOf("://") - 1; i >= 0; i--) {
|
|
||||||
if (!input.substring(i, i + 1).matches("\\p{L}")) {
|
|
||||||
start = i + 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return input.substring(start, input.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String trim(final String input) {
|
|
||||||
if (input == null || input.length() < 1) {
|
|
||||||
return input;
|
|
||||||
} else {
|
|
||||||
String output = input;
|
|
||||||
while (output.length() > 0 && output.substring(0, 1).matches(REGEX_REMOVE_FROM_URL)) {
|
|
||||||
output = output.substring(1);
|
|
||||||
}
|
|
||||||
while (output.length() > 0
|
|
||||||
&& output.substring(output.length() - 1, output.length()).matches(REGEX_REMOVE_FROM_URL)) {
|
|
||||||
output = output.substring(0, output.length() - 1);
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves all Strings which look remotely like URLs from a text.
|
|
||||||
* Used if NewPipe was called through share menu.
|
|
||||||
*
|
|
||||||
* @param sharedText text to scan for URLs.
|
|
||||||
* @return potential URLs
|
|
||||||
*/
|
|
||||||
private String[] getUris(final String sharedText) {
|
|
||||||
final Collection<String> result = new HashSet<>();
|
|
||||||
if (sharedText != null) {
|
|
||||||
final String[] array = sharedText.split("\\p{Space}");
|
|
||||||
for (String s : array) {
|
|
||||||
s = trim(s);
|
|
||||||
if (s.length() != 0) {
|
|
||||||
if (s.matches(".+://.+")) {
|
|
||||||
result.add(removeHeadingGibberish(s));
|
|
||||||
} else if (s.matches(".+\\..+")) {
|
|
||||||
result.add("http://" + s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result.toArray(new String[result.size()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleIntent(Intent intent) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
|
||||||
&& !PermissionHelper.checkSystemAlertWindowPermission(this)) {
|
|
||||||
Toast.makeText(this, R.string.msg_popup_permission, Toast.LENGTH_LONG).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String videoUrl = "";
|
|
||||||
StreamingService service = null;
|
|
||||||
|
|
||||||
// first gather data and find service
|
|
||||||
if (intent.getData() != null) {
|
|
||||||
// this means the video was called though another app
|
|
||||||
videoUrl = intent.getData().toString();
|
|
||||||
} else if (intent.getStringExtra(Intent.EXTRA_TEXT) != null) {
|
|
||||||
//this means that vidoe was called through share menu
|
|
||||||
String extraText = intent.getStringExtra(Intent.EXTRA_TEXT);
|
|
||||||
videoUrl = getUris(extraText)[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
service = NewPipe.getServiceByUrl(videoUrl);
|
|
||||||
if (service == null) {
|
|
||||||
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG)
|
|
||||||
.show();
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
Intent callIntent = new Intent();
|
|
||||||
switch (service.getLinkTypeByUrl(videoUrl)) {
|
|
||||||
case STREAM:
|
|
||||||
callIntent.setClass(this, PopupVideoPlayer.class);
|
|
||||||
break;
|
|
||||||
case PLAYLIST:
|
|
||||||
Log.e(TAG, "NOT YET DEFINED");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
callIntent.putExtra(NavStack.URL, videoUrl);
|
|
||||||
callIntent.putExtra(NavStack.SERVICE_ID, service.getServiceId());
|
|
||||||
startService(callIntent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -8,6 +8,7 @@ import android.os.Bundle;
|
|||||||
import android.support.v4.app.NavUtils;
|
import android.support.v4.app.NavUtils;
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.webkit.CookieManager;
|
import android.webkit.CookieManager;
|
||||||
import android.webkit.ValueCallback;
|
import android.webkit.ValueCallback;
|
||||||
@@ -48,10 +49,15 @@ public class ReCaptchaActivity extends AppCompatActivity {
|
|||||||
// Set return to Cancel by default
|
// Set return to Cancel by default
|
||||||
setResult(RESULT_CANCELED);
|
setResult(RESULT_CANCELED);
|
||||||
|
|
||||||
|
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
|
||||||
ActionBar actionBar = getSupportActionBar();
|
ActionBar actionBar = getSupportActionBar();
|
||||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
if (actionBar != null) {
|
||||||
actionBar.setTitle(R.string.reCaptcha_title);
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
actionBar.setDisplayShowTitleEnabled(true);
|
actionBar.setTitle(R.string.reCaptcha_title);
|
||||||
|
actionBar.setDisplayShowTitleEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
WebView myWebView = (WebView) findViewById(R.id.reCaptchaWebView);
|
WebView myWebView = (WebView) findViewById(R.id.reCaptchaWebView);
|
||||||
|
|
||||||
|
@@ -3,19 +3,14 @@ package org.schabi.newpipe;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.schabi.newpipe.detail.VideoItemDetailActivity;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
|
||||||
import org.schabi.newpipe.util.NavStack;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Copyright (C) Christian Schabesberger 2017 <chris.schabesberger@mailbox.org>
|
* Copyright (C) Christian Schabesberger 2017 <chris.schabesberger@mailbox.org>
|
||||||
* RouterActivity .java is part of NewPipe.
|
* RouterActivity .java is part of NewPipe.
|
||||||
*
|
*
|
||||||
@@ -38,24 +33,52 @@ import java.util.HashSet;
|
|||||||
* to the part of the service which can handle the url.
|
* to the part of the service which can handle the url.
|
||||||
*/
|
*/
|
||||||
public class RouterActivity extends Activity {
|
public class RouterActivity extends Activity {
|
||||||
private static final String TAG = RouterActivity.class.toString();
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
String videoUrl = getUrl(getIntent());
|
||||||
|
handleUrl(videoUrl);
|
||||||
|
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void handleUrl(String url) {
|
||||||
|
try {
|
||||||
|
NavigationHelper.openByLink(this, url);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Utils
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes invisible separators (\p{Z}) and punctuation characters including
|
* Removes invisible separators (\p{Z}) and punctuation characters including
|
||||||
* brackets (\p{P}). See http://www.regular-expressions.info/unicode.html for
|
* brackets (\p{P}). See http://www.regular-expressions.info/unicode.html for
|
||||||
* more details.
|
* more details.
|
||||||
*/
|
*/
|
||||||
private final static String REGEX_REMOVE_FROM_URL = "[\\p{Z}\\p{P}]";
|
protected final static String REGEX_REMOVE_FROM_URL = "[\\p{Z}\\p{P}]";
|
||||||
|
|
||||||
@Override
|
protected String getUrl(Intent intent) {
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
// first gather data and find service
|
||||||
super.onCreate(savedInstanceState);
|
String videoUrl = null;
|
||||||
handleIntent(getIntent());
|
if (intent.getData() != null) {
|
||||||
finish();
|
// this means the video was called though another app
|
||||||
|
videoUrl = intent.getData().toString();
|
||||||
|
} else if (intent.getStringExtra(Intent.EXTRA_TEXT) != null) {
|
||||||
|
//this means that vidoe was called through share menu
|
||||||
|
String extraText = intent.getStringExtra(Intent.EXTRA_TEXT);
|
||||||
|
videoUrl = getUris(extraText)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return videoUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected String removeHeadingGibberish(final String input) {
|
||||||
private static String removeHeadingGibberish(final String input) {
|
|
||||||
int start = 0;
|
int start = 0;
|
||||||
for (int i = input.indexOf("://") - 1; i >= 0; i--) {
|
for (int i = input.indexOf("://") - 1; i >= 0; i--) {
|
||||||
if (!input.substring(i, i + 1).matches("\\p{L}")) {
|
if (!input.substring(i, i + 1).matches("\\p{L}")) {
|
||||||
@@ -66,7 +89,7 @@ public class RouterActivity extends Activity {
|
|||||||
return input.substring(start, input.length());
|
return input.substring(start, input.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String trim(final String input) {
|
protected String trim(final String input) {
|
||||||
if (input == null || input.length() < 1) {
|
if (input == null || input.length() < 1) {
|
||||||
return input;
|
return input;
|
||||||
} else {
|
} else {
|
||||||
@@ -89,7 +112,7 @@ public class RouterActivity extends Activity {
|
|||||||
* @param sharedText text to scan for URLs.
|
* @param sharedText text to scan for URLs.
|
||||||
* @return potential URLs
|
* @return potential URLs
|
||||||
*/
|
*/
|
||||||
private String[] getUris(final String sharedText) {
|
protected String[] getUris(final String sharedText) {
|
||||||
final Collection<String> result = new HashSet<>();
|
final Collection<String> result = new HashSet<>();
|
||||||
if (sharedText != null) {
|
if (sharedText != null) {
|
||||||
final String[] array = sharedText.split("\\p{Space}");
|
final String[] array = sharedText.split("\\p{Space}");
|
||||||
@@ -107,50 +130,4 @@ public class RouterActivity extends Activity {
|
|||||||
return result.toArray(new String[result.size()]);
|
return result.toArray(new String[result.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleIntent(Intent intent) {
|
|
||||||
String videoUrl = "";
|
|
||||||
StreamingService service = null;
|
|
||||||
|
|
||||||
// first gather data and find service
|
|
||||||
if (intent.getData() != null) {
|
|
||||||
// this means the video was called though another app
|
|
||||||
videoUrl = intent.getData().toString();
|
|
||||||
} else if(intent.getStringExtra(Intent.EXTRA_TEXT) != null) {
|
|
||||||
//this means that vidoe was called through share menu
|
|
||||||
String extraText = intent.getStringExtra(Intent.EXTRA_TEXT);
|
|
||||||
videoUrl = getUris(extraText)[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
service = NewPipe.getServiceByUrl(videoUrl);
|
|
||||||
if(service == null) {
|
|
||||||
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG)
|
|
||||||
.show();
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
Intent callIntent = new Intent();
|
|
||||||
switch(service.getLinkTypeByUrl(videoUrl)) {
|
|
||||||
case CHANNEL:
|
|
||||||
callIntent.setClass(this, ChannelActivity.class);
|
|
||||||
break;
|
|
||||||
case STREAM:
|
|
||||||
callIntent.setClass(this, VideoItemDetailActivity.class);
|
|
||||||
callIntent.putExtra(VideoItemDetailActivity.AUTO_PLAY,
|
|
||||||
PreferenceManager.getDefaultSharedPreferences(this)
|
|
||||||
.getBoolean(
|
|
||||||
getString(R.string.autoplay_through_intent_key), false));
|
|
||||||
break;
|
|
||||||
case PLAYLIST:
|
|
||||||
Log.e(TAG, "NOT YET DEFINED");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG)
|
|
||||||
.show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
callIntent.putExtra(NavStack.URL, videoUrl);
|
|
||||||
callIntent.putExtra(NavStack.SERVICE_ID, service.getServiceId());
|
|
||||||
startActivity(callIntent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,44 @@
|
|||||||
|
package org.schabi.newpipe;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
|
import org.schabi.newpipe.player.PopupVideoPlayer;
|
||||||
|
import org.schabi.newpipe.util.Constants;
|
||||||
|
import org.schabi.newpipe.util.PermissionHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the url from the intent and open a popup player
|
||||||
|
*/
|
||||||
|
public class RouterPopupActivity extends RouterActivity {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void handleUrl(String url) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||||
|
&& !PermissionHelper.checkSystemAlertWindowPermission(this)) {
|
||||||
|
Toast.makeText(this, R.string.msg_popup_permission, Toast.LENGTH_LONG).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
StreamingService service = NewPipe.getServiceByUrl(url);
|
||||||
|
if (service == null) {
|
||||||
|
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Intent callIntent = new Intent(this, PopupVideoPlayer.class);
|
||||||
|
switch (service.getLinkTypeByUrl(url)) {
|
||||||
|
case STREAM:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callIntent.putExtra(Constants.KEY_URL, url);
|
||||||
|
callIntent.putExtra(Constants.KEY_SERVICE_ID, service.getServiceId());
|
||||||
|
startService(callIntent);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,28 +0,0 @@
|
|||||||
package org.schabi.newpipe;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.support.v7.app.AppCompatActivity;
|
|
||||||
|
|
||||||
public class ThemableActivity extends AppCompatActivity {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
if (PreferenceManager.getDefaultSharedPreferences(this)
|
|
||||||
.getString("theme", getResources().getString(R.string.light_theme_title)).
|
|
||||||
equals(getResources().getString(R.string.dark_theme_title))) {
|
|
||||||
setTheme(R.style.DarkTheme);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
if (PreferenceManager.getDefaultSharedPreferences(this)
|
|
||||||
.getString("theme", getResources().getString(R.string.light_theme_title)).
|
|
||||||
equals(getResources().getString(R.string.dark_theme_title))) {
|
|
||||||
setTheme(R.style.DarkTheme);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,245 +0,0 @@
|
|||||||
package org.schabi.newpipe.detail;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.support.v7.app.ActionBar;
|
|
||||||
import android.support.v7.app.AppCompatActivity;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuInflater;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
|
||||||
import org.schabi.newpipe.settings.SettingsActivity;
|
|
||||||
import org.schabi.newpipe.extractor.MediaFormat;
|
|
||||||
import org.schabi.newpipe.extractor.stream_info.VideoStream;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by Christian Schabesberger on 18.08.15.
|
|
||||||
*
|
|
||||||
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
|
||||||
* DetailsMenuHandler.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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
class ActionBarHandler {
|
|
||||||
private static final String TAG = ActionBarHandler.class.toString();
|
|
||||||
|
|
||||||
private AppCompatActivity activity;
|
|
||||||
private int selectedVideoStream = -1;
|
|
||||||
|
|
||||||
private SharedPreferences defaultPreferences;
|
|
||||||
|
|
||||||
private Menu menu;
|
|
||||||
|
|
||||||
// Only callbacks are listed here, there are more actions which don't need a callback.
|
|
||||||
// those are edited directly. Typically VideoItemDetailFragment will implement those callbacks.
|
|
||||||
private OnActionListener onShareListener;
|
|
||||||
private OnActionListener onOpenInBrowserListener;
|
|
||||||
private OnActionListener onOpenInPopupListener;
|
|
||||||
private OnActionListener onDownloadListener;
|
|
||||||
private OnActionListener onPlayWithKodiListener;
|
|
||||||
private OnActionListener onPlayAudioListener;
|
|
||||||
|
|
||||||
|
|
||||||
// Triggered when a stream related action is triggered.
|
|
||||||
public interface OnActionListener {
|
|
||||||
void onActionSelected(int selectedStreamId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ActionBarHandler(AppCompatActivity activity) {
|
|
||||||
this.activity = activity;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings({"deprecation", "ConstantConditions"})
|
|
||||||
public void setupNavMenu(AppCompatActivity activity) {
|
|
||||||
this.activity = activity;
|
|
||||||
try {
|
|
||||||
activity.getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
|
|
||||||
} catch (NullPointerException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setupStreamList(final List<VideoStream> videoStreams) {
|
|
||||||
if (activity != null) {
|
|
||||||
selectedVideoStream = 0;
|
|
||||||
|
|
||||||
|
|
||||||
// this array will be shown in the dropdown menu for selecting the stream/resolution.
|
|
||||||
String[] itemArray = new String[videoStreams.size()];
|
|
||||||
for (int i = 0; i < videoStreams.size(); i++) {
|
|
||||||
VideoStream item = videoStreams.get(i);
|
|
||||||
itemArray[i] = MediaFormat.getNameById(item.format) + " " + item.resolution;
|
|
||||||
}
|
|
||||||
int defaultResolution = getDefaultResolution(videoStreams);
|
|
||||||
|
|
||||||
ArrayAdapter<String> itemAdapter = new ArrayAdapter<>(activity.getBaseContext(),
|
|
||||||
android.R.layout.simple_spinner_dropdown_item, itemArray);
|
|
||||||
|
|
||||||
ActionBar ab = activity.getSupportActionBar();
|
|
||||||
//todo: make this throwsable
|
|
||||||
assert ab != null : "Could not get actionbar";
|
|
||||||
ab.setListNavigationCallbacks(itemAdapter
|
|
||||||
, new ActionBar.OnNavigationListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onNavigationItemSelected(int itemPosition, long itemId) {
|
|
||||||
selectedVideoStream = (int) itemId;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ab.setSelectedNavigationItem(defaultResolution);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private int getDefaultResolution(final List<VideoStream> videoStreams) {
|
|
||||||
if (defaultPreferences == null)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
String defaultResolution = defaultPreferences
|
|
||||||
.getString(activity.getString(R.string.default_resolution_key),
|
|
||||||
activity.getString(R.string.default_resolution_value));
|
|
||||||
|
|
||||||
for (int i = 0; i < videoStreams.size(); i++) {
|
|
||||||
VideoStream item = videoStreams.get(i);
|
|
||||||
if (defaultResolution.equals(item.resolution)) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// this is actually an error,
|
|
||||||
// but maybe there is really no stream fitting to the default value.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setupMenu(Menu menu, MenuInflater inflater) {
|
|
||||||
this.menu = menu;
|
|
||||||
|
|
||||||
// CAUTION set item properties programmatically otherwise it would not be accepted by
|
|
||||||
// appcompat itemsinflater.inflate(R.menu.videoitem_detail, menu);
|
|
||||||
|
|
||||||
defaultPreferences = PreferenceManager.getDefaultSharedPreferences(activity);
|
|
||||||
inflater.inflate(R.menu.videoitem_detail, menu);
|
|
||||||
|
|
||||||
showPlayWithKodiAction(defaultPreferences
|
|
||||||
.getBoolean(activity.getString(R.string.show_play_with_kodi_key), false));
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean onItemSelected(MenuItem item) {
|
|
||||||
int id = item.getItemId();
|
|
||||||
switch (id) {
|
|
||||||
case R.id.menu_item_share: {
|
|
||||||
/*
|
|
||||||
Intent intent = new Intent();
|
|
||||||
intent.setAction(Intent.ACTION_SEND);
|
|
||||||
intent.putExtra(Intent.EXTRA_TEXT, websiteUrl);
|
|
||||||
intent.setType("text/plain");
|
|
||||||
activity.startActivity(Intent.createChooser(intent, activity.getString(R.string.share_dialog_title)));
|
|
||||||
*/
|
|
||||||
if(onShareListener != null) {
|
|
||||||
onShareListener.onActionSelected(selectedVideoStream);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case R.id.menu_item_openInBrowser: {
|
|
||||||
if(onOpenInBrowserListener != null) {
|
|
||||||
onOpenInBrowserListener.onActionSelected(selectedVideoStream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
case R.id.menu_item_download:
|
|
||||||
if(onDownloadListener != null) {
|
|
||||||
onDownloadListener.onActionSelected(selectedVideoStream);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
case R.id.action_settings: {
|
|
||||||
Intent intent = new Intent(activity, SettingsActivity.class);
|
|
||||||
activity.startActivity(intent);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case R.id.action_play_with_kodi:
|
|
||||||
if(onPlayWithKodiListener != null) {
|
|
||||||
onPlayWithKodiListener.onActionSelected(selectedVideoStream);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
case R.id.menu_item_play_audio:
|
|
||||||
if(onPlayAudioListener != null) {
|
|
||||||
onPlayAudioListener.onActionSelected(selectedVideoStream);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
case R.id.menu_item_downloads: {
|
|
||||||
Intent intent =
|
|
||||||
new Intent(activity, org.schabi.newpipe.download.DownloadActivity.class);
|
|
||||||
activity.startActivity(intent);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case R.id.menu_item_popup: {
|
|
||||||
if(onOpenInPopupListener != null) {
|
|
||||||
onOpenInPopupListener.onActionSelected(selectedVideoStream);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
Log.e(TAG, "Menu Item not known");
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getSelectedVideoStream() {
|
|
||||||
return selectedVideoStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOnShareListener(OnActionListener listener) {
|
|
||||||
onShareListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOnOpenInBrowserListener(OnActionListener listener) {
|
|
||||||
onOpenInBrowserListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOnOpenInPopupListener(OnActionListener listener) {
|
|
||||||
onOpenInPopupListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOnDownloadListener(OnActionListener listener) {
|
|
||||||
onDownloadListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOnPlayWithKodiListener(OnActionListener listener) {
|
|
||||||
onPlayWithKodiListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOnPlayAudioListener(OnActionListener listener) {
|
|
||||||
onPlayAudioListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void showAudioAction(boolean visible) {
|
|
||||||
menu.findItem(R.id.menu_item_play_audio).setVisible(visible);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void showDownloadAction(boolean visible) {
|
|
||||||
menu.findItem(R.id.menu_item_download).setVisible(visible);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void showPlayWithKodiAction(boolean visible) {
|
|
||||||
menu.findItem(R.id.action_play_with_kodi).setVisible(visible);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,250 +0,0 @@
|
|||||||
package org.schabi.newpipe.detail;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
|
||||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
|
||||||
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
|
||||||
import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
|
|
||||||
import org.schabi.newpipe.extractor.stream_info.StreamExtractor;
|
|
||||||
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
|
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extract {@link StreamInfo} with {@link StreamExtractor} from the given url of the given service
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("WeakerAccess")
|
|
||||||
public class StreamExtractorWorker extends Thread {
|
|
||||||
private static final String TAG = "StreamExtractorWorker";
|
|
||||||
|
|
||||||
private Activity activity;
|
|
||||||
private final String videoUrl;
|
|
||||||
private final int serviceId;
|
|
||||||
private OnStreamInfoReceivedListener callback;
|
|
||||||
|
|
||||||
private final AtomicBoolean isRunning = new AtomicBoolean(false);
|
|
||||||
private final Handler handler = new Handler();
|
|
||||||
|
|
||||||
|
|
||||||
public interface OnStreamInfoReceivedListener {
|
|
||||||
void onReceive(StreamInfo info);
|
|
||||||
void onError(int messageId);
|
|
||||||
void onReCaptchaException();
|
|
||||||
void onBlockedByGemaError();
|
|
||||||
void onContentErrorWithMessage(int messageId);
|
|
||||||
void onContentError();
|
|
||||||
}
|
|
||||||
|
|
||||||
public StreamExtractorWorker(Activity activity, String videoUrl, int serviceId, OnStreamInfoReceivedListener callback) {
|
|
||||||
this.serviceId = serviceId;
|
|
||||||
this.videoUrl = videoUrl;
|
|
||||||
this.activity = activity;
|
|
||||||
this.callback = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a new instance <b>already</b> started of {@link StreamExtractorWorker}.<br>
|
|
||||||
* The caller is responsible to check if {@link StreamExtractorWorker#isRunning()}, or {@link StreamExtractorWorker#cancel()} it
|
|
||||||
*
|
|
||||||
* @param serviceId id of the request service
|
|
||||||
* @param url videoUrl of the service (e.g. https://www.youtube.com/watch?v=HyHNuVaZJ-k)
|
|
||||||
* @param activity activity for error reporting purposes
|
|
||||||
* @param callback listener that will be called-back when events occur (check {@link OnStreamInfoReceivedListener})
|
|
||||||
* @return new instance already started of {@link StreamExtractorWorker}
|
|
||||||
*/
|
|
||||||
public static StreamExtractorWorker startExtractorThread(int serviceId, String url, Activity activity, OnStreamInfoReceivedListener callback) {
|
|
||||||
StreamExtractorWorker extractorThread = getExtractorThread(serviceId, url, activity, callback);
|
|
||||||
extractorThread.start();
|
|
||||||
return extractorThread;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a new instance of {@link StreamExtractorWorker}.<br>
|
|
||||||
* The caller is responsible to check if {@link StreamExtractorWorker#isRunning()}, or {@link StreamExtractorWorker#cancel()}
|
|
||||||
* when it doesn't need it anymore
|
|
||||||
* <p>
|
|
||||||
* <b>Note:</b> this instance is <b>not</b> started yet
|
|
||||||
*
|
|
||||||
* @param serviceId id of the request service
|
|
||||||
* @param url videoUrl of the service (e.g. https://www.youtube.com/watch?v=HyHNuVaZJ-k)
|
|
||||||
* @param activity activity for error reporting purposes
|
|
||||||
* @param callback listener that will be called-back when events occur (check {@link OnStreamInfoReceivedListener})
|
|
||||||
* @return instance of {@link StreamExtractorWorker}
|
|
||||||
*/
|
|
||||||
public static StreamExtractorWorker getExtractorThread(int serviceId, String url, Activity activity, OnStreamInfoReceivedListener callback) {
|
|
||||||
return new StreamExtractorWorker(activity, url, serviceId, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
//Just ignore the errors for now
|
|
||||||
@SuppressWarnings("ConstantConditions")
|
|
||||||
public void run() {
|
|
||||||
// TODO: Improve error checking
|
|
||||||
// and this method in general
|
|
||||||
|
|
||||||
StreamInfo streamInfo = null;
|
|
||||||
StreamingService service;
|
|
||||||
try {
|
|
||||||
service = NewPipe.getService(serviceId);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
ErrorActivity.reportError(handler, activity, e, VideoItemDetailActivity.class, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
|
||||||
"", videoUrl, R.string.could_not_get_stream));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
isRunning.set(true);
|
|
||||||
StreamExtractor streamExtractor = service.getExtractorInstance(videoUrl);
|
|
||||||
streamInfo = StreamInfo.getVideoInfo(streamExtractor);
|
|
||||||
|
|
||||||
final StreamInfo info = streamInfo;
|
|
||||||
if (callback != null) handler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
callback.onReceive(info);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
isRunning.set(false);
|
|
||||||
// look for errors during extraction
|
|
||||||
// this if statement only covers extra information.
|
|
||||||
// if these are not available or caused an error, they are just not available
|
|
||||||
// but don't render the stream information unusalbe.
|
|
||||||
if (streamInfo != null && !streamInfo.errors.isEmpty()) {
|
|
||||||
Log.e(TAG, "OCCURRED ERRORS DURING EXTRACTION:");
|
|
||||||
for (Throwable e : streamInfo.errors) {
|
|
||||||
e.printStackTrace();
|
|
||||||
Log.e(TAG, "------");
|
|
||||||
}
|
|
||||||
|
|
||||||
View rootView = activity != null ? activity.findViewById(R.id.video_item_detail) : null;
|
|
||||||
ErrorActivity.reportError(handler, activity,
|
|
||||||
streamInfo.errors, null, rootView,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
|
||||||
service.getServiceInfo().name, videoUrl, 0 /* no message for the user */));
|
|
||||||
}
|
|
||||||
|
|
||||||
// These errors render the stream information unusable.
|
|
||||||
} catch (ReCaptchaException e) {
|
|
||||||
if (callback != null) handler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
callback.onReCaptchaException();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (callback != null) handler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
callback.onError(R.string.network_error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (callback != null) e.printStackTrace();
|
|
||||||
} catch (YoutubeStreamExtractor.DecryptException de) {
|
|
||||||
// custom service related exceptions
|
|
||||||
ErrorActivity.reportError(handler, activity, de, VideoItemDetailActivity.class, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
|
||||||
service.getServiceInfo().name, videoUrl, R.string.youtube_signature_decryption_error));
|
|
||||||
handler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
activity.finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
de.printStackTrace();
|
|
||||||
} catch (YoutubeStreamExtractor.GemaException ge) {
|
|
||||||
if (callback != null) handler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
callback.onBlockedByGemaError();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (YoutubeStreamExtractor.LiveStreamException e) {
|
|
||||||
if (callback != null) handler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
callback.onContentErrorWithMessage(R.string.live_streams_not_supported);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// ----------------------------------------
|
|
||||||
catch (StreamExtractor.ContentNotAvailableException e) {
|
|
||||||
if (callback != null) handler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
callback.onContentError();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (StreamInfo.StreamExctractException e) {
|
|
||||||
if (!streamInfo.errors.isEmpty()) {
|
|
||||||
// !!! if this case ever kicks in someone gets kicked out !!!
|
|
||||||
ErrorActivity.reportError(handler, activity, e, VideoItemDetailActivity.class, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
|
||||||
service.getServiceInfo().name, videoUrl, R.string.could_not_get_stream));
|
|
||||||
} else {
|
|
||||||
ErrorActivity.reportError(handler, activity, streamInfo.errors, VideoItemDetailActivity.class, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
|
||||||
service.getServiceInfo().name, videoUrl, R.string.could_not_get_stream));
|
|
||||||
}
|
|
||||||
handler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
activity.finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (ParsingException e) {
|
|
||||||
ErrorActivity.reportError(handler, activity, e, VideoItemDetailActivity.class, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
|
||||||
service.getServiceInfo().name, videoUrl, R.string.parsing_error));
|
|
||||||
handler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
activity.finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (Exception e) {
|
|
||||||
ErrorActivity.reportError(handler, activity, e, VideoItemDetailActivity.class, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
|
||||||
service.getServiceInfo().name, videoUrl, R.string.general_error));
|
|
||||||
handler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
activity.finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if the extraction is not completed yet
|
|
||||||
*
|
|
||||||
* @return the value of the AtomicBoolean {@link #isRunning}
|
|
||||||
*/
|
|
||||||
public boolean isRunning() {
|
|
||||||
return isRunning.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancel this ExtractorThread, setting the callback to null, the AtomicBoolean {@link #isRunning} to false and interrupt this thread.
|
|
||||||
* <p>
|
|
||||||
* <b>Note:</b> Any I/O that is active in the moment that this method is called will be canceled and a Exception will be thrown, because of the {@link #interrupt()}.<br>
|
|
||||||
* This is useful when you don't want the resulting {@link StreamInfo} anymore, but don't want to waste bandwidth, otherwise it'd run till it receives the StreamInfo.
|
|
||||||
*/
|
|
||||||
public void cancel() {
|
|
||||||
this.callback = null;
|
|
||||||
this.isRunning.set(false);
|
|
||||||
this.interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,6 @@ import android.content.Intent;
|
|||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.v4.app.NavUtils;
|
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
@@ -26,13 +25,11 @@ import android.widget.TextView;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.settings.NewPipeSettings;
|
import org.schabi.newpipe.settings.NewPipeSettings;
|
||||||
import org.schabi.newpipe.settings.SettingsActivity;
|
import org.schabi.newpipe.settings.SettingsActivity;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Vector;
|
|
||||||
|
|
||||||
import us.shandian.giga.service.DownloadManagerService;
|
import us.shandian.giga.service.DownloadManagerService;
|
||||||
import us.shandian.giga.ui.fragment.AllMissionsFragment;
|
import us.shandian.giga.ui.fragment.AllMissionsFragment;
|
||||||
@@ -64,17 +61,19 @@ public class DownloadActivity extends AppCompatActivity implements AdapterView.O
|
|||||||
i.setClass(this, DownloadManagerService.class);
|
i.setClass(this, DownloadManagerService.class);
|
||||||
startService(i);
|
startService(i);
|
||||||
|
|
||||||
|
ThemeHelper.setTheme(this);
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
ThemeHelper.setTheme(this, true);
|
|
||||||
setContentView(R.layout.activity_downloader);
|
setContentView(R.layout.activity_downloader);
|
||||||
|
|
||||||
//noinspection ConstantConditions
|
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
|
||||||
// its ok if this fails, we will catch that error later, and send it as report
|
|
||||||
ActionBar actionBar = getSupportActionBar();
|
ActionBar actionBar = getSupportActionBar();
|
||||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
if (actionBar != null) {
|
||||||
actionBar.setTitle(R.string.downloads_title);
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
actionBar.setDisplayShowTitleEnabled(true);
|
actionBar.setTitle(R.string.downloads_title);
|
||||||
|
actionBar.setDisplayShowTitleEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
|
mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
|
|
||||||
@@ -159,7 +158,7 @@ public class DownloadActivity extends AppCompatActivity implements AdapterView.O
|
|||||||
name.setText(getIntent().getStringExtra("fileName"));
|
name.setText(getIntent().getStringExtra("fileName"));
|
||||||
|
|
||||||
toolbar.setTitle(R.string.add);
|
toolbar.setTitle(R.string.add);
|
||||||
toolbar.setNavigationIcon(R.drawable.ic_arrow_back_black_24dp);
|
toolbar.setNavigationIcon(ThemeHelper.isLightThemeSelected(this) ? R.drawable.ic_arrow_back_black_24dp : R.drawable.ic_arrow_back_white_24dp);
|
||||||
toolbar.inflateMenu(R.menu.dialog_url);
|
toolbar.inflateMenu(R.menu.dialog_url);
|
||||||
|
|
||||||
// Show the dialog
|
// Show the dialog
|
||||||
@@ -183,7 +182,7 @@ public class DownloadActivity extends AppCompatActivity implements AdapterView.O
|
|||||||
if (item.getItemId() == R.id.okay) {
|
if (item.getItemId() == R.id.okay) {
|
||||||
|
|
||||||
String location;
|
String location;
|
||||||
if(audioButton.isChecked()) {
|
if (audioButton.isChecked()) {
|
||||||
location = NewPipeSettings.getAudioDownloadPath(DownloadActivity.this);
|
location = NewPipeSettings.getAudioDownloadPath(DownloadActivity.this);
|
||||||
} else {
|
} else {
|
||||||
location = NewPipeSettings.getVideoDownloadPath(DownloadActivity.this);
|
location = NewPipeSettings.getVideoDownloadPath(DownloadActivity.this);
|
||||||
@@ -201,7 +200,7 @@ public class DownloadActivity extends AppCompatActivity implements AdapterView.O
|
|||||||
audioButton.isChecked(), threads.getProgress() + 1);
|
audioButton.isChecked(), threads.getProgress() + 1);
|
||||||
mFragment.notifyChange();
|
mFragment.notifyChange();
|
||||||
|
|
||||||
mPrefs.edit().putInt(THREADS, threads.getProgress() + 1).commit();
|
mPrefs.edit().putInt(THREADS, threads.getProgress() + 1).apply();
|
||||||
mPendingUrl = null;
|
mPendingUrl = null;
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,165 +0,0 @@
|
|||||||
package org.schabi.newpipe.download;
|
|
||||||
|
|
||||||
import android.app.NotificationManager;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.support.v4.app.NotificationCompat;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
|
|
||||||
import javax.net.ssl.HttpsURLConnection;
|
|
||||||
|
|
||||||
import info.guardianproject.netcipher.NetCipher;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by Christian Schabesberger on 14.08.15.
|
|
||||||
*
|
|
||||||
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
|
||||||
* FileDownloader.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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: FOR HEAVEN SAKE !!! DO NOT SIMPLY USE ASYNCTASK. MAKE THIS A PROPER SERVICE !!!
|
|
||||||
public class FileDownloader extends AsyncTask<Void, Integer, Void> {
|
|
||||||
public static final String TAG = "FileDownloader";
|
|
||||||
|
|
||||||
|
|
||||||
private NotificationManager nm;
|
|
||||||
private NotificationCompat.Builder builder;
|
|
||||||
private int notifyId = 0x1234;
|
|
||||||
private int fileSize = 0xffffffff;
|
|
||||||
|
|
||||||
private final Context context;
|
|
||||||
private final String fileURL;
|
|
||||||
private final File saveFilePath;
|
|
||||||
private final String title;
|
|
||||||
|
|
||||||
private final String debugContext;
|
|
||||||
|
|
||||||
public FileDownloader(Context context, String fileURL, File saveFilePath, String title) {
|
|
||||||
this.context = context;
|
|
||||||
this.fileURL = fileURL;
|
|
||||||
this.saveFilePath = saveFilePath;
|
|
||||||
this.title = title;
|
|
||||||
|
|
||||||
this.debugContext = "'" + fileURL +
|
|
||||||
"' => '" + saveFilePath + "'";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Downloads a file from a URL in the background using an {@link AsyncTask}.
|
|
||||||
*
|
|
||||||
* @param fileURL HTTP URL of the file to be downloaded
|
|
||||||
* @param saveFilePath path of the directory to save the file
|
|
||||||
* @param title
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
public static void downloadFile(final Context context, final String fileURL, final File saveFilePath, String title) {
|
|
||||||
new FileDownloader(context, fileURL, saveFilePath, title).execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** AsyncTask impl: executed in gui thread */
|
|
||||||
@Override
|
|
||||||
protected void onPreExecute() {
|
|
||||||
super.onPreExecute();
|
|
||||||
nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
|
||||||
Drawable icon = context.getResources().getDrawable(R.mipmap.ic_launcher);
|
|
||||||
builder = new NotificationCompat.Builder(context)
|
|
||||||
.setSmallIcon(android.R.drawable.stat_sys_download)
|
|
||||||
.setLargeIcon(((BitmapDrawable) icon).getBitmap())
|
|
||||||
.setContentTitle(saveFilePath.getName())
|
|
||||||
.setContentText(saveFilePath.getAbsolutePath())
|
|
||||||
.setProgress(fileSize, 0, false);
|
|
||||||
nm.notify(notifyId, builder.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
/** AsyncTask impl: executed in background thread does the download */
|
|
||||||
@Override
|
|
||||||
protected Void doInBackground(Void... voids) {
|
|
||||||
HttpsURLConnection con = null;
|
|
||||||
InputStream inputStream = null;
|
|
||||||
FileOutputStream outputStream = null;
|
|
||||||
try {
|
|
||||||
con = NetCipher.getHttpsURLConnection(fileURL);
|
|
||||||
int responseCode = con.getResponseCode();
|
|
||||||
|
|
||||||
// always check HTTP response code first
|
|
||||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
|
||||||
fileSize = con.getContentLength();
|
|
||||||
inputStream = new BufferedInputStream(con.getInputStream());
|
|
||||||
outputStream = new FileOutputStream(saveFilePath);
|
|
||||||
|
|
||||||
int bufferSize = 8192;
|
|
||||||
int downloaded = 0;
|
|
||||||
|
|
||||||
int bytesRead = -1;
|
|
||||||
byte[] buffer = new byte[bufferSize];
|
|
||||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
|
||||||
outputStream.write(buffer, 0, bytesRead);
|
|
||||||
downloaded += bytesRead;
|
|
||||||
if (downloaded % 50000 < bufferSize) {
|
|
||||||
publishProgress(downloaded);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
publishProgress(bufferSize);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Log.i(TAG, "No file to download. Server replied HTTP code: " + responseCode);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e(TAG, "No file to download. Server replied HTTP code: ", e);
|
|
||||||
e.printStackTrace();
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
if (outputStream != null) {
|
|
||||||
outputStream.close();
|
|
||||||
}
|
|
||||||
if (inputStream != null) {
|
|
||||||
inputStream.close();
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
if (con != null) {
|
|
||||||
con.disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onProgressUpdate(Integer... progress) {
|
|
||||||
builder.setProgress(fileSize, progress[0], false);
|
|
||||||
nm.notify(notifyId, builder.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Void aVoid) {
|
|
||||||
super.onPostExecute(aVoid);
|
|
||||||
nm.cancel(notifyId);
|
|
||||||
}
|
|
||||||
}
|
|
Submodule app/src/main/java/org/schabi/newpipe/extractor updated: 6ab3dc876e...ab530381cf
177
app/src/main/java/org/schabi/newpipe/fragments/BaseFragment.java
Normal file
177
app/src/main/java/org/schabi/newpipe/fragments/BaseFragment.java
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
package org.schabi.newpipe.fragments;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.AttrRes;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.support.v4.view.ViewCompat;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.nostra13.universalimageloader.core.DisplayImageOptions;
|
||||||
|
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||||
|
import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.MainActivity;
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||||
|
|
||||||
|
public abstract class BaseFragment extends Fragment {
|
||||||
|
protected final String TAG = "BaseFragment@" + Integer.toHexString(hashCode());
|
||||||
|
protected static final boolean DEBUG = MainActivity.DEBUG;
|
||||||
|
|
||||||
|
protected AppCompatActivity activity;
|
||||||
|
|
||||||
|
protected AtomicBoolean isLoading = new AtomicBoolean(false);
|
||||||
|
protected AtomicBoolean wasLoading = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
protected static final ImageLoader imageLoader = ImageLoader.getInstance();
|
||||||
|
protected static final DisplayImageOptions displayImageOptions =
|
||||||
|
new DisplayImageOptions.Builder().displayer(new FadeInBitmapDisplayer(400)).cacheInMemory(false).build();
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Views
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
protected Toolbar toolbar;
|
||||||
|
|
||||||
|
protected View errorPanel;
|
||||||
|
protected Button errorButtonRetry;
|
||||||
|
protected TextView errorTextView;
|
||||||
|
protected ProgressBar loadingProgressBar;
|
||||||
|
//protected SwipeRefreshLayout swipeRefreshLayout;
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Fragment's Lifecycle
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
if (DEBUG) Log.d(TAG, "onAttach() called with: context = [" + context + "]");
|
||||||
|
|
||||||
|
activity = (AppCompatActivity) context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
|
||||||
|
|
||||||
|
isLoading.set(false);
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View rootView, Bundle savedInstanceState) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onViewCreated() called with: rootView = [" + rootView + "], savedInstanceState = [" + savedInstanceState + "]");
|
||||||
|
initViews(rootView, savedInstanceState);
|
||||||
|
initListeners();
|
||||||
|
wasLoading.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
if (DEBUG) Log.d(TAG, "onDestroyView() called");
|
||||||
|
toolbar = null;
|
||||||
|
|
||||||
|
errorPanel = null;
|
||||||
|
errorButtonRetry = null;
|
||||||
|
errorTextView = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Init
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
protected void initViews(View rootView, Bundle savedInstanceState) {
|
||||||
|
toolbar = (Toolbar) activity.findViewById(R.id.toolbar);
|
||||||
|
|
||||||
|
loadingProgressBar = (ProgressBar) rootView.findViewById(R.id.loading_progress_bar);
|
||||||
|
//swipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.swipe_refresh);
|
||||||
|
|
||||||
|
errorPanel = rootView.findViewById(R.id.error_panel);
|
||||||
|
errorButtonRetry = (Button) rootView.findViewById(R.id.error_button_retry);
|
||||||
|
errorTextView = (TextView) rootView.findViewById(R.id.error_message_view);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void initListeners() {
|
||||||
|
errorButtonRetry.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
onRetryButtonClicked();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void reloadContent();
|
||||||
|
|
||||||
|
protected void onRetryButtonClicked() {
|
||||||
|
if (DEBUG) Log.d(TAG, "onRetryButtonClicked() called");
|
||||||
|
reloadContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Utils
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
protected void setErrorMessage(String message, boolean showRetryButton) {
|
||||||
|
if (errorTextView == null || activity == null) return;
|
||||||
|
|
||||||
|
errorTextView.setText(message);
|
||||||
|
if (showRetryButton) animateView(errorButtonRetry, true, 300);
|
||||||
|
else animateView(errorButtonRetry, false, 0);
|
||||||
|
|
||||||
|
animateView(errorPanel, true, 300);
|
||||||
|
isLoading.set(false);
|
||||||
|
|
||||||
|
animateView(loadingProgressBar, false, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getResourceIdFromAttr(@AttrRes int attr) {
|
||||||
|
TypedArray a = activity.getTheme().obtainStyledAttributes(new int[]{attr});
|
||||||
|
int attributeResourceId = a.getResourceId(0, 0);
|
||||||
|
a.recycle();
|
||||||
|
return attributeResourceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void showMenuTooltip(View v, String message) {
|
||||||
|
final int[] screenPos = new int[2];
|
||||||
|
final Rect displayFrame = new Rect();
|
||||||
|
v.getLocationOnScreen(screenPos);
|
||||||
|
v.getWindowVisibleDisplayFrame(displayFrame);
|
||||||
|
|
||||||
|
final Context context = v.getContext();
|
||||||
|
final int width = v.getWidth();
|
||||||
|
final int height = v.getHeight();
|
||||||
|
final int midy = screenPos[1] + height / 2;
|
||||||
|
int referenceX = screenPos[0] + width / 2;
|
||||||
|
if (ViewCompat.getLayoutDirection(v) == View.LAYOUT_DIRECTION_LTR) {
|
||||||
|
final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
|
||||||
|
referenceX = screenWidth - referenceX; // mirror
|
||||||
|
}
|
||||||
|
Toast cheatSheet = Toast.makeText(context, message, Toast.LENGTH_SHORT);
|
||||||
|
if (midy < displayFrame.height()) {
|
||||||
|
// Show along the top; follow action buttons
|
||||||
|
cheatSheet.setGravity(Gravity.TOP | Gravity.END, referenceX,
|
||||||
|
screenPos[1] + height - displayFrame.top);
|
||||||
|
} else {
|
||||||
|
// Show along the bottom center
|
||||||
|
cheatSheet.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, height);
|
||||||
|
}
|
||||||
|
cheatSheet.show();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,77 @@
|
|||||||
|
package org.schabi.newpipe.fragments;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.support.v7.app.ActionBar;
|
||||||
|
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.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.MainActivity;
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
|
|
||||||
|
public class MainFragment extends Fragment {
|
||||||
|
private final String TAG = "MainFragment@" + Integer.toHexString(hashCode());
|
||||||
|
private static final boolean DEBUG = MainActivity.DEBUG;
|
||||||
|
|
||||||
|
private AppCompatActivity activity;
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Fragment's LifeCycle
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
if (DEBUG) Log.d(TAG, "onAttach() called with: context = [" + context + "]");
|
||||||
|
activity = ((AppCompatActivity) context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onCreateView() called with: inflater = [" + inflater + "], container = [" + container + "], savedInstanceState = [" + savedInstanceState + "]");
|
||||||
|
return inflater.inflate(R.layout.fragment_main, container, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Menu
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu + "], inflater = [" + inflater + "]");
|
||||||
|
inflater.inflate(R.menu.main_fragment_menu, menu);
|
||||||
|
|
||||||
|
ActionBar supportActionBar = activity.getSupportActionBar();
|
||||||
|
if (supportActionBar != null) {
|
||||||
|
supportActionBar.setDisplayShowTitleEnabled(false);
|
||||||
|
supportActionBar.setDisplayHomeAsUpEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.action_search:
|
||||||
|
NavigationHelper.openSearchFragment(getFragmentManager(), 0, "");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,163 @@
|
|||||||
|
package org.schabi.newpipe.fragments.detail;
|
||||||
|
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.Spinner;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.extractor.stream_info.VideoStream;
|
||||||
|
import org.schabi.newpipe.util.Utils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Christian Schabesberger on 18.08.15.
|
||||||
|
* <p>
|
||||||
|
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
||||||
|
* DetailsMenuHandler.java is part of NewPipe.
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* <p>
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
class ActionBarHandler {
|
||||||
|
private static final String TAG = "ActionBarHandler";
|
||||||
|
|
||||||
|
private AppCompatActivity activity;
|
||||||
|
private int selectedVideoStream = -1;
|
||||||
|
|
||||||
|
private SharedPreferences defaultPreferences;
|
||||||
|
|
||||||
|
private Menu menu;
|
||||||
|
|
||||||
|
// Only callbacks are listed here, there are more actions which don't need a callback.
|
||||||
|
// those are edited directly. Typically VideoDetailFragment will implement those callbacks.
|
||||||
|
private OnActionListener onShareListener;
|
||||||
|
private OnActionListener onOpenInBrowserListener;
|
||||||
|
private OnActionListener onDownloadListener;
|
||||||
|
private OnActionListener onPlayWithKodiListener;
|
||||||
|
|
||||||
|
// Triggered when a stream related action is triggered.
|
||||||
|
public interface OnActionListener {
|
||||||
|
void onActionSelected(int selectedStreamId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActionBarHandler(AppCompatActivity activity) {
|
||||||
|
this.activity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setupStreamList(final List<VideoStream> videoStreams, Spinner toolbarSpinner) {
|
||||||
|
if (activity == null) return;
|
||||||
|
selectedVideoStream = 0;
|
||||||
|
|
||||||
|
int defaultResolutionIndex = Utils.getDefaultResolution(activity, videoStreams);
|
||||||
|
boolean isExternalPlayerEnabled = PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(activity.getString(R.string.use_external_video_player_key), false);
|
||||||
|
toolbarSpinner.setAdapter(new SpinnerToolbarAdapter(activity, videoStreams, isExternalPlayerEnabled));
|
||||||
|
toolbarSpinner.setSelection(defaultResolutionIndex);
|
||||||
|
toolbarSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||||
|
selectedVideoStream = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNothingSelected(AdapterView<?> parent) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setupMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
this.menu = menu;
|
||||||
|
|
||||||
|
// CAUTION set item properties programmatically otherwise it would not be accepted by
|
||||||
|
// appcompat itemsinflater.inflate(R.menu.videoitem_detail, menu);
|
||||||
|
|
||||||
|
defaultPreferences = PreferenceManager.getDefaultSharedPreferences(activity);
|
||||||
|
inflater.inflate(R.menu.video_detail_menu, menu);
|
||||||
|
|
||||||
|
updateItemsVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateItemsVisibility(){
|
||||||
|
showPlayWithKodiAction(defaultPreferences.getBoolean(activity.getString(R.string.show_play_with_kodi_key), false));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onItemSelected(MenuItem item) {
|
||||||
|
int id = item.getItemId();
|
||||||
|
switch (id) {
|
||||||
|
case R.id.menu_item_share: {
|
||||||
|
if (onShareListener != null) {
|
||||||
|
onShareListener.onActionSelected(selectedVideoStream);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case R.id.menu_item_openInBrowser: {
|
||||||
|
if (onOpenInBrowserListener != null) {
|
||||||
|
onOpenInBrowserListener.onActionSelected(selectedVideoStream);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case R.id.menu_item_download:
|
||||||
|
if (onDownloadListener != null) {
|
||||||
|
onDownloadListener.onActionSelected(selectedVideoStream);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case R.id.action_play_with_kodi:
|
||||||
|
if (onPlayWithKodiListener != null) {
|
||||||
|
onPlayWithKodiListener.onActionSelected(selectedVideoStream);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
Log.e(TAG, "Menu Item not known");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSelectedVideoStream() {
|
||||||
|
return selectedVideoStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnShareListener(OnActionListener listener) {
|
||||||
|
onShareListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnOpenInBrowserListener(OnActionListener listener) {
|
||||||
|
onOpenInBrowserListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnDownloadListener(OnActionListener listener) {
|
||||||
|
onDownloadListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnPlayWithKodiListener(OnActionListener listener) {
|
||||||
|
onPlayWithKodiListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showDownloadAction(boolean visible) {
|
||||||
|
menu.findItem(R.id.menu_item_download).setVisible(visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showPlayWithKodiAction(boolean visible) {
|
||||||
|
menu.findItem(R.id.action_play_with_kodi).setVisible(visible);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,74 @@
|
|||||||
|
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_info.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 = (ImageView) convertView.findViewById(R.id.wo_sound_icon);
|
||||||
|
TextView text = (TextView) convertView.findViewById(android.R.id.text1);
|
||||||
|
VideoStream item = (VideoStream) getItem(position);
|
||||||
|
text.setText(MediaFormat.getNameById(item.format) + " " + item.resolution);
|
||||||
|
|
||||||
|
int visibility = !showIconNoAudio ? View.GONE
|
||||||
|
: item.isVideoOnly ? View.VISIBLE
|
||||||
|
: isDropdownItem ? View.INVISIBLE
|
||||||
|
: View.GONE;
|
||||||
|
woSoundIcon.setVisibility(visibility);
|
||||||
|
|
||||||
|
return convertView;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,42 @@
|
|||||||
|
package org.schabi.newpipe.fragments.detail;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
public class StackItem implements Serializable {
|
||||||
|
private String title, url;
|
||||||
|
private StreamInfo info;
|
||||||
|
|
||||||
|
public StackItem(String url, String title) {
|
||||||
|
this.title = title;
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitle(String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInfo(StreamInfo info) {
|
||||||
|
this.info = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StreamInfo getInfo() {
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getUrl() + " > " + getTitle();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,85 @@
|
|||||||
|
package org.schabi.newpipe.fragments.detail;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.MainActivity;
|
||||||
|
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
public class StreamInfoCache {
|
||||||
|
private static String TAG = "StreamInfoCache@";
|
||||||
|
private static final boolean DEBUG = MainActivity.DEBUG;
|
||||||
|
private static final StreamInfoCache instance = new StreamInfoCache();
|
||||||
|
private static final int MAX_ITEMS_ON_CACHE = 20;
|
||||||
|
|
||||||
|
private final LinkedHashMap<String, StreamInfo> myCache = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
private StreamInfoCache() {
|
||||||
|
TAG += "" + Integer.toHexString(hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static StreamInfoCache getInstance() {
|
||||||
|
if (DEBUG) Log.d(TAG, "getInstance() called");
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasKey(@NonNull String url) {
|
||||||
|
if (DEBUG) Log.d(TAG, "hasKey() called with: url = [" + url + "]");
|
||||||
|
return !TextUtils.isEmpty(url) && myCache.containsKey(url) && myCache.get(url) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StreamInfo getFromKey(@NonNull String url) {
|
||||||
|
if (DEBUG) Log.d(TAG, "getFromKey() called with: url = [" + url + "]");
|
||||||
|
return myCache.get(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putInfo(@NonNull StreamInfo info) {
|
||||||
|
if (DEBUG) Log.d(TAG, "putInfo() called with: info = [" + info + "]");
|
||||||
|
putInfo(info.webpage_url, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putInfo(@NonNull String url, @NonNull StreamInfo info) {
|
||||||
|
if (DEBUG) Log.d(TAG, "putInfo() called with: url = [" + url + "], info = [" + info + "]");
|
||||||
|
myCache.put(url, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeInfo(@NonNull StreamInfo info) {
|
||||||
|
if (DEBUG) Log.d(TAG, "removeInfo() called with: info = [" + info + "]");
|
||||||
|
myCache.remove(info.webpage_url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeInfo(@NonNull String url) {
|
||||||
|
if (DEBUG) Log.d(TAG, "removeInfo() called with: url = [" + url + "]");
|
||||||
|
myCache.remove(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public void clearCache() {
|
||||||
|
if (DEBUG) Log.d(TAG, "clearCache() called");
|
||||||
|
myCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeOldEntries() {
|
||||||
|
if (DEBUG) Log.d(TAG, "removeOldEntries() called , size = " + getSize());
|
||||||
|
if (getSize() > MAX_ITEMS_ON_CACHE) {
|
||||||
|
Iterator<String> iterator = myCache.keySet().iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
iterator.next();
|
||||||
|
iterator.remove();
|
||||||
|
if (DEBUG) Log.d(TAG, "getSize() = " + getSize());
|
||||||
|
if (getSize() <= MAX_ITEMS_ON_CACHE) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSize() {
|
||||||
|
return myCache.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
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
Reference in New Issue
Block a user