mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-09-24 08:40:51 +02:00
Compare commits
139 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
58393ad4ef | ||
![]() |
977f7e28b5 | ||
![]() |
99e77249de | ||
![]() |
3847b32c11 | ||
![]() |
9054575f6c | ||
![]() |
0dca92dd59 | ||
![]() |
b19cd00dba | ||
![]() |
88d8d90bbd | ||
![]() |
c569f08a32 | ||
![]() |
246fc034c1 | ||
![]() |
52942ffd30 | ||
![]() |
e4b0245530 | ||
![]() |
c6b8bcf0f4 | ||
![]() |
e31a8ad7a2 | ||
![]() |
b21981a9c7 | ||
![]() |
f9711a3402 | ||
![]() |
df941670a8 | ||
![]() |
57e66b17c6 | ||
![]() |
d298a12533 | ||
![]() |
a79bc3db14 | ||
![]() |
661e6155c1 | ||
![]() |
12558172d1 | ||
![]() |
dc3f55674f | ||
![]() |
acf2e88cb3 | ||
![]() |
726c12e934 | ||
![]() |
33b96d238a | ||
![]() |
213f49f5c4 | ||
![]() |
16c79c8219 | ||
![]() |
14081505cd | ||
![]() |
ebd4880188 | ||
![]() |
ffcba175ff | ||
![]() |
c7848e5e86 | ||
![]() |
6d686b93cb | ||
![]() |
2cc38f59d3 | ||
![]() |
8bf24e6b14 | ||
![]() |
10e7a5cf9c | ||
![]() |
9f2f219613 | ||
![]() |
841471bf85 | ||
![]() |
06d25b0310 | ||
![]() |
3c8d81a3c2 | ||
![]() |
cf870add49 | ||
![]() |
a962e6d633 | ||
![]() |
970ef9357b | ||
![]() |
4ba961fe7a | ||
![]() |
e6c03bf4ac | ||
![]() |
1f39523429 | ||
![]() |
b43031fb99 | ||
![]() |
986cd52da0 | ||
![]() |
bcd4579187 | ||
![]() |
6fe417abc6 | ||
![]() |
a229ab68d5 | ||
![]() |
544b30290d | ||
![]() |
cb300724da | ||
![]() |
0ac5a269ff | ||
![]() |
0009613608 | ||
![]() |
7c18d4dd01 | ||
![]() |
fe1c538f9c | ||
![]() |
f08e07873a | ||
![]() |
1193b02ca1 | ||
![]() |
c0b36b86b9 | ||
![]() |
66ec596f67 | ||
![]() |
90404a23ce | ||
![]() |
64ad05d813 | ||
![]() |
734b6e2b67 | ||
![]() |
94f992a2e2 | ||
![]() |
c8550695aa | ||
![]() |
cdac50bab3 | ||
![]() |
23961548c0 | ||
![]() |
ba1e9c8e1b | ||
![]() |
f4baf4628e | ||
![]() |
05a87da827 | ||
![]() |
fef40014a0 | ||
![]() |
1996c1176c | ||
![]() |
0190bcee25 | ||
![]() |
1ed4928f40 | ||
![]() |
63bc982cb2 | ||
![]() |
3a286515f2 | ||
![]() |
2e96b65fda | ||
![]() |
2482615460 | ||
![]() |
9384365061 | ||
![]() |
b1d4b66aa6 | ||
![]() |
ea0da5fdbd | ||
![]() |
d80b6a759c | ||
![]() |
8106ba68b5 | ||
![]() |
ee15a72e4f | ||
![]() |
2eb256799d | ||
![]() |
0cf4732d8a | ||
![]() |
53edd054aa | ||
![]() |
678f0a786a | ||
![]() |
b14f65804d | ||
![]() |
781a69d60d | ||
![]() |
eb9f300e60 | ||
![]() |
063568b620 | ||
![]() |
035c394cf6 | ||
![]() |
fad3120b00 | ||
![]() |
38c823a042 | ||
![]() |
51ee2f8d1e | ||
![]() |
d442b45836 | ||
![]() |
dbcb721dc2 | ||
![]() |
64a8f6575b | ||
![]() |
03a6b5c7b9 | ||
![]() |
56b6241311 | ||
![]() |
947ac2826a | ||
![]() |
0e8303f13a | ||
![]() |
72e9f7f9cf | ||
![]() |
ad6b676c81 | ||
![]() |
0f64158469 | ||
![]() |
acc5be92ac | ||
![]() |
0e0cee1bce | ||
![]() |
6f71c000ad | ||
![]() |
9f766ebf78 | ||
![]() |
07c63f794e | ||
![]() |
26dd86e967 | ||
![]() |
5e5e77f746 | ||
![]() |
1f309854bc | ||
![]() |
2ac0d1f13a | ||
![]() |
4eeea7b787 | ||
![]() |
e64c01d2da | ||
![]() |
0c7a91f852 | ||
![]() |
a2d93b389c | ||
![]() |
c795214abb | ||
![]() |
71822a47a5 | ||
![]() |
e1bf67c676 | ||
![]() |
8583c48264 | ||
![]() |
2a3d133bcf | ||
![]() |
3e3d1fd265 | ||
![]() |
8645618f1a | ||
![]() |
e48ce5a103 | ||
![]() |
c02ceda22f | ||
![]() |
46139340fe | ||
![]() |
7204407690 | ||
![]() |
e37336eef2 | ||
![]() |
cf21b9feaf | ||
![]() |
b74cab6642 | ||
![]() |
8267d325ed | ||
![]() |
879d7a24f0 | ||
![]() |
9e4ac2eacb | ||
![]() |
d9d6fff48f | ||
![]() |
f4fb960c62 |
10
.github/CONTRIBUTING.md
vendored
10
.github/CONTRIBUTING.md
vendored
@@ -42,10 +42,6 @@ You'll see *exactly* what is sent, be able to add **your comments**, and then se
|
|||||||
* Create PRs that cover only **one specific issue/solution/bug**. Do not create PRs that are huge monoliths and could have been split into multiple independent contributions.
|
* Create PRs that cover only **one specific issue/solution/bug**. Do not create PRs that are huge monoliths and could have been split into multiple independent contributions.
|
||||||
* NewPipe uses [NewPipeExtractor](https://github.com/TeamNewPipe/NewPipeExtractor) to fetch data from services. If you need to change something there, you must test your changes in NewPipe. Telling NewPipe to use your extractor version can be accomplished by editing the `app/build.gradle` file: the comments under the "NewPipe libraries" section of `dependencies` will help you out.
|
* NewPipe uses [NewPipeExtractor](https://github.com/TeamNewPipe/NewPipeExtractor) to fetch data from services. If you need to change something there, you must test your changes in NewPipe. Telling NewPipe to use your extractor version can be accomplished by editing the `app/build.gradle` file: the comments under the "NewPipe libraries" section of `dependencies` will help you out.
|
||||||
|
|
||||||
### Kotlin in NewPipe
|
|
||||||
* NewPipe will remain mostly Java for time being
|
|
||||||
* Contributions containing a simple conversion from Java to Kotlin should be avoided. Conversions to Kotlin should only be done if Kotlin actually brings improvements like bug fixes or better performance which are not, or only with much more effort, implementable in Java. The core team sees Java as an easier to learn and generally well adopted programming language.
|
|
||||||
|
|
||||||
### Creating a Pull Request (PR)
|
### Creating a Pull Request (PR)
|
||||||
|
|
||||||
* Make changes on a **separate branch** with a meaningful name, not on the _master_ branch or the _dev_ branch. This is commonly known as *feature branch workflow*. You may then send your changes as a pull request (PR) on GitHub.
|
* Make changes on a **separate branch** with a meaningful name, not on the _master_ branch or the _dev_ branch. This is commonly known as *feature branch workflow*. You may then send your changes as a pull request (PR) on GitHub.
|
||||||
@@ -83,6 +79,6 @@ The [ktlint](https://github.com/pinterest/ktlint) plugin does the same job as ch
|
|||||||
|
|
||||||
## Communication
|
## Communication
|
||||||
|
|
||||||
* The #newpipe channel on Libera Chat (`ircs://irc.libera.chat:6697/newpipe`) has the core team and other developers in it. [Click here for webchat](https://web.libera.chat/#newpipe)!
|
* You can use a Matrix account to join the NewPipe channel at [#newpipe:matrix.newpipe-ev.de](https://matrix.to/#/#newpipe:matrix.newpipe-ev.de). Some convenient clients, available both for phone and desktop, are listed at that link.
|
||||||
* You can also use a Matrix account to join the NewPipe channel at [#newpipe:libera.chat](https://matrix.to/#/#newpipe:libera.chat). Some convenient clients, available both for phone and desktop, are listed at that link.
|
* Alternatively, the #newpipe channel on Libera Chat (`ircs://irc.libera.chat:6697/newpipe`) can also be joined, as it is bridged to the Matrix room. [Click here for webchat](https://web.libera.chat/#newpipe)!
|
||||||
* You can post your suggestions, changes, ideas etc. on either GitHub or IRC.
|
* You can post your suggestions, changes, ideas etc. on either GitHub or Matrix (including via IRC).
|
||||||
|
6
.github/ISSUE_TEMPLATE/config.yml
vendored
6
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -3,9 +3,9 @@ contact_links:
|
|||||||
- name: ❓ Question
|
- name: ❓ Question
|
||||||
url: https://github.com/TeamNewPipe/NewPipe/discussions/new?category=questions
|
url: https://github.com/TeamNewPipe/NewPipe/discussions/new?category=questions
|
||||||
about: Ask about anything NewPipe-related
|
about: Ask about anything NewPipe-related
|
||||||
|
- name: 💬 Matrix
|
||||||
|
url: https://matrix.to/#/#newpipe:matrix.newpipe-ev.de
|
||||||
|
about: Chat with us via Matrix for quick Q/A
|
||||||
- name: 💬 IRC
|
- name: 💬 IRC
|
||||||
url: https://web.libera.chat/#newpipe
|
url: https://web.libera.chat/#newpipe
|
||||||
about: Chat with us via IRC for quick Q/A
|
about: Chat with us via IRC for quick Q/A
|
||||||
- name: 💬 Matrix
|
|
||||||
url: https://matrix.to/#/#newpipe:libera.chat
|
|
||||||
about: Chat with us via Matrix for quick Q/A
|
|
||||||
|
38
.github/workflows/build-release-apk.yml
vendored
Normal file
38
.github/workflows/build-release-apk.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
name: "Build unsigned release APK on master"
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: 'master'
|
||||||
|
|
||||||
|
- uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: 'temurin'
|
||||||
|
java-version: '17'
|
||||||
|
cache: 'gradle'
|
||||||
|
|
||||||
|
- name: "Build release APK"
|
||||||
|
run: ./gradlew assembleRelease --stacktrace
|
||||||
|
|
||||||
|
- name: "Rename APK"
|
||||||
|
run: |
|
||||||
|
VERSION_NAME="$(jq -r ".elements[0].versionName" "app/build/outputs/apk/release/output-metadata.json")"
|
||||||
|
echo "Version name: $VERSION_NAME" >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
echo '```json' >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
cat "app/build/outputs/apk/release/output-metadata.json" >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
echo >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
echo '```' >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
# assume there is only one APK in that folder
|
||||||
|
mv app/build/outputs/apk/release/*.apk "app/build/outputs/apk/release/NewPipe_v$VERSION_NAME.apk"
|
||||||
|
|
||||||
|
- name: "Upload APK"
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: app
|
||||||
|
path: app/build/outputs/apk/release/*.apk
|
22
.github/workflows/ci.yml
vendored
22
.github/workflows/ci.yml
vendored
@@ -6,6 +6,7 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- dev
|
- dev
|
||||||
- master
|
- master
|
||||||
|
- refactor
|
||||||
- release**
|
- release**
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- 'README.md'
|
- 'README.md'
|
||||||
@@ -46,10 +47,10 @@ jobs:
|
|||||||
BRANCH: ${{ github.head_ref }}
|
BRANCH: ${{ github.head_ref }}
|
||||||
run: git checkout -B "$BRANCH"
|
run: git checkout -B "$BRANCH"
|
||||||
|
|
||||||
- name: set up JDK 17
|
- name: set up JDK
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: 17
|
java-version: 21
|
||||||
distribution: "temurin"
|
distribution: "temurin"
|
||||||
cache: 'gradle'
|
cache: 'gradle'
|
||||||
|
|
||||||
@@ -63,8 +64,7 @@ jobs:
|
|||||||
path: app/build/outputs/apk/debug/*.apk
|
path: app/build/outputs/apk/debug/*.apk
|
||||||
|
|
||||||
test-android:
|
test-android:
|
||||||
# macos has hardware acceleration. See android-emulator-runner action
|
runs-on: ubuntu-latest
|
||||||
runs-on: macos-latest
|
|
||||||
timeout-minutes: 20
|
timeout-minutes: 20
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
@@ -82,10 +82,16 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: set up JDK 17
|
- name: Enable KVM
|
||||||
|
run: |
|
||||||
|
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
||||||
|
sudo udevadm control --reload-rules
|
||||||
|
sudo udevadm trigger --name-match=kvm
|
||||||
|
|
||||||
|
- name: set up JDK
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: 17
|
java-version: 21
|
||||||
distribution: "temurin"
|
distribution: "temurin"
|
||||||
cache: 'gradle'
|
cache: 'gradle'
|
||||||
|
|
||||||
@@ -115,10 +121,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
|
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
|
||||||
|
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: 17
|
java-version: 21
|
||||||
distribution: "temurin"
|
distribution: "temurin"
|
||||||
cache: 'gradle'
|
cache: 'gradle'
|
||||||
|
|
||||||
|
21
.idea/icon.svg
generated
Normal file
21
.idea/icon.svg
generated
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
|
||||||
|
viewBox="0 0 100 100" style="enable-background:new 0 0 100 100;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#CD201F;}
|
||||||
|
.st1{fill:#FFFFFF;}
|
||||||
|
</style>
|
||||||
|
<g id="Alapkör">
|
||||||
|
<circle id="XMLID_23_" class="st0" cx="50" cy="50" r="50"/>
|
||||||
|
</g>
|
||||||
|
<g id="Elemek">
|
||||||
|
<path id="XMLID_19_" class="st1" d="M47,28.2c-9-5.3-15.3-9-15.3-9v61.7c0,0,30.4-18,52.3-30.9C72.1,43,57.7,34.5,47,28.2z"/>
|
||||||
|
</g>
|
||||||
|
<g id="Fedő">
|
||||||
|
<path id="XMLID_5_" class="st0" d="M48.4,40.1c-4.1-2.4-7-4.1-7-4.1V64c0,0,13.9-8.2,23.8-14C59.8,46.8,53.3,42.9,48.4,40.1z"/>
|
||||||
|
<rect id="XMLID_4_" x="41.4" y="55.6" class="st0" width="6.2" height="21"/>
|
||||||
|
</g>
|
||||||
|
<g id="Vonalak">
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 850 B |
@@ -20,8 +20,15 @@ android {
|
|||||||
resValue "string", "app_name", "NewPipe"
|
resValue "string", "app_name", "NewPipe"
|
||||||
minSdk 21
|
minSdk 21
|
||||||
targetSdk 33
|
targetSdk 33
|
||||||
versionCode 997
|
if (System.properties.containsKey('versionCodeOverride')) {
|
||||||
versionName "0.27.0"
|
versionCode System.getProperty('versionCodeOverride') as Integer
|
||||||
|
} else {
|
||||||
|
versionCode 1002
|
||||||
|
}
|
||||||
|
versionName "0.27.5"
|
||||||
|
if (System.properties.containsKey('versionNameSuffix')) {
|
||||||
|
versionNameSuffix System.getProperty('versionNameSuffix')
|
||||||
|
}
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
@@ -92,6 +99,7 @@ android {
|
|||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
viewBinding true
|
viewBinding true
|
||||||
|
buildConfig true
|
||||||
}
|
}
|
||||||
|
|
||||||
packagingOptions {
|
packagingOptions {
|
||||||
@@ -112,7 +120,7 @@ ext {
|
|||||||
androidxRoomVersion = '2.6.1'
|
androidxRoomVersion = '2.6.1'
|
||||||
androidxWorkVersion = '2.8.1'
|
androidxWorkVersion = '2.8.1'
|
||||||
|
|
||||||
icepickVersion = '3.2.0'
|
stateSaverVersion = '1.4.1'
|
||||||
exoPlayerVersion = '2.18.7'
|
exoPlayerVersion = '2.18.7'
|
||||||
googleAutoServiceVersion = '1.1.1'
|
googleAutoServiceVersion = '1.1.1'
|
||||||
groupieVersion = '2.10.1'
|
groupieVersion = '2.10.1'
|
||||||
@@ -198,7 +206,9 @@ dependencies {
|
|||||||
// name and the commit hash with the commit hash of the (pushed) commit you want to test
|
// name and the commit hash with the commit hash of the (pushed) commit you want to test
|
||||||
// This works thanks to JitPack: https://jitpack.io/
|
// This works thanks to JitPack: https://jitpack.io/
|
||||||
implementation 'com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751'
|
implementation 'com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751'
|
||||||
implementation 'com.github.TeamNewPipe:NewPipeExtractor:v0.24.0'
|
// WORKAROUND: if you get errors with the NewPipeExtractor dependency, replace `v0.24.3` with
|
||||||
|
// the corresponding commit hash, since JitPack is sometimes buggy
|
||||||
|
implementation 'com.github.TeamNewPipe:NewPipeExtractor:v0.24.4'
|
||||||
implementation 'com.github.TeamNewPipe:NoNonsense-FilePicker:5.0.0'
|
implementation 'com.github.TeamNewPipe:NoNonsense-FilePicker:5.0.0'
|
||||||
|
|
||||||
/** Checkstyle **/
|
/** Checkstyle **/
|
||||||
@@ -234,8 +244,9 @@ dependencies {
|
|||||||
|
|
||||||
/** Third-party libraries **/
|
/** Third-party libraries **/
|
||||||
// Instance state boilerplate elimination
|
// Instance state boilerplate elimination
|
||||||
implementation "frankiesardo:icepick:${icepickVersion}"
|
implementation 'com.github.livefront:bridge:v2.0.2'
|
||||||
kapt "frankiesardo:icepick-processor:${icepickVersion}"
|
implementation "com.evernote:android-state:$stateSaverVersion"
|
||||||
|
kapt "com.evernote:android-state-processor:$stateSaverVersion"
|
||||||
|
|
||||||
// HTML parser
|
// HTML parser
|
||||||
implementation "org.jsoup:jsoup:1.17.2"
|
implementation "org.jsoup:jsoup:1.17.2"
|
||||||
@@ -282,7 +293,7 @@ dependencies {
|
|||||||
implementation "com.jakewharton.rxbinding4:rxbinding:4.0.0"
|
implementation "com.jakewharton.rxbinding4:rxbinding:4.0.0"
|
||||||
|
|
||||||
// Date and time formatting
|
// Date and time formatting
|
||||||
implementation "org.ocpsoft.prettytime:prettytime:5.0.7.Final"
|
implementation "org.ocpsoft.prettytime:prettytime:5.0.8.Final"
|
||||||
|
|
||||||
/** Debugging **/
|
/** Debugging **/
|
||||||
// Memory leak detection
|
// Memory leak detection
|
||||||
|
10
app/proguard-rules.pro
vendored
10
app/proguard-rules.pro
vendored
@@ -7,20 +7,12 @@
|
|||||||
-keep class org.schabi.newpipe.extractor.timeago.patterns.** { *; }
|
-keep class org.schabi.newpipe.extractor.timeago.patterns.** { *; }
|
||||||
-keep class org.mozilla.javascript.** { *; }
|
-keep class org.mozilla.javascript.** { *; }
|
||||||
-keep class org.mozilla.classfile.ClassFileWriter
|
-keep class org.mozilla.classfile.ClassFileWriter
|
||||||
|
-dontwarn org.mozilla.javascript.JavaToJSONConverters
|
||||||
-dontwarn org.mozilla.javascript.tools.**
|
-dontwarn org.mozilla.javascript.tools.**
|
||||||
|
|
||||||
## Rules for ExoPlayer
|
## Rules for ExoPlayer
|
||||||
-keep class com.google.android.exoplayer2.** { *; }
|
-keep class com.google.android.exoplayer2.** { *; }
|
||||||
|
|
||||||
## Rules for Icepick. Copy pasted from https://github.com/frankiesardo/icepick
|
|
||||||
-dontwarn icepick.**
|
|
||||||
-keep class icepick.** { *; }
|
|
||||||
-keep class **$$Icepick { *; }
|
|
||||||
-keepclasseswithmembernames class * {
|
|
||||||
@icepick.* <fields>;
|
|
||||||
}
|
|
||||||
-keepnames class * { @icepick.State *;}
|
|
||||||
|
|
||||||
## Rules for OkHttp. Copy pasted from https://github.com/square/okhttp
|
## Rules for OkHttp. Copy pasted from https://github.com/square/okhttp
|
||||||
-dontwarn okhttp3.**
|
-dontwarn okhttp3.**
|
||||||
-dontwarn okio.**
|
-dontwarn okio.**
|
||||||
|
@@ -367,6 +367,7 @@
|
|||||||
<data android:host="tilvids.com" />
|
<data android:host="tilvids.com" />
|
||||||
<data android:host="video.lqdn.fr" />
|
<data android:host="video.lqdn.fr" />
|
||||||
<data android:host="video.ploud.fr" />
|
<data android:host="video.ploud.fr" />
|
||||||
|
<data android:host="subscribeto.me" />
|
||||||
|
|
||||||
<data android:pathPrefix="/videos/" /> <!-- it contains playlists -->
|
<data android:pathPrefix="/videos/" /> <!-- it contains playlists -->
|
||||||
<data android:pathPrefix="/w/" /> <!-- short video URLs -->
|
<data android:pathPrefix="/w/" /> <!-- short video URLs -->
|
||||||
|
@@ -19,11 +19,12 @@ import org.schabi.newpipe.extractor.NewPipe;
|
|||||||
import org.schabi.newpipe.extractor.downloader.Downloader;
|
import org.schabi.newpipe.extractor.downloader.Downloader;
|
||||||
import org.schabi.newpipe.ktx.ExceptionUtils;
|
import org.schabi.newpipe.ktx.ExceptionUtils;
|
||||||
import org.schabi.newpipe.settings.NewPipeSettings;
|
import org.schabi.newpipe.settings.NewPipeSettings;
|
||||||
|
import org.schabi.newpipe.util.BridgeStateSaverInitializer;
|
||||||
import org.schabi.newpipe.util.Localization;
|
import org.schabi.newpipe.util.Localization;
|
||||||
import org.schabi.newpipe.util.image.ImageStrategy;
|
|
||||||
import org.schabi.newpipe.util.image.PicassoHelper;
|
|
||||||
import org.schabi.newpipe.util.ServiceHelper;
|
import org.schabi.newpipe.util.ServiceHelper;
|
||||||
import org.schabi.newpipe.util.StateSaver;
|
import org.schabi.newpipe.util.StateSaver;
|
||||||
|
import org.schabi.newpipe.util.image.ImageStrategy;
|
||||||
|
import org.schabi.newpipe.util.image.PicassoHelper;
|
||||||
import org.schabi.newpipe.util.image.PreferredImageQuality;
|
import org.schabi.newpipe.util.image.PreferredImageQuality;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -101,6 +102,7 @@ public class App extends Application {
|
|||||||
Localization.getPreferredContentCountry(this));
|
Localization.getPreferredContentCountry(this));
|
||||||
Localization.initPrettyTime(Localization.resolvePrettyTime(getApplicationContext()));
|
Localization.initPrettyTime(Localization.resolvePrettyTime(getApplicationContext()));
|
||||||
|
|
||||||
|
BridgeStateSaverInitializer.init(this);
|
||||||
StateSaver.init(this);
|
StateSaver.init(this);
|
||||||
initNotificationChannels();
|
initNotificationChannels();
|
||||||
|
|
||||||
|
@@ -10,8 +10,9 @@ import androidx.appcompat.app.AppCompatActivity;
|
|||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
import icepick.Icepick;
|
import com.evernote.android.state.State;
|
||||||
import icepick.State;
|
import com.livefront.bridge.Bridge;
|
||||||
|
|
||||||
|
|
||||||
public abstract class BaseFragment extends Fragment {
|
public abstract class BaseFragment extends Fragment {
|
||||||
protected final String TAG = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode());
|
protected final String TAG = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode());
|
||||||
@@ -48,7 +49,7 @@ public abstract class BaseFragment extends Fragment {
|
|||||||
+ "savedInstanceState = [" + savedInstanceState + "]");
|
+ "savedInstanceState = [" + savedInstanceState + "]");
|
||||||
}
|
}
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
Icepick.restoreInstanceState(this, savedInstanceState);
|
Bridge.restoreInstanceState(this, savedInstanceState);
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
onRestoreInstanceState(savedInstanceState);
|
onRestoreInstanceState(savedInstanceState);
|
||||||
}
|
}
|
||||||
@@ -70,7 +71,7 @@ public abstract class BaseFragment extends Fragment {
|
|||||||
@Override
|
@Override
|
||||||
public void onSaveInstanceState(@NonNull final Bundle outState) {
|
public void onSaveInstanceState(@NonNull final Bundle outState) {
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
Icepick.saveInstanceState(this, outState);
|
Bridge.saveInstanceState(this, outState);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onRestoreInstanceState(@NonNull final Bundle savedInstanceState) {
|
protected void onRestoreInstanceState(@NonNull final Bundle savedInstanceState) {
|
||||||
|
@@ -29,7 +29,7 @@ import okhttp3.ResponseBody;
|
|||||||
|
|
||||||
public final class DownloaderImpl extends Downloader {
|
public final class DownloaderImpl extends Downloader {
|
||||||
public static final String USER_AGENT =
|
public static final String USER_AGENT =
|
||||||
"Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0";
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0";
|
||||||
public static final String YOUTUBE_RESTRICTED_MODE_COOKIE_KEY =
|
public static final String YOUTUBE_RESTRICTED_MODE_COOKIE_KEY =
|
||||||
"youtube_restricted_mode_key";
|
"youtube_restricted_mode_key";
|
||||||
public static final String YOUTUBE_RESTRICTED_MODE_COOKIE = "PREF=f2=8000000";
|
public static final String YOUTUBE_RESTRICTED_MODE_COOKIE = "PREF=f2=8000000";
|
||||||
|
@@ -41,6 +41,9 @@ import androidx.lifecycle.Lifecycle;
|
|||||||
import androidx.lifecycle.LifecycleOwner;
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import com.evernote.android.state.State;
|
||||||
|
import com.livefront.bridge.Bridge;
|
||||||
|
|
||||||
import org.schabi.newpipe.database.stream.model.StreamEntity;
|
import org.schabi.newpipe.database.stream.model.StreamEntity;
|
||||||
import org.schabi.newpipe.databinding.ListRadioIconItemBinding;
|
import org.schabi.newpipe.databinding.ListRadioIconItemBinding;
|
||||||
import org.schabi.newpipe.databinding.SingleChoiceDialogViewBinding;
|
import org.schabi.newpipe.databinding.SingleChoiceDialogViewBinding;
|
||||||
@@ -98,8 +101,6 @@ import java.util.List;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import icepick.Icepick;
|
|
||||||
import icepick.State;
|
|
||||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.rxjava3.core.Observable;
|
import io.reactivex.rxjava3.core.Observable;
|
||||||
import io.reactivex.rxjava3.core.Single;
|
import io.reactivex.rxjava3.core.Single;
|
||||||
@@ -152,7 +153,7 @@ public class RouterActivity extends AppCompatActivity {
|
|||||||
getWindow().setAttributes(params);
|
getWindow().setAttributes(params);
|
||||||
|
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
Icepick.restoreInstanceState(this, savedInstanceState);
|
Bridge.restoreInstanceState(this, savedInstanceState);
|
||||||
|
|
||||||
// FragmentManager will take care to recreate (Playlist|Download)Dialog when screen rotates
|
// FragmentManager will take care to recreate (Playlist|Download)Dialog when screen rotates
|
||||||
// We used to .setOnDismissListener(dialog -> finish()); when creating these DialogFragments
|
// We used to .setOnDismissListener(dialog -> finish()); when creating these DialogFragments
|
||||||
@@ -197,7 +198,7 @@ public class RouterActivity extends AppCompatActivity {
|
|||||||
@Override
|
@Override
|
||||||
protected void onSaveInstanceState(@NonNull final Bundle outState) {
|
protected void onSaveInstanceState(@NonNull final Bundle outState) {
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
Icepick.saveInstanceState(this, outState);
|
Bridge.saveInstanceState(this, outState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -138,8 +138,12 @@ class AboutActivity : AppCompatActivity() {
|
|||||||
"https://github.com/lisawray/groupie", StandardLicenses.MIT
|
"https://github.com/lisawray/groupie", StandardLicenses.MIT
|
||||||
),
|
),
|
||||||
SoftwareComponent(
|
SoftwareComponent(
|
||||||
"Icepick", "2015", "Frankie Sardo",
|
"Android-State", "2018", "Evernote",
|
||||||
"https://github.com/frankiesardo/icepick", StandardLicenses.EPL1
|
"https://github.com/Evernote/android-state", StandardLicenses.EPL1
|
||||||
|
),
|
||||||
|
SoftwareComponent(
|
||||||
|
"Bridge", "2021", "Livefront",
|
||||||
|
"https://github.com/livefront/bridge", StandardLicenses.APACHE2
|
||||||
),
|
),
|
||||||
SoftwareComponent(
|
SoftwareComponent(
|
||||||
"Jsoup", "2009 - 2020", "Jonathan Hedley",
|
"Jsoup", "2009 - 2020", "Jonathan Hedley",
|
||||||
|
@@ -154,6 +154,6 @@ public interface PlaylistStreamDAO extends BasicDAO<PlaylistStreamEntity> {
|
|||||||
+ " AND :streamUrl = :streamUrl"
|
+ " AND :streamUrl = :streamUrl"
|
||||||
|
|
||||||
+ " GROUP BY " + JOIN_PLAYLIST_ID
|
+ " GROUP BY " + JOIN_PLAYLIST_ID
|
||||||
+ " ORDER BY " + PLAYLIST_DISPLAY_INDEX)
|
+ " ORDER BY " + PLAYLIST_DISPLAY_INDEX + ", " + PLAYLIST_NAME)
|
||||||
Flowable<List<PlaylistDuplicatesEntry>> getPlaylistDuplicatesMetadata(String streamUrl);
|
Flowable<List<PlaylistDuplicatesEntry>> getPlaylistDuplicatesMetadata(String streamUrl);
|
||||||
}
|
}
|
||||||
|
@@ -39,6 +39,8 @@ import androidx.documentfile.provider.DocumentFile;
|
|||||||
import androidx.fragment.app.DialogFragment;
|
import androidx.fragment.app.DialogFragment;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import com.evernote.android.state.State;
|
||||||
|
import com.livefront.bridge.Bridge;
|
||||||
import com.nononsenseapps.filepicker.Utils;
|
import com.nononsenseapps.filepicker.Utils;
|
||||||
|
|
||||||
import org.schabi.newpipe.MainActivity;
|
import org.schabi.newpipe.MainActivity;
|
||||||
@@ -59,6 +61,8 @@ import org.schabi.newpipe.settings.NewPipeSettings;
|
|||||||
import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard;
|
import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard;
|
||||||
import org.schabi.newpipe.streams.io.StoredDirectoryHelper;
|
import org.schabi.newpipe.streams.io.StoredDirectoryHelper;
|
||||||
import org.schabi.newpipe.streams.io.StoredFileHelper;
|
import org.schabi.newpipe.streams.io.StoredFileHelper;
|
||||||
|
import org.schabi.newpipe.util.AudioTrackAdapter;
|
||||||
|
import org.schabi.newpipe.util.AudioTrackAdapter.AudioTracksWrapper;
|
||||||
import org.schabi.newpipe.util.FilePickerActivityHelper;
|
import org.schabi.newpipe.util.FilePickerActivityHelper;
|
||||||
import org.schabi.newpipe.util.FilenameUtils;
|
import org.schabi.newpipe.util.FilenameUtils;
|
||||||
import org.schabi.newpipe.util.ListHelper;
|
import org.schabi.newpipe.util.ListHelper;
|
||||||
@@ -67,8 +71,6 @@ import org.schabi.newpipe.util.SecondaryStreamHelper;
|
|||||||
import org.schabi.newpipe.util.SimpleOnSeekBarChangeListener;
|
import org.schabi.newpipe.util.SimpleOnSeekBarChangeListener;
|
||||||
import org.schabi.newpipe.util.StreamItemAdapter;
|
import org.schabi.newpipe.util.StreamItemAdapter;
|
||||||
import org.schabi.newpipe.util.StreamItemAdapter.StreamInfoWrapper;
|
import org.schabi.newpipe.util.StreamItemAdapter.StreamInfoWrapper;
|
||||||
import org.schabi.newpipe.util.AudioTrackAdapter;
|
|
||||||
import org.schabi.newpipe.util.AudioTrackAdapter.AudioTracksWrapper;
|
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -79,8 +81,6 @@ import java.util.Locale;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import icepick.Icepick;
|
|
||||||
import icepick.State;
|
|
||||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||||
import us.shandian.giga.get.MissionRecoveryInfo;
|
import us.shandian.giga.get.MissionRecoveryInfo;
|
||||||
import us.shandian.giga.postprocessing.Postprocessing;
|
import us.shandian.giga.postprocessing.Postprocessing;
|
||||||
@@ -214,7 +214,7 @@ public class DownloadDialog extends DialogFragment
|
|||||||
context = getContext();
|
context = getContext();
|
||||||
|
|
||||||
setStyle(STYLE_NO_TITLE, ThemeHelper.getDialogTheme(context));
|
setStyle(STYLE_NO_TITLE, ThemeHelper.getDialogTheme(context));
|
||||||
Icepick.restoreInstanceState(this, savedInstanceState);
|
Bridge.restoreInstanceState(this, savedInstanceState);
|
||||||
|
|
||||||
this.audioTrackAdapter = new AudioTrackAdapter(wrappedAudioTracks);
|
this.audioTrackAdapter = new AudioTrackAdapter(wrappedAudioTracks);
|
||||||
this.subtitleStreamsAdapter = new StreamItemAdapter<>(wrappedSubtitleStreams);
|
this.subtitleStreamsAdapter = new StreamItemAdapter<>(wrappedSubtitleStreams);
|
||||||
@@ -372,7 +372,7 @@ public class DownloadDialog extends DialogFragment
|
|||||||
@Override
|
@Override
|
||||||
public void onSaveInstanceState(@NonNull final Bundle outState) {
|
public void onSaveInstanceState(@NonNull final Bundle outState) {
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
Icepick.saveInstanceState(this, outState);
|
Bridge.saveInstanceState(this, outState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -2,7 +2,6 @@ package org.schabi.newpipe.error;
|
|||||||
|
|
||||||
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
|
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
@@ -13,7 +12,6 @@ import android.view.Menu;
|
|||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
@@ -22,7 +20,6 @@ import androidx.core.content.IntentCompat;
|
|||||||
import com.grack.nanojson.JsonWriter;
|
import com.grack.nanojson.JsonWriter;
|
||||||
|
|
||||||
import org.schabi.newpipe.BuildConfig;
|
import org.schabi.newpipe.BuildConfig;
|
||||||
import org.schabi.newpipe.MainActivity;
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.databinding.ActivityErrorBinding;
|
import org.schabi.newpipe.databinding.ActivityErrorBinding;
|
||||||
import org.schabi.newpipe.util.Localization;
|
import org.schabi.newpipe.util.Localization;
|
||||||
@@ -187,25 +184,6 @@ public class ErrorActivity extends AppCompatActivity {
|
|||||||
.collect(Collectors.joining(separator + "\n", separator + "\n", separator));
|
.collect(Collectors.joining(separator + "\n", separator + "\n", separator));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the checked activity.
|
|
||||||
*
|
|
||||||
* @param returnActivity the activity to return to
|
|
||||||
* @return the casted return activity or null
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
static Class<? extends Activity> getReturnActivity(final Class<?> returnActivity) {
|
|
||||||
Class<? extends Activity> checkedReturnActivity = null;
|
|
||||||
if (returnActivity != null) {
|
|
||||||
if (Activity.class.isAssignableFrom(returnActivity)) {
|
|
||||||
checkedReturnActivity = returnActivity.asSubclass(Activity.class);
|
|
||||||
} else {
|
|
||||||
checkedReturnActivity = MainActivity.class;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return checkedReturnActivity;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void buildInfo(final ErrorInfo info) {
|
private void buildInfo(final ErrorInfo info) {
|
||||||
String text = "";
|
String text = "";
|
||||||
|
|
||||||
|
@@ -54,7 +54,7 @@ class ErrorUtil {
|
|||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun showSnackbar(context: Context, errorInfo: ErrorInfo) {
|
fun showSnackbar(context: Context, errorInfo: ErrorInfo) {
|
||||||
val rootView = if (context is Activity) context.findViewById<View>(R.id.content) else null
|
val rootView = (context as? Activity)?.findViewById<View>(android.R.id.content)
|
||||||
showSnackbar(context, rootView, errorInfo)
|
showSnackbar(context, rootView, errorInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ class ErrorUtil {
|
|||||||
fun showSnackbar(fragment: Fragment, errorInfo: ErrorInfo) {
|
fun showSnackbar(fragment: Fragment, errorInfo: ErrorInfo) {
|
||||||
var rootView = fragment.view
|
var rootView = fragment.view
|
||||||
if (rootView == null && fragment.activity != null) {
|
if (rootView == null && fragment.activity != null) {
|
||||||
rootView = fragment.requireActivity().findViewById(R.id.content)
|
rootView = fragment.requireActivity().findViewById(android.R.id.content)
|
||||||
}
|
}
|
||||||
showSnackbar(fragment.requireContext(), rootView, errorInfo)
|
showSnackbar(fragment.requireContext(), rootView, errorInfo)
|
||||||
}
|
}
|
||||||
|
@@ -27,8 +27,6 @@ import org.schabi.newpipe.databinding.ActivityRecaptchaBinding;
|
|||||||
import org.schabi.newpipe.extractor.utils.Utils;
|
import org.schabi.newpipe.extractor.utils.Utils;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Created by beneth <bmauduit@beneth.fr> on 06.12.16.
|
* Created by beneth <bmauduit@beneth.fr> on 06.12.16.
|
||||||
*
|
*
|
||||||
@@ -187,14 +185,11 @@ public class ReCaptchaActivity extends AppCompatActivity {
|
|||||||
final int abuseEnd = url.indexOf("+path");
|
final int abuseEnd = url.indexOf("+path");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String abuseCookie = url.substring(abuseStart + 13, abuseEnd);
|
handleCookies(Utils.decodeUrlUtf8(url.substring(abuseStart + 13, abuseEnd)));
|
||||||
abuseCookie = Utils.decodeUrlUtf8(abuseCookie);
|
} catch (final StringIndexOutOfBoundsException e) {
|
||||||
handleCookies(abuseCookie);
|
|
||||||
} catch (UnsupportedEncodingException | StringIndexOutOfBoundsException e) {
|
|
||||||
if (MainActivity.DEBUG) {
|
if (MainActivity.DEBUG) {
|
||||||
e.printStackTrace();
|
Log.e(TAG, "handleCookiesFromUrl: invalid google abuse starting at "
|
||||||
Log.d(TAG, "handleCookiesFromUrl: invalid google abuse starting at "
|
+ abuseStart + " and ending at " + abuseEnd + " for url " + url, e);
|
||||||
+ abuseStart + " and ending at " + abuseEnd + " for url " + url);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -13,6 +13,8 @@ import androidx.annotation.Nullable;
|
|||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import com.evernote.android.state.State;
|
||||||
|
|
||||||
import org.schabi.newpipe.BaseFragment;
|
import org.schabi.newpipe.BaseFragment;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.error.ErrorInfo;
|
import org.schabi.newpipe.error.ErrorInfo;
|
||||||
@@ -22,8 +24,6 @@ import org.schabi.newpipe.util.InfoCache;
|
|||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import icepick.State;
|
|
||||||
|
|
||||||
public abstract class BaseStateFragment<I> extends BaseFragment implements ViewContract<I> {
|
public abstract class BaseStateFragment<I> extends BaseFragment implements ViewContract<I> {
|
||||||
@State
|
@State
|
||||||
protected AtomicBoolean wasLoading = new AtomicBoolean();
|
protected AtomicBoolean wasLoading = new AtomicBoolean();
|
||||||
@@ -134,6 +134,7 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
|
|||||||
hideErrorPanel();
|
hideErrorPanel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void showEmptyState() {
|
public void showEmptyState() {
|
||||||
isLoading.set(false);
|
isLoading.set(false);
|
||||||
if (emptyStateView != null) {
|
if (emptyStateView != null) {
|
||||||
|
@@ -245,10 +245,10 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
|||||||
// change the background and icon color of the tab layout:
|
// change the background and icon color of the tab layout:
|
||||||
// service-colored at the top, app-background-colored at the bottom
|
// service-colored at the top, app-background-colored at the bottom
|
||||||
tabLayout.setBackgroundColor(ThemeHelper.resolveColorFromAttr(requireContext(),
|
tabLayout.setBackgroundColor(ThemeHelper.resolveColorFromAttr(requireContext(),
|
||||||
bottom ? R.attr.colorSecondary : R.attr.colorPrimary));
|
bottom ? android.R.attr.windowBackground : R.attr.colorPrimary));
|
||||||
|
|
||||||
@ColorInt final int iconColor = bottom
|
@ColorInt final int iconColor = bottom
|
||||||
? ThemeHelper.resolveColorFromAttr(requireContext(), R.attr.colorAccent)
|
? ThemeHelper.resolveColorFromAttr(requireContext(), android.R.attr.colorAccent)
|
||||||
: Color.WHITE;
|
: Color.WHITE;
|
||||||
tabLayout.setTabRippleColor(ColorStateList.valueOf(iconColor).withAlpha(32));
|
tabLayout.setTabRippleColor(ColorStateList.valueOf(iconColor).withAlpha(32));
|
||||||
tabLayout.setTabIconTint(ColorStateList.valueOf(iconColor));
|
tabLayout.setTabIconTint(ColorStateList.valueOf(iconColor));
|
||||||
|
@@ -11,6 +11,8 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
|
|
||||||
|
import com.evernote.android.state.State;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
import org.schabi.newpipe.extractor.stream.Description;
|
import org.schabi.newpipe.extractor.stream.Description;
|
||||||
@@ -19,8 +21,6 @@ import org.schabi.newpipe.util.Localization;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import icepick.State;
|
|
||||||
|
|
||||||
public class DescriptionFragment extends BaseDescriptionFragment {
|
public class DescriptionFragment extends BaseDescriptionFragment {
|
||||||
|
|
||||||
@State
|
@State
|
||||||
@@ -31,7 +31,7 @@ public class DescriptionFragment extends BaseDescriptionFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public DescriptionFragment() {
|
public DescriptionFragment() {
|
||||||
// keep empty constructor for IcePick when resuming fragment from memory
|
// keep empty constructor for State when resuming fragment from memory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -56,6 +56,7 @@ import androidx.core.content.ContextCompat;
|
|||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import com.evernote.android.state.State;
|
||||||
import com.google.android.exoplayer2.PlaybackException;
|
import com.google.android.exoplayer2.PlaybackException;
|
||||||
import com.google.android.exoplayer2.PlaybackParameters;
|
import com.google.android.exoplayer2.PlaybackParameters;
|
||||||
import com.google.android.material.appbar.AppBarLayout;
|
import com.google.android.material.appbar.AppBarLayout;
|
||||||
@@ -127,7 +128,6 @@ import java.util.Optional;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import icepick.State;
|
|
||||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||||
import io.reactivex.rxjava3.disposables.Disposable;
|
import io.reactivex.rxjava3.disposables.Disposable;
|
||||||
|
@@ -9,6 +9,8 @@ import android.view.View;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.evernote.android.state.State;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.error.ErrorInfo;
|
import org.schabi.newpipe.error.ErrorInfo;
|
||||||
import org.schabi.newpipe.error.UserAction;
|
import org.schabi.newpipe.error.UserAction;
|
||||||
@@ -24,7 +26,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
|
|
||||||
import icepick.State;
|
|
||||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.rxjava3.core.Single;
|
import io.reactivex.rxjava3.core.Single;
|
||||||
import io.reactivex.rxjava3.disposables.Disposable;
|
import io.reactivex.rxjava3.disposables.Disposable;
|
||||||
@@ -143,7 +144,7 @@ public abstract class BaseListInfoFragment<I extends InfoItem, L extends ListInf
|
|||||||
currentWorker = loadResult(forceLoad)
|
currentWorker = loadResult(forceLoad)
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe((@NonNull L result) -> {
|
.subscribe((@NonNull final L result) -> {
|
||||||
isLoading.set(false);
|
isLoading.set(false);
|
||||||
currentInfo = result;
|
currentInfo = result;
|
||||||
currentNextPage = result.getNextPage();
|
currentNextPage = result.getNextPage();
|
||||||
|
@@ -10,6 +10,8 @@ import android.widget.LinearLayout;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.evernote.android.state.State;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
||||||
@@ -20,8 +22,6 @@ import org.schabi.newpipe.util.Localization;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import icepick.State;
|
|
||||||
|
|
||||||
public class ChannelAboutFragment extends BaseDescriptionFragment {
|
public class ChannelAboutFragment extends BaseDescriptionFragment {
|
||||||
@State
|
@State
|
||||||
protected ChannelInfo channelInfo;
|
protected ChannelInfo channelInfo;
|
||||||
@@ -31,7 +31,7 @@ public class ChannelAboutFragment extends BaseDescriptionFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ChannelAboutFragment() {
|
public ChannelAboutFragment() {
|
||||||
// keep empty constructor for IcePick when resuming fragment from memory
|
// keep empty constructor for State when resuming fragment from memory
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -22,8 +22,10 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.core.graphics.ColorUtils;
|
import androidx.core.graphics.ColorUtils;
|
||||||
|
import androidx.core.view.MenuProvider;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import com.evernote.android.state.State;
|
||||||
import com.google.android.material.snackbar.Snackbar;
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
import com.google.android.material.tabs.TabLayout;
|
import com.google.android.material.tabs.TabLayout;
|
||||||
import com.jakewharton.rxbinding4.view.RxView;
|
import com.jakewharton.rxbinding4.view.RxView;
|
||||||
@@ -49,16 +51,15 @@ import org.schabi.newpipe.util.ExtractorHelper;
|
|||||||
import org.schabi.newpipe.util.Localization;
|
import org.schabi.newpipe.util.Localization;
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
import org.schabi.newpipe.util.StateSaver;
|
import org.schabi.newpipe.util.StateSaver;
|
||||||
import org.schabi.newpipe.util.image.ImageStrategy;
|
|
||||||
import org.schabi.newpipe.util.image.PicassoHelper;
|
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
import org.schabi.newpipe.util.external_communication.ShareUtils;
|
import org.schabi.newpipe.util.external_communication.ShareUtils;
|
||||||
|
import org.schabi.newpipe.util.image.ImageStrategy;
|
||||||
|
import org.schabi.newpipe.util.image.PicassoHelper;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import icepick.State;
|
|
||||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.rxjava3.core.Observable;
|
import io.reactivex.rxjava3.core.Observable;
|
||||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||||
@@ -99,6 +100,7 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo>
|
|||||||
private MenuItem menuRssButton;
|
private MenuItem menuRssButton;
|
||||||
private MenuItem menuNotifyButton;
|
private MenuItem menuNotifyButton;
|
||||||
private SubscriptionEntity channelSubscription;
|
private SubscriptionEntity channelSubscription;
|
||||||
|
private MenuProvider menuProvider;
|
||||||
|
|
||||||
public static ChannelFragment getInstance(final int serviceId, final String url,
|
public static ChannelFragment getInstance(final int serviceId, final String url,
|
||||||
final String name) {
|
final String name) {
|
||||||
@@ -121,7 +123,62 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo>
|
|||||||
@Override
|
@Override
|
||||||
public void onCreate(final Bundle savedInstanceState) {
|
public void onCreate(final Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setHasOptionsMenu(true);
|
menuProvider = new MenuProvider() {
|
||||||
|
@Override
|
||||||
|
public void onCreateMenu(@NonNull final Menu menu,
|
||||||
|
@NonNull final MenuInflater inflater) {
|
||||||
|
inflater.inflate(R.menu.menu_channel, menu);
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "onCreateOptionsMenu() called with: "
|
||||||
|
+ "menu = [" + menu + "], inflater = [" + inflater + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPrepareMenu(@NonNull final Menu menu) {
|
||||||
|
menuRssButton = menu.findItem(R.id.menu_item_rss);
|
||||||
|
menuNotifyButton = menu.findItem(R.id.menu_item_notify);
|
||||||
|
updateRssButton();
|
||||||
|
updateNotifyButton(channelSubscription);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMenuItemSelected(@NonNull final MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.menu_item_notify:
|
||||||
|
final boolean value = !item.isChecked();
|
||||||
|
item.setEnabled(false);
|
||||||
|
setNotify(value);
|
||||||
|
break;
|
||||||
|
case R.id.action_settings:
|
||||||
|
NavigationHelper.openSettings(requireContext());
|
||||||
|
break;
|
||||||
|
case R.id.menu_item_rss:
|
||||||
|
if (currentInfo != null) {
|
||||||
|
ShareUtils.openUrlInApp(requireContext(), currentInfo.getFeedUrl());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case R.id.menu_item_openInBrowser:
|
||||||
|
if (currentInfo != null) {
|
||||||
|
ShareUtils.openUrlInBrowser(requireContext(),
|
||||||
|
currentInfo.getOriginalUrl());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case R.id.menu_item_share:
|
||||||
|
if (currentInfo != null) {
|
||||||
|
ShareUtils.shareText(requireContext(), name,
|
||||||
|
currentInfo.getOriginalUrl(), currentInfo.getAvatars());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
activity.addMenuProvider(menuProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -183,73 +240,16 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo>
|
|||||||
}
|
}
|
||||||
disposables.clear();
|
disposables.clear();
|
||||||
binding = null;
|
binding = null;
|
||||||
|
activity.removeMenuProvider(menuProvider);
|
||||||
|
menuProvider = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
|
||||||
// Menu
|
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreateOptionsMenu(@NonNull final Menu menu,
|
|
||||||
@NonNull final MenuInflater inflater) {
|
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
|
||||||
inflater.inflate(R.menu.menu_channel, menu);
|
|
||||||
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.d(TAG, "onCreateOptionsMenu() called with: "
|
|
||||||
+ "menu = [" + menu + "], inflater = [" + inflater + "]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPrepareOptionsMenu(@NonNull final Menu menu) {
|
|
||||||
super.onPrepareOptionsMenu(menu);
|
|
||||||
menuRssButton = menu.findItem(R.id.menu_item_rss);
|
|
||||||
menuNotifyButton = menu.findItem(R.id.menu_item_notify);
|
|
||||||
updateNotifyButton(channelSubscription);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
|
|
||||||
switch (item.getItemId()) {
|
|
||||||
case R.id.menu_item_notify:
|
|
||||||
final boolean value = !item.isChecked();
|
|
||||||
item.setEnabled(false);
|
|
||||||
setNotify(value);
|
|
||||||
break;
|
|
||||||
case R.id.action_settings:
|
|
||||||
NavigationHelper.openSettings(requireContext());
|
|
||||||
break;
|
|
||||||
case R.id.menu_item_rss:
|
|
||||||
if (currentInfo != null) {
|
|
||||||
ShareUtils.openUrlInApp(requireContext(), currentInfo.getFeedUrl());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case R.id.menu_item_openInBrowser:
|
|
||||||
if (currentInfo != null) {
|
|
||||||
ShareUtils.openUrlInBrowser(requireContext(), currentInfo.getOriginalUrl());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case R.id.menu_item_share:
|
|
||||||
if (currentInfo != null) {
|
|
||||||
ShareUtils.shareText(requireContext(), name, currentInfo.getOriginalUrl(),
|
|
||||||
currentInfo.getAvatars());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Channel Subscription
|
// Channel Subscription
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
private void monitorSubscription(final ChannelInfo info) {
|
private void monitorSubscription(final ChannelInfo info) {
|
||||||
final Consumer<Throwable> onError = (Throwable throwable) -> {
|
final Consumer<Throwable> onError = (final Throwable throwable) -> {
|
||||||
animate(binding.channelSubscribeButton, false, 100);
|
animate(binding.channelSubscribeButton, false, 100);
|
||||||
showSnackBarError(new ErrorInfo(throwable, UserAction.SUBSCRIPTION_GET,
|
showSnackBarError(new ErrorInfo(throwable, UserAction.SUBSCRIPTION_GET,
|
||||||
"Get subscription status", currentInfo));
|
"Get subscription status", currentInfo));
|
||||||
@@ -284,14 +284,14 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo>
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Function<Object, Object> mapOnSubscribe(final SubscriptionEntity subscription) {
|
private Function<Object, Object> mapOnSubscribe(final SubscriptionEntity subscription) {
|
||||||
return (@NonNull Object o) -> {
|
return (@NonNull final Object o) -> {
|
||||||
subscriptionManager.insertSubscription(subscription);
|
subscriptionManager.insertSubscription(subscription);
|
||||||
return o;
|
return o;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private Function<Object, Object> mapOnUnsubscribe(final SubscriptionEntity subscription) {
|
private Function<Object, Object> mapOnUnsubscribe(final SubscriptionEntity subscription) {
|
||||||
return (@NonNull Object o) -> {
|
return (@NonNull final Object o) -> {
|
||||||
subscriptionManager.deleteSubscription(subscription);
|
subscriptionManager.deleteSubscription(subscription);
|
||||||
return o;
|
return o;
|
||||||
};
|
};
|
||||||
@@ -318,7 +318,7 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo>
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Disposable monitorSubscribeButton(final Function<Object, Object> action) {
|
private Disposable monitorSubscribeButton(final Function<Object, Object> action) {
|
||||||
final Consumer<Object> onNext = (@NonNull Object o) -> {
|
final Consumer<Object> onNext = (@NonNull final Object o) -> {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "Changed subscription status to this channel!");
|
Log.d(TAG, "Changed subscription status to this channel!");
|
||||||
}
|
}
|
||||||
@@ -338,7 +338,7 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo>
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Consumer<List<SubscriptionEntity>> getSubscribeUpdateMonitor(final ChannelInfo info) {
|
private Consumer<List<SubscriptionEntity>> getSubscribeUpdateMonitor(final ChannelInfo info) {
|
||||||
return (List<SubscriptionEntity> subscriptionEntities) -> {
|
return (final List<SubscriptionEntity> subscriptionEntities) -> {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "subscriptionManager.subscriptionTable.doOnNext() called with: "
|
Log.d(TAG, "subscriptionManager.subscriptionTable.doOnNext() called with: "
|
||||||
+ "subscriptionEntities = [" + subscriptionEntities + "]");
|
+ "subscriptionEntities = [" + subscriptionEntities + "]");
|
||||||
@@ -408,6 +408,13 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo>
|
|||||||
animate(binding.channelSubscribeButton, true, 100, AnimationType.LIGHT_SCALE_AND_ALPHA);
|
animate(binding.channelSubscribeButton, true, 100, AnimationType.LIGHT_SCALE_AND_ALPHA);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateRssButton() {
|
||||||
|
if (menuRssButton == null || currentInfo == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
menuRssButton.setVisible(!TextUtils.isEmpty(currentInfo.getFeedUrl()));
|
||||||
|
}
|
||||||
|
|
||||||
private void updateNotifyButton(@Nullable final SubscriptionEntity subscription) {
|
private void updateNotifyButton(@Nullable final SubscriptionEntity subscription) {
|
||||||
if (menuNotifyButton == null) {
|
if (menuNotifyButton == null) {
|
||||||
return;
|
return;
|
||||||
@@ -610,9 +617,7 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo>
|
|||||||
binding.subChannelAvatarView.setVisibility(View.VISIBLE);
|
binding.subChannelAvatarView.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (menuRssButton != null) {
|
updateRssButton();
|
||||||
menuRssButton.setVisible(!TextUtils.isEmpty(result.getFeedUrl()));
|
|
||||||
}
|
|
||||||
|
|
||||||
channelContentNotSupported = false;
|
channelContentNotSupported = false;
|
||||||
for (final Throwable throwable : result.getErrors()) {
|
for (final Throwable throwable : result.getErrors()) {
|
||||||
|
@@ -9,6 +9,8 @@ import android.view.ViewGroup;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.evernote.android.state.State;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.databinding.PlaylistControlBinding;
|
import org.schabi.newpipe.databinding.PlaylistControlBinding;
|
||||||
import org.schabi.newpipe.error.UserAction;
|
import org.schabi.newpipe.error.UserAction;
|
||||||
@@ -32,13 +34,12 @@ import java.util.List;
|
|||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import icepick.State;
|
|
||||||
import io.reactivex.rxjava3.core.Single;
|
import io.reactivex.rxjava3.core.Single;
|
||||||
|
|
||||||
public class ChannelTabFragment extends BaseListInfoFragment<InfoItem, ChannelTabInfo>
|
public class ChannelTabFragment extends BaseListInfoFragment<InfoItem, ChannelTabInfo>
|
||||||
implements PlaylistControlViewHolder {
|
implements PlaylistControlViewHolder {
|
||||||
|
|
||||||
// states must be protected and not private for IcePick being able to access them
|
// states must be protected and not private for State being able to access them
|
||||||
@State
|
@State
|
||||||
protected ListLinkHandler tabHandler;
|
protected ListLinkHandler tabHandler;
|
||||||
@State
|
@State
|
||||||
@@ -156,6 +157,7 @@ public class ChannelTabFragment extends BaseListInfoFragment<InfoItem, ChannelTa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public PlayQueue getPlayQueue() {
|
public PlayQueue getPlayQueue() {
|
||||||
final List<StreamInfoItem> streamItems = infoListAdapter.getItemsList().stream()
|
final List<StreamInfoItem> streamItems = infoListAdapter.getItemsList().stream()
|
||||||
.filter(StreamInfoItem.class::isInstance)
|
.filter(StreamInfoItem.class::isInstance)
|
||||||
|
@@ -12,6 +12,8 @@ import androidx.annotation.Nullable;
|
|||||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||||
import androidx.core.text.HtmlCompat;
|
import androidx.core.text.HtmlCompat;
|
||||||
|
|
||||||
|
import com.evernote.android.state.State;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.databinding.CommentRepliesHeaderBinding;
|
import org.schabi.newpipe.databinding.CommentRepliesHeaderBinding;
|
||||||
import org.schabi.newpipe.error.UserAction;
|
import org.schabi.newpipe.error.UserAction;
|
||||||
@@ -30,7 +32,6 @@ import org.schabi.newpipe.util.text.TextLinkifier;
|
|||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import icepick.State;
|
|
||||||
import io.reactivex.rxjava3.core.Single;
|
import io.reactivex.rxjava3.core.Single;
|
||||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||||
|
|
||||||
|
@@ -11,6 +11,8 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
|
|
||||||
|
import com.evernote.android.state.State;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.error.ErrorInfo;
|
import org.schabi.newpipe.error.ErrorInfo;
|
||||||
import org.schabi.newpipe.error.UserAction;
|
import org.schabi.newpipe.error.UserAction;
|
||||||
@@ -29,7 +31,6 @@ import org.schabi.newpipe.util.ExtractorHelper;
|
|||||||
import org.schabi.newpipe.util.KioskTranslator;
|
import org.schabi.newpipe.util.KioskTranslator;
|
||||||
import org.schabi.newpipe.util.Localization;
|
import org.schabi.newpipe.util.Localization;
|
||||||
|
|
||||||
import icepick.State;
|
|
||||||
import io.reactivex.rxjava3.core.Single;
|
import io.reactivex.rxjava3.core.Single;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -352,6 +352,7 @@ public class PlaylistFragment extends BaseListInfoFragment<StreamInfoItem, Playl
|
|||||||
});
|
});
|
||||||
ellipsizer.setContent(description);
|
ellipsizer.setContent(description);
|
||||||
headerBinding.playlistDescriptionReadMore.setOnClickListener(v -> ellipsizer.toggle());
|
headerBinding.playlistDescriptionReadMore.setOnClickListener(v -> ellipsizer.toggle());
|
||||||
|
headerBinding.playlistDescription.setOnClickListener(v -> ellipsizer.toggle());
|
||||||
} else {
|
} else {
|
||||||
headerBinding.playlistDescription.setVisibility(View.GONE);
|
headerBinding.playlistDescription.setVisibility(View.GONE);
|
||||||
headerBinding.playlistDescriptionReadMore.setVisibility(View.GONE);
|
headerBinding.playlistDescriptionReadMore.setVisibility(View.GONE);
|
||||||
|
@@ -40,6 +40,8 @@ import androidx.preference.PreferenceManager;
|
|||||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.evernote.android.state.State;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.databinding.FragmentSearchBinding;
|
import org.schabi.newpipe.databinding.FragmentSearchBinding;
|
||||||
import org.schabi.newpipe.error.ErrorInfo;
|
import org.schabi.newpipe.error.ErrorInfo;
|
||||||
@@ -77,7 +79,6 @@ import java.util.Queue;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import icepick.State;
|
|
||||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.rxjava3.core.Observable;
|
import io.reactivex.rxjava3.core.Observable;
|
||||||
import io.reactivex.rxjava3.core.Single;
|
import io.reactivex.rxjava3.core.Single;
|
||||||
@@ -550,7 +551,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
searchEditText.setOnFocusChangeListener((View v, boolean hasFocus) -> {
|
searchEditText.setOnFocusChangeListener((final View v, final boolean hasFocus) -> {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "onFocusChange() called with: "
|
Log.d(TAG, "onFocusChange() called with: "
|
||||||
+ "v = [" + v + "], hasFocus = [" + hasFocus + "]");
|
+ "v = [" + v + "], hasFocus = [" + hasFocus + "]");
|
||||||
@@ -611,7 +612,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||||||
};
|
};
|
||||||
searchEditText.addTextChangedListener(textWatcher);
|
searchEditText.addTextChangedListener(textWatcher);
|
||||||
searchEditText.setOnEditorActionListener(
|
searchEditText.setOnEditorActionListener(
|
||||||
(TextView v, int actionId, KeyEvent event) -> {
|
(final TextView v, final int actionId, final KeyEvent event) -> {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "onEditorAction() called with: v = [" + v + "], "
|
Log.d(TAG, "onEditorAction() called with: v = [" + v + "], "
|
||||||
+ "actionId = [" + actionId + "], event = [" + event + "]");
|
+ "actionId = [" + actionId + "], event = [" + event + "]");
|
||||||
|
@@ -10,6 +10,7 @@ import android.view.ViewGroup;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
@@ -18,8 +19,10 @@ import org.schabi.newpipe.error.UserAction;
|
|||||||
import org.schabi.newpipe.extractor.InfoItem;
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
import org.schabi.newpipe.extractor.ListExtractor;
|
import org.schabi.newpipe.extractor.ListExtractor;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
|
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||||
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
|
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
|
||||||
import org.schabi.newpipe.info_list.ItemViewMode;
|
import org.schabi.newpipe.info_list.ItemViewMode;
|
||||||
|
import org.schabi.newpipe.info_list.dialog.InfoItemDialog;
|
||||||
import org.schabi.newpipe.ktx.ViewUtils;
|
import org.schabi.newpipe.ktx.ViewUtils;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
@@ -173,4 +176,27 @@ public class RelatedItemsFragment extends BaseListInfoFragment<InfoItem, Related
|
|||||||
}
|
}
|
||||||
return mode;
|
return mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void showInfoItemDialog(final StreamInfoItem item) {
|
||||||
|
// Try and attach the InfoItemDialog to the parent fragment of the RelatedItemsFragment
|
||||||
|
// so that its context is not lost when the RelatedItemsFragment is reinitialized,
|
||||||
|
// e.g. when a new stream is loaded in a parent VideoDetailFragment.
|
||||||
|
final Fragment parentFragment = getParentFragment();
|
||||||
|
if (parentFragment != null) {
|
||||||
|
try {
|
||||||
|
new InfoItemDialog.Builder(
|
||||||
|
parentFragment.getActivity(),
|
||||||
|
parentFragment.getContext(),
|
||||||
|
parentFragment,
|
||||||
|
item
|
||||||
|
).create().show();
|
||||||
|
} catch (final IllegalArgumentException e) {
|
||||||
|
InfoItemDialog.Builder.reportErrorDuringInitialization(e, item);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
super.showInfoItemDialog(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user