mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-11-17 19:03:28 +01:00
Compare commits
264 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
a1266c919c | ||
|
|
a1925a0302 | ||
|
|
a7a4c03372 | ||
|
|
37201600e0 | ||
|
|
a94f40ed62 | ||
|
|
0b08cf8c76 | ||
|
|
33e29be7db | ||
|
|
bd1c7851c7 | ||
|
|
82faea5965 | ||
|
|
0bbbfd3217 | ||
|
|
fb578ecda8 | ||
|
|
e804647a65 | ||
|
|
c3c3a94593 | ||
|
|
9d55569f80 | ||
|
|
5dd8271c15 | ||
|
|
7a4a54c3ea | ||
|
|
7c9078a625 | ||
|
|
71ae342f52 | ||
|
|
b43c56085d | ||
|
|
83d2ab95e0 | ||
|
|
20a8d7372c | ||
|
|
c36ba88db7 | ||
|
|
4aa23023ee | ||
|
|
8735cf931a | ||
|
|
2473069326 | ||
|
|
03bab57a97 | ||
|
|
e3baf69533 | ||
|
|
461f747af1 | ||
|
|
028872a7d8 | ||
|
|
a1483b6c55 | ||
|
|
e406ba094c | ||
|
|
c100d15ba8 | ||
|
|
b4ea592638 | ||
|
|
16b757d9a3 | ||
|
|
2aa801a392 | ||
|
|
b838344526 | ||
|
|
233a3df222 | ||
|
|
da6661b1ea | ||
|
|
c6e120fc51 | ||
|
|
c416a1254d | ||
|
|
2aa4f6ddda | ||
|
|
129597023d | ||
|
|
02aed86b7e | ||
|
|
e44f4b5823 |
24
.travis.yml
24
.travis.yml
@@ -5,35 +5,15 @@ 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
|
||||||
|
|
||||||
# Additional components
|
# Additional components
|
||||||
- extra-android-support
|
|
||||||
- extra-android-m2repository
|
- extra-android-m2repository
|
||||||
- extra-google-m2repository
|
|
||||||
|
|
||||||
# Emulators
|
script: ./gradlew -Dorg.gradle.jvmargs=-Xmx1536m assembleDebug lintDebug testDebugUnitTest
|
||||||
- sys-img-armeabi-v7a-android-21
|
|
||||||
- sys-img-armeabi-v7a-android-19
|
|
||||||
- sys-img-armeabi-v7a-android-15
|
|
||||||
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
- ADB_INSTALL_TIMEOUT=8 # minutes (2 by default)
|
|
||||||
- GRADLE_OPTS=-Xmx512m # give gradle more memory since it seem to fail otherwise
|
|
||||||
matrix:
|
|
||||||
- ANDROID_TARGET=android-21 ANDROID_ABI=armeabi-v7a
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
- echo no | android create avd --force -n test -t $ANDROID_TARGET --abi $ANDROID_ABI
|
|
||||||
- emulator -avd test -no-skin -no-audio -no-window &
|
|
||||||
- android-wait-for-emulator
|
|
||||||
- adb shell input keyevent 82 &
|
|
||||||
|
|
||||||
script: ./gradlew --info build connectedCheck
|
|
||||||
|
|
||||||
licenses:
|
licenses:
|
||||||
- '.+'
|
- '.+'
|
||||||
|
|||||||
26
README.md
26
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,25 +40,30 @@ 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 * Open a video in Kodi
|
* Download audio only
|
||||||
|
* Open a video in Kodi
|
||||||
* Show Next/Related videos
|
* Show Next/Related videos
|
||||||
* Search YouTube in a specific language
|
* Search YouTube in a specific language
|
||||||
* Watch age restricted material
|
* Watch age restricted material
|
||||||
* Display general information about channels
|
* Display general information about channels
|
||||||
* Search channels
|
* Search channels
|
||||||
|
* Watch videos from a channel
|
||||||
|
* Orbot/Tor support (not yet directly)
|
||||||
|
* 1080p/2k/4k support
|
||||||
|
|
||||||
### Coming Features
|
### Coming Features
|
||||||
|
|
||||||
* Orbot/Tor support
|
|
||||||
* Bookmarks
|
* Bookmarks
|
||||||
* View history
|
* View history
|
||||||
* Search history
|
* Search history
|
||||||
* Subscribe to channels
|
* Subscribe to channels
|
||||||
* Watch videos from a channel
|
|
||||||
* Search/Watch Playlists
|
* Search/Watch Playlists
|
||||||
* Queeing videos
|
* Queeing videos
|
||||||
|
* Subtitles support
|
||||||
|
* livestream support
|
||||||
* ... and many more
|
* ... and many more
|
||||||
|
|
||||||
### Multiservice support
|
### Multiservice support
|
||||||
|
|||||||
@@ -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 26
|
versionCode 34
|
||||||
versionName "0.8.12"
|
versionName "0.9.7"
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
@@ -31,22 +31,24 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
testCompile 'junit:junit:4.12'
|
|
||||||
compile 'com.android.support:appcompat-v7:25.1.0'
|
|
||||||
compile 'com.android.support:support-v4:25.1.0'
|
|
||||||
compile 'com.android.support:design:25.1.0'
|
|
||||||
compile 'com.android.support:recyclerview-v7:25.1.0'
|
|
||||||
compile 'org.jsoup:jsoup:1.8.3'
|
|
||||||
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 'com.devbrackets.android:exomedia:3.1.1'
|
|
||||||
testCompile 'junit:junit:4.12'
|
testCompile 'junit:junit:4.12'
|
||||||
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.3.1'
|
||||||
|
compile 'com.android.support:support-v4:25.3.1'
|
||||||
|
compile 'com.android.support:design:25.3.1'
|
||||||
|
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.mozilla:rhino:1.7.7'
|
||||||
|
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'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,22 +19,15 @@
|
|||||||
tools:ignore="AllowBackup">
|
tools:ignore="AllowBackup">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:label="@string/app_name">
|
android:label="@string/app_name"
|
||||||
|
android:launchMode="singleTask">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
|
||||||
android:name=".detail.VideoItemDetailActivity"
|
|
||||||
android:label="@string/title_videoitem_detail"
|
|
||||||
android:launchMode="singleTask"
|
|
||||||
android:theme="@style/AppTheme">
|
|
||||||
<meta-data
|
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
|
||||||
android:value=".MainActivity" />
|
|
||||||
</activity>
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".player.PlayVideoActivity"
|
android:name=".player.PlayVideoActivity"
|
||||||
android:configChanges="orientation|keyboardHidden|screenSize"
|
android:configChanges="orientation|keyboardHidden|screenSize"
|
||||||
@@ -47,28 +40,11 @@
|
|||||||
android:label="@string/background_player_name" />
|
android:label="@string/background_player_name" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".player.ExoPlayerActivity"
|
android:name=".player.MainVideoPlayer"
|
||||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:launchMode="singleInstance"
|
android:launchMode="singleTask"
|
||||||
android:theme="@style/PlayerTheme">
|
android:theme="@style/PlayerTheme"/>
|
||||||
<intent-filter>
|
|
||||||
<action android:name="org.schabi.newpipe.exoplayer.action.VIEW" />
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
|
|
||||||
<data android:scheme="http" />
|
|
||||||
<data android:scheme="https" />
|
|
||||||
<data android:scheme="content" />
|
|
||||||
<data android:scheme="asset" />
|
|
||||||
<data android:scheme="file" />
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
|
||||||
|
|
||||||
<service
|
|
||||||
android:name=".player.BackgroundPlayer"
|
|
||||||
android:exported="false"
|
|
||||||
android:label="@string/background_player_name" />
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".settings.SettingsActivity"
|
android:name=".settings.SettingsActivity"
|
||||||
@@ -104,9 +80,6 @@
|
|||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
android:theme="@style/FilePickerTheme"/>
|
android:theme="@style/FilePickerTheme"/>
|
||||||
<activity
|
|
||||||
android:name=".ChannelActivity"
|
|
||||||
android:launchMode="singleTask" />
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ReCaptchaActivity"
|
android:name=".ReCaptchaActivity"
|
||||||
android:label="@string/reCaptchaActivity" />
|
android:label="@string/reCaptchaActivity" />
|
||||||
@@ -121,7 +94,9 @@
|
|||||||
android:resource="@xml/provider_paths" />
|
android:resource="@xml/provider_paths" />
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
<activity android:name=".RouterActivity"
|
<activity
|
||||||
|
android:name=".RouterActivity"
|
||||||
|
android:taskAffinity=""
|
||||||
android:theme="@android:style/Theme.NoDisplay">
|
android:theme="@android:style/Theme.NoDisplay">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
@@ -138,6 +113,7 @@
|
|||||||
<data android:host="www.youtube.com" />
|
<data android:host="www.youtube.com" />
|
||||||
<!-- video prefix -->
|
<!-- video prefix -->
|
||||||
<data android:pathPrefix="/v/" />
|
<data android:pathPrefix="/v/" />
|
||||||
|
<data android:pathPrefix="/embed/" />
|
||||||
<data android:pathPrefix="/watch" />
|
<data android:pathPrefix="/watch" />
|
||||||
<data android:pathPrefix="/attribution_link" />
|
<data android:pathPrefix="/attribution_link" />
|
||||||
<!-- channel prefix -->
|
<!-- channel prefix -->
|
||||||
@@ -177,9 +153,11 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity android:name=".PopupActivity"
|
<activity
|
||||||
|
android:name=".RouterPopupActivity"
|
||||||
|
android:taskAffinity=""
|
||||||
android:theme="@android:style/Theme.NoDisplay"
|
android:theme="@android:style/Theme.NoDisplay"
|
||||||
android:label="NewPipe Popup mode">
|
android:label="@string/popup_mode_share_menu_title">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH" />
|
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH" />
|
||||||
@@ -195,11 +173,9 @@
|
|||||||
<data android:host="www.youtube.com" />
|
<data android:host="www.youtube.com" />
|
||||||
<!-- video prefix -->
|
<!-- video prefix -->
|
||||||
<data android:pathPrefix="/v/" />
|
<data android:pathPrefix="/v/" />
|
||||||
|
<data android:pathPrefix="/embed/" />
|
||||||
<data android:pathPrefix="/watch" />
|
<data android:pathPrefix="/watch" />
|
||||||
<data android:pathPrefix="/attribution_link" />
|
<data android:pathPrefix="/attribution_link" />
|
||||||
<!-- channel prefix -->
|
|
||||||
<data android:pathPrefix="/channel/"/>
|
|
||||||
<data android:pathPrefix="/user/"/>
|
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
@@ -227,9 +203,7 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND" />
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
||||||
<data android:mimeType="text/plain" />
|
<data android:mimeType="text/plain" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
@@ -238,4 +212,4 @@
|
|||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -2,22 +2,15 @@ package org.schabi.newpipe;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.support.v7.app.AppCompatActivity;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
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.detail.VideoItemDetailFragment;
|
|
||||||
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.
|
||||||
*
|
*
|
||||||
@@ -40,7 +33,7 @@ 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();
|
//private static final String TAG = "RouterActivity"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes invisible separators (\p{Z}) and punctuation characters including
|
* Removes invisible separators (\p{Z}) and punctuation characters including
|
||||||
@@ -56,6 +49,25 @@ public class RouterActivity extends Activity {
|
|||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleIntent(Intent intent) {
|
||||||
|
String videoUrl = "";
|
||||||
|
|
||||||
|
// 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];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
NavigationHelper.openByLink(this, videoUrl);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static String removeHeadingGibberish(final String input) {
|
private static String removeHeadingGibberish(final String input) {
|
||||||
int start = 0;
|
int start = 0;
|
||||||
@@ -109,50 +121,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(VideoItemDetailFragment.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,24 +4,22 @@ import android.app.Activity;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
import org.schabi.newpipe.player.PopupVideoPlayer;
|
import org.schabi.newpipe.player.PopupVideoPlayer;
|
||||||
import org.schabi.newpipe.util.NavStack;
|
import org.schabi.newpipe.util.Constants;
|
||||||
import org.schabi.newpipe.util.PermissionHelper;
|
import org.schabi.newpipe.util.PermissionHelper;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This activity is thought to open video streams form an external app using the popup playser.
|
* This activity is thought to open video streams form an external app using the popup player.
|
||||||
*/
|
*/
|
||||||
|
public class RouterPopupActivity extends Activity {
|
||||||
public class PopupActivity extends Activity {
|
//private static final String TAG = "RouterPopupActivity";
|
||||||
private static final String TAG = RouterActivity.class.toString();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes invisible separators (\p{Z}) and punctuation characters including
|
* Removes invisible separators (\p{Z}) and punctuation characters including
|
||||||
@@ -38,6 +36,45 @@ public class PopupActivity extends Activity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
Intent callIntent = new Intent(this, PopupVideoPlayer.class);
|
||||||
|
switch (service.getLinkTypeByUrl(videoUrl)) {
|
||||||
|
case STREAM:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callIntent.putExtra(Constants.KEY_URL, videoUrl);
|
||||||
|
callIntent.putExtra(Constants.KEY_SERVICE_ID, service.getServiceId());
|
||||||
|
startService(callIntent);
|
||||||
|
}
|
||||||
|
|
||||||
private static 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--) {
|
||||||
@@ -90,47 +127,4 @@ public class PopupActivity extends Activity {
|
|||||||
return result.toArray(new String[result.size()]);
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -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,230 +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.extractor.exceptions.ParsingException;
|
|
||||||
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
|
||||||
import org.schabi.newpipe.extractor.stream_info.StreamExtractor;
|
|
||||||
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
|
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.R;
|
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
|
||||||
import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by Christian Schabesberger on 02.08.16.
|
|
||||||
*
|
|
||||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
|
||||||
* StreamInfoWorker.java is part of NewPipe.
|
|
||||||
*
|
|
||||||
* NewPipe is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* NewPipe is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class StreamInfoWorker {
|
|
||||||
|
|
||||||
private static final String TAG = StreamInfoWorker.class.toString();
|
|
||||||
|
|
||||||
public interface OnStreamInfoReceivedListener {
|
|
||||||
void onReceive(StreamInfo info);
|
|
||||||
void onError(int messageId);
|
|
||||||
void onReCaptchaException();
|
|
||||||
void onBlockedByGemaError();
|
|
||||||
void onContentErrorWithMessage(int messageId);
|
|
||||||
void onContentError();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class StreamExtractorRunnable implements Runnable {
|
|
||||||
private final Handler h = new Handler();
|
|
||||||
private StreamExtractor streamExtractor;
|
|
||||||
private final int serviceId;
|
|
||||||
private final String videoUrl;
|
|
||||||
private Activity a;
|
|
||||||
|
|
||||||
public StreamExtractorRunnable(Activity a, String videoUrl, int serviceId) {
|
|
||||||
this.serviceId = serviceId;
|
|
||||||
this.videoUrl = videoUrl;
|
|
||||||
this.a = a;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
StreamInfo streamInfo = null;
|
|
||||||
StreamingService service = null;
|
|
||||||
try {
|
|
||||||
service = NewPipe.getService(serviceId);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
ErrorActivity.reportError(h, a, e, VideoItemDetailFragment.class, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
|
||||||
"", videoUrl, R.string.could_not_get_stream));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
streamExtractor = service.getExtractorInstance(videoUrl);
|
|
||||||
streamInfo = StreamInfo.getVideoInfo(streamExtractor);
|
|
||||||
|
|
||||||
final StreamInfo info = streamInfo;
|
|
||||||
h.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
onStreamInfoReceivedListener.onReceive(info);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 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 = a != null ? a.findViewById(R.id.video_item_detail) : null;
|
|
||||||
ErrorActivity.reportError(h, a,
|
|
||||||
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) {
|
|
||||||
h.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
onStreamInfoReceivedListener.onReCaptchaException();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (IOException e) {
|
|
||||||
h.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
onStreamInfoReceivedListener.onError(R.string.network_error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (YoutubeStreamExtractor.DecryptException de) {
|
|
||||||
// custom service related exceptions
|
|
||||||
ErrorActivity.reportError(h, a, de, VideoItemDetailFragment.class, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
|
||||||
service.getServiceInfo().name, videoUrl, R.string.youtube_signature_decryption_error));
|
|
||||||
h.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
a.finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
de.printStackTrace();
|
|
||||||
} catch (YoutubeStreamExtractor.GemaException ge) {
|
|
||||||
h.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
onStreamInfoReceivedListener.onBlockedByGemaError();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch(YoutubeStreamExtractor.LiveStreamException e) {
|
|
||||||
h.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
onStreamInfoReceivedListener
|
|
||||||
.onContentErrorWithMessage(R.string.live_streams_not_supported);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// ----------------------------------------
|
|
||||||
catch(StreamExtractor.ContentNotAvailableException e) {
|
|
||||||
h.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
onStreamInfoReceivedListener
|
|
||||||
.onContentError();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch(StreamInfo.StreamExctractException e) {
|
|
||||||
if(!streamInfo.errors.isEmpty()) {
|
|
||||||
// !!! if this case ever kicks in someone gets kicked out !!!
|
|
||||||
ErrorActivity.reportError(h, a, e, VideoItemDetailFragment.class, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
|
||||||
service.getServiceInfo().name, videoUrl, R.string.could_not_get_stream));
|
|
||||||
} else {
|
|
||||||
ErrorActivity.reportError(h, a, streamInfo.errors, VideoItemDetailFragment.class, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
|
||||||
service.getServiceInfo().name, videoUrl, R.string.could_not_get_stream));
|
|
||||||
}
|
|
||||||
h.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
a.finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (ParsingException e) {
|
|
||||||
ErrorActivity.reportError(h, a, e, VideoItemDetailFragment.class, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
|
||||||
service.getServiceInfo().name, videoUrl, R.string.parsing_error));
|
|
||||||
h.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
a.finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch(Exception e) {
|
|
||||||
ErrorActivity.reportError(h, a, e, VideoItemDetailFragment.class, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
|
||||||
service.getServiceInfo().name, videoUrl, R.string.general_error));
|
|
||||||
h.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
a.finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static StreamInfoWorker streamInfoWorker = null;
|
|
||||||
private StreamExtractorRunnable runnable = null;
|
|
||||||
private OnStreamInfoReceivedListener onStreamInfoReceivedListener = null;
|
|
||||||
|
|
||||||
private StreamInfoWorker() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static StreamInfoWorker getInstance() {
|
|
||||||
return streamInfoWorker == null ? (streamInfoWorker = new StreamInfoWorker()) : streamInfoWorker;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void search(int serviceId, String url, Activity a) {
|
|
||||||
runnable = new StreamExtractorRunnable(a, url, serviceId);
|
|
||||||
Thread thread = new Thread(runnable);
|
|
||||||
thread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOnStreamInfoReceivedListener(
|
|
||||||
OnStreamInfoReceivedListener onStreamInfoReceivedListener) {
|
|
||||||
this.onStreamInfoReceivedListener = onStreamInfoReceivedListener;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,155 +0,0 @@
|
|||||||
package org.schabi.newpipe.detail;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.media.AudioManager;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.v7.app.AppCompatActivity;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.App;
|
|
||||||
import org.schabi.newpipe.R;
|
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.util.NavStack;
|
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
|
||||||
* VideoItemDetailActivity.java is part of NewPipe.
|
|
||||||
*
|
|
||||||
* NewPipe is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* NewPipe is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class VideoItemDetailActivity extends AppCompatActivity {
|
|
||||||
private static final String TAG = VideoItemDetailActivity.class.toString();
|
|
||||||
|
|
||||||
private VideoItemDetailFragment fragment;
|
|
||||||
|
|
||||||
private String videoUrl;
|
|
||||||
private int currentStreamingService = -1;
|
|
||||||
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
ThemeHelper.setTheme(this, true);
|
|
||||||
setContentView(R.layout.activity_videoitem_detail);
|
|
||||||
setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
|
||||||
// Show the Up button in the action bar.
|
|
||||||
try {
|
|
||||||
//noinspection ConstantConditions
|
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
|
||||||
} catch(Exception e) {
|
|
||||||
Log.d(TAG, "Could not get SupportActionBar");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
// savedInstanceState is non-null when there is fragment state
|
|
||||||
// saved from previous configurations of this activity
|
|
||||||
// (e.g. when rotating the screen from portrait to landscape).
|
|
||||||
// In this case, the fragment will automatically be re-added
|
|
||||||
// to its container so we don't need to manually add it.
|
|
||||||
// For more information, see the Fragments API guide at:
|
|
||||||
//
|
|
||||||
// http://developer.android.com/guide/components/fragments.html
|
|
||||||
//
|
|
||||||
|
|
||||||
if (savedInstanceState == null) {
|
|
||||||
handleIntent(getIntent());
|
|
||||||
} else {
|
|
||||||
videoUrl = savedInstanceState.getString(NavStack.URL);
|
|
||||||
currentStreamingService = savedInstanceState.getInt(NavStack.SERVICE_ID);
|
|
||||||
NavStack.getInstance()
|
|
||||||
.restoreSavedInstanceState(savedInstanceState);
|
|
||||||
addFragment(savedInstanceState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onNewIntent(Intent intent) {
|
|
||||||
super.onNewIntent(intent);
|
|
||||||
setIntent(intent);
|
|
||||||
handleIntent(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleIntent(Intent intent) {
|
|
||||||
Bundle arguments = new Bundle();
|
|
||||||
boolean autoplay = false;
|
|
||||||
|
|
||||||
videoUrl = intent.getStringExtra(NavStack.URL);
|
|
||||||
currentStreamingService = intent.getIntExtra(NavStack.SERVICE_ID, -1);
|
|
||||||
if(intent.hasExtra(VideoItemDetailFragment.AUTO_PLAY)) {
|
|
||||||
arguments.putBoolean(VideoItemDetailFragment.AUTO_PLAY,
|
|
||||||
intent.getBooleanExtra(VideoItemDetailFragment.AUTO_PLAY, false));
|
|
||||||
}
|
|
||||||
arguments.putString(NavStack.URL, videoUrl);
|
|
||||||
arguments.putInt(NavStack.SERVICE_ID, currentStreamingService);
|
|
||||||
addFragment(arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addFragment(final Bundle arguments) {
|
|
||||||
// Create the detail fragment and add it to the activity
|
|
||||||
// using a fragment transaction.
|
|
||||||
fragment = new VideoItemDetailFragment();
|
|
||||||
fragment.setArguments(arguments);
|
|
||||||
getSupportFragmentManager().beginTransaction()
|
|
||||||
.replace(R.id.videoitem_detail_container, fragment)
|
|
||||||
.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
App.checkStartTor(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
|
||||||
super.onSaveInstanceState(outState);
|
|
||||||
outState.putString(NavStack.URL, videoUrl);
|
|
||||||
outState.putInt(NavStack.SERVICE_ID, currentStreamingService);
|
|
||||||
outState.putBoolean(VideoItemDetailFragment.AUTO_PLAY, false);
|
|
||||||
NavStack.getInstance()
|
|
||||||
.onSaveInstanceState(outState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
super.onOptionsItemSelected(item);
|
|
||||||
int id = item.getItemId();
|
|
||||||
if (id == android.R.id.home) {
|
|
||||||
// This ID represents the Home or Up button. In the case of this
|
|
||||||
// activity, the Up button is shown. Use NavUtils to allow users
|
|
||||||
// to navigate up one level in the application structure. For
|
|
||||||
// more details, see the Navigation pattern on Android Design:
|
|
||||||
|
|
||||||
// http://developer.android.com/design/patterns/navigation.html#up-vs-back
|
|
||||||
|
|
||||||
NavStack.getInstance()
|
|
||||||
.openMainActivity(this);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBackPressed() {
|
|
||||||
try {
|
|
||||||
NavStack.getInstance()
|
|
||||||
.navBack(this);
|
|
||||||
} catch (Exception e) {
|
|
||||||
ErrorActivity.reportUiError(this, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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: 9005105e52...5907c35dfb
179
app/src/main/java/org/schabi/newpipe/fragments/BaseFragment.java
Normal file
179
app/src/main/java/org/schabi/newpipe/fragments/BaseFragment.java
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
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 OnItemSelectedListener onItemSelectedListener;
|
||||||
|
|
||||||
|
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;
|
||||||
|
onItemSelectedListener = (OnItemSelectedListener) 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,76 @@
|
|||||||
|
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.openSearch(activity, 0, "");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package org.schabi.newpipe.fragments;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for communication purposes between activity and fragment
|
||||||
|
*/
|
||||||
|
public interface OnItemSelectedListener {
|
||||||
|
void onItemSelected(StreamingService.LinkType linkType, int serviceId, String url, String name);
|
||||||
|
}
|
||||||
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
@@ -1,4 +1,4 @@
|
|||||||
package org.schabi.newpipe.search_fragment;
|
package org.schabi.newpipe.fragments.search;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
@@ -75,6 +75,11 @@ public class SuggestionListAdapter extends ResourceCursorAdapter {
|
|||||||
return ((Cursor) getItem(position)).getString(INDEX_TITLE);
|
return ((Cursor) getItem(position)).getString(INDEX_TITLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CharSequence convertToString(Cursor cursor) {
|
||||||
|
return cursor.getString(INDEX_TITLE);
|
||||||
|
}
|
||||||
|
|
||||||
private class ViewHolder {
|
private class ViewHolder {
|
||||||
private final TextView suggestionTitle;
|
private final TextView suggestionTitle;
|
||||||
private ViewHolder(View view) {
|
private ViewHolder(View view) {
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user