SponsorBlock: Merge branch 'dev' into sponsorblock
# Conflicts: # app/src/main/res/layout-large-land/player.xml # app/src/main/res/layout/player.xml # app/src/main/res/values/attrs.xml
47
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -7,20 +7,20 @@ assignees: ''
|
||||||
---
|
---
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Oh no, a bug! It happens. Thanks for reporting an issue with NewPipe. If this is your first bug report, read the following information before proceeding:
|
Oh no, a bug! It happens. Thanks for reporting an issue with NewPipe. To make it easier for us to help you please enter detailed information in the template we have provided below. If a section isn't relevant, just delete it, though it would be helpful to still provide as much detail as possible.
|
||||||
|
|
||||||
Please note, we only support the latest version of NewPipe. In order to check your app version, open the left drawer and click on "About". If you don't have the latest version, upgrade to it and reproduce the problem before opening the issue. The release page (https://github.com/TeamNewPipe/NewPipe/releases/latest) is where you can get it.
|
|
||||||
|
|
||||||
P.S.: Our contribution guidelines might be a nice document to read before you fill out the report :) You can find it at https://github.com/TeamNewPipe/NewPipe/blob/HEAD/.github/CONTRIBUTING.md
|
|
||||||
|
|
||||||
To make it easier for us to help you please enter detailed information in the template we have provided below. If a section isn't relevant, just delete it, though it would be helpful to still provide as much detail as possible.
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!-- The comments between these brackets won't show up in the submitted issue (as you can see in the preview). -->
|
<!-- IF YOU DON'T FILL IN THE TEMPLATE PROPERLY, YOUR ISSUE IS LIABLE TO BE CLOSED. If you feel tired/lazy right now, open your issue some other time. We'll wait. -->
|
||||||
|
|
||||||
### Version
|
<!-- The comments between these brackets won't show up in the submitted issue (as you can see in the Preview). -->
|
||||||
<!-- Which version are you using? Hopefully the latest! We just told you that above! -->
|
|
||||||
-
|
### Checklist
|
||||||
|
<!-- The first box has been checked for you to show you how it is done. -->
|
||||||
|
|
||||||
|
- [x] I am using the latest version - x.xx.x <!-- Check https://github.com/TeamNewPipe/NewPipe/releases -->
|
||||||
|
- [ ] I checked, but didn't find any duplicates (open OR closed) of this issue in the repo. <!-- Seriously, check. O_O -->
|
||||||
|
- [ ] I have read the contribution guidelines given at https://github.com/TeamNewPipe/NewPipe/blob/HEAD/.github/CONTRIBUTING.md.
|
||||||
|
- [ ] This issue contains only one bug. I will open one issue for every bug report I want to file.
|
||||||
|
|
||||||
### Steps to reproduce the bug
|
### Steps to reproduce the bug
|
||||||
<!--
|
<!--
|
||||||
|
@ -31,16 +31,35 @@ To make it easier for us to help you please enter detailed information in the te
|
||||||
|
|
||||||
<!-- If you can't cause the bug to show up again reliably (and hence don't have a proper set of steps to give us), please still try to give as many details as possible on how you think you encountered the bug. -->
|
<!-- If you can't cause the bug to show up again reliably (and hence don't have a proper set of steps to give us), please still try to give as many details as possible on how you think you encountered the bug. -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Actual behaviour
|
||||||
|
<!-- Tell us what happens with the steps given above. -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Expected behavior
|
### Expected behavior
|
||||||
<!-- Tell us what you expect to happen. -->
|
<!-- Tell us what you expect to happen. -->
|
||||||
|
|
||||||
### Actual behaviour
|
|
||||||
<!-- Tell us what happens instead. -->
|
|
||||||
|
|
||||||
### Screenshots/Screen recordings
|
### Screenshots/Screen recordings
|
||||||
<!-- If applicable, add screenshots or a screen recording to help explain your problem. GitHub supports uploading them directly in the issue text box. If your file is too big for Github to accept, feel free to paste a link from an image/video hoster here instead. -->
|
<!-- If applicable, add screenshots or a screen recording to help explain your problem. GitHub supports uploading them directly in the issue text box. If your file is too big for Github to accept, feel free to paste a link from an image/video hoster here instead. -->
|
||||||
|
|
||||||
|
<!-- DON'T POST SCREENSHOTS OF THE ERROR PAGE. Use the buttons given on the error page to paste the error as text in the Logs section below. -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Logs
|
### Logs
|
||||||
<!-- If your bug includes a crash (where you're shown the Error Report page with a bunch of info), copy it to the clipboard (there is a share button for this), head over to our bug report to markdown converter at https://teamnewpipe.github.io/CrashReportToMarkdown/ and paste it. Copy the converted text (it is MUCH easier to read this way) from there and paste it here: -->
|
<!-- If your bug includes a crash (where you're shown the Error Report page with a bunch of info), tap on "Copy formatted report" at the bottom and paste it here: -->
|
||||||
|
|
||||||
<!-- That's right, here! -->
|
<!-- That's right, here! -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Please fill this out when you do not provide a log generate by NewPipe -->
|
||||||
|
|
||||||
|
### Device info
|
||||||
|
|
||||||
|
- Android version/Custom ROM version:
|
||||||
|
- Device model:
|
||||||
|
|
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
blank_issues_enabled: false
|
25
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
@ -5,35 +5,42 @@ labels: enhancement
|
||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
<!-- Hey. Our contribution guidelines (https://github.com/TeamNewPipe/NewPipe/blob/HEAD/.github/CONTRIBUTING.md) might be an appropriate
|
<!-- IF YOU DON'T FILL IN THE TEMPLATE PROPERLY, YOUR ISSUE IS LIABLE TO BE CLOSED. If you feel tired/lazy right now, open your issue some other time. We'll wait. -->
|
||||||
document to read before you fill out the request :) -->
|
|
||||||
|
|
||||||
|
<!-- The comments between these brackets won't show up in the submitted issue (as you can see in the Preview). -->
|
||||||
|
|
||||||
|
### Checklist
|
||||||
|
<!-- The first box has been checked for you to show you how it is done. -->
|
||||||
|
|
||||||
|
- [x] I checked, but didn't find any duplicates (open OR closed) of this issue in the repo. <!-- Seriously, check. O_O -->
|
||||||
|
- [ ] I have read the contribution guidelines given at https://github.com/TeamNewPipe/NewPipe/blob/HEAD/.github/CONTRIBUTING.md.
|
||||||
|
- [ ] This issue contains only one feature request. I will open one issue for every feature I want to request.
|
||||||
|
|
||||||
<!-- The comments between these brackets won't show up in the submitted issue (as you can see in the preview). -->
|
|
||||||
|
|
||||||
#### Describe the feature you want
|
#### Describe the feature you want
|
||||||
<!-- A clear and concise description of what you want to happen. PLEASE MAKE SURE it is one feature ONLY. You should open separate issues for separate feature requests, because those issues will be used to track their status.
|
<!-- A clear and concise description of what you wish should happen.
|
||||||
Example: *I think it would be nice if you add feature Y which makes X possible.*
|
Example: *I think it would be nice if you add feature Y which makes X possible.*
|
||||||
|
|
||||||
Optionally, also describe alternatives you've considered.
|
Optionally, also describe alternatives you've considered.
|
||||||
Example: *Z is also a good alternative. Not as good as Y, but at least...* or *I considered Z, but that didn't turn out to be a good idea because...* -->
|
Example: *Z is also a good alternative. Not as good as Y, but at least...* or *I considered Z, but that didn't turn out to be a good idea because...* -->
|
||||||
|
|
||||||
<!-- Write below this -->
|
|
||||||
|
|
||||||
#### Is your feature request related to a problem? Please describe it
|
#### Is your feature request related to a problem? Please describe it
|
||||||
<!-- A clear and concise description of what the problem is. Maybe the developers could brainstorm and come up with a better solution to your problem. If they exist, link to related Issues and/or PRs for developers to keep track easier.
|
<!-- A clear and concise description of what the problem is. Maybe the developers and the community could brainstorm and come up with a better solution to your problem. If they exist, link to related Issues and/or PRs for developers to keep track easier.
|
||||||
Example: *I want to do X, but there is no way to do it.* -->
|
Example: *I want to do X, but there is no way to do it.* -->
|
||||||
|
|
||||||
<!-- Write below this -->
|
|
||||||
|
|
||||||
#### Additional context
|
#### Additional context
|
||||||
<!-- Add any other context, like screenshots, about the feature request here.
|
<!-- Add any other context, like screenshots, about the feature request here.
|
||||||
Example: *Here's a photo of my cat!* -->
|
Example: *Here's a photo of my cat!* -->
|
||||||
|
|
||||||
<!-- Write below this -->
|
|
||||||
|
|
||||||
#### How will you/everyone benefit from this feature?
|
#### How will you/everyone benefit from this feature?
|
||||||
<!-- Convince us! How does it change your NewPipe experience and/or your life?
|
<!-- Convince us! How does it change your NewPipe experience and/or your life?
|
||||||
The better this paragraph is, the more likely a developer will think about working on it.
|
The better this paragraph is, the more likely a developer will think about working on it.
|
||||||
Example: *This feature will help us colonize the galaxy! -->
|
Example: *This feature will help us colonize the galaxy! -->
|
||||||
|
|
||||||
<!-- Write below this -->
|
|
||||||
|
|
18
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
@ -1,28 +1,28 @@
|
||||||
<!-- Hey there. Thank you so much for improving NewPipe. Please take a moment to fill out the following suggestion on how to structure this PR description. Having roughly the same layout helps everyone considerably :)-->
|
<!-- Hey there. Thank you so much for improving NewPipe, and filling out the details. Having roughly the same layout helps everyone considerably :)-->
|
||||||
|
|
||||||
#### What is it?
|
#### What is it?
|
||||||
- [ ] Bug fix (user facing)
|
- [ ] Bugfix (user facing)
|
||||||
- [ ] Feature (user facing)
|
- [ ] Feature (user facing)
|
||||||
- [ ] Code base improvement (dev facing)
|
- [ ] Codebase improvement (dev facing)
|
||||||
- [ ] Meta improvement to the project (dev facing)
|
- [ ] Meta improvement to the project (dev facing)
|
||||||
|
|
||||||
#### Description of the changes in your PR
|
#### Description of the changes in your PR
|
||||||
<!-- While bullet points are the norm in this section, feel free to write a text instead if you can't fit it in a list -->
|
<!-- While bullet points are the norm in this section, feel free to write free-form text instead of a list -->
|
||||||
- record videos
|
- record videos
|
||||||
- create clones
|
- create clones
|
||||||
- take over the world
|
- take over the world
|
||||||
|
|
||||||
#### Fixes the following issue(s)
|
#### Fixes the following issue(s)
|
||||||
<!-- Also add reddit or other links which are relevant to your change. -->
|
<!-- Also add any other links relevant to your change. -->
|
||||||
-
|
-
|
||||||
|
|
||||||
#### Relies on the following changes
|
#### Relies on the following changes
|
||||||
<!-- Delete this if it doesn't apply to you. -->
|
<!-- Delete this if it doesn't apply to you. -->
|
||||||
-
|
-
|
||||||
|
|
||||||
#### Testing apk
|
#### APK testing
|
||||||
<!-- Ensure that you have your changes on a new branch which has a meaningful name. This name will be used as a suffix for the app ID to allow installing and testing multiple versions of NewPipe. Do NOT name your branches like "patch-0" and "feature-1". For example, if your PR implements a bug fix for comments, an appropriate branch name would be "commentfix". -->
|
<!-- Use a new, meaningfully named branch. The name is used as a suffix for the app ID to allow installing and testing multiple versions of NewPipe, e.g. "commentfix", if your PR implements a bugfix for comments. (No names like "patch-0" and "feature-1".) -->
|
||||||
debug.zip
|
debug.zip
|
||||||
|
|
||||||
#### Agreement
|
#### Due diligence
|
||||||
- [ ] I carefully read the [contribution guidelines](https://github.com/TeamNewPipe/NewPipe/blob/HEAD/.github/CONTRIBUTING.md) and agree to them.
|
- [ ] I read the [contribution guidelines](https://github.com/TeamNewPipe/NewPipe/blob/HEAD/.github/CONTRIBUTING.md).
|
||||||
|
|
21
README.md
|
@ -22,16 +22,16 @@
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
|
|
||||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_01.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_01.png)
|
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_01.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_01.png)<!--
|
||||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_02.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_02.png)
|
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_02.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_02.png)
|
||||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_03.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_03.png)
|
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_03.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_03.png)-->
|
||||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_04.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_04.png)
|
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_04.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_04.png)<!--
|
||||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_05.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_05.png)
|
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_05.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_05.png)-->
|
||||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_06.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_06.png)
|
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_06.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_06.png)<!--
|
||||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_07.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_07.png)
|
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_07.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_07.png)
|
||||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_08.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_08.png)
|
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_08.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_08.png)
|
||||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_09.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_09.png)
|
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_09.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_09.png)
|
||||||
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_10.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_10.png)
|
[<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/shot_10.png" width=160>](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_10.png)-->
|
||||||
[<img src="fastlane/metadata/android/en-US/images/tenInchScreenshots/shot_11.png" width=405>](fastlane/metadata/android/en-US/images/tenInchScreenshots/shot_11.png)
|
[<img src="fastlane/metadata/android/en-US/images/tenInchScreenshots/shot_11.png" width=405>](fastlane/metadata/android/en-US/images/tenInchScreenshots/shot_11.png)
|
||||||
[<img src="fastlane/metadata/android/en-US/images/tenInchScreenshots/shot_12.png" width=405>](fastlane/metadata/android/en-US/images/tenInchScreenshots/shot_12.png)
|
[<img src="fastlane/metadata/android/en-US/images/tenInchScreenshots/shot_12.png" width=405>](fastlane/metadata/android/en-US/images/tenInchScreenshots/shot_12.png)
|
||||||
|
|
||||||
|
@ -69,11 +69,6 @@ NewPipe does not use any Google framework libraries, nor the YouTube API. Websit
|
||||||
* Livestream support
|
* Livestream support
|
||||||
* Show comments
|
* Show comments
|
||||||
|
|
||||||
### Coming Features
|
|
||||||
|
|
||||||
* Cast to UPnP and Cast
|
|
||||||
* … and many more
|
|
||||||
|
|
||||||
### Supported Services
|
### Supported Services
|
||||||
|
|
||||||
NewPipe supports multiple services. Our [docs](https://teamnewpipe.github.io/documentation/) provide more info on how a new service can be added to the app and the extractor. Please get in touch with us if you intend to add a new one. Currently supported services are:
|
NewPipe supports multiple services. Our [docs](https://teamnewpipe.github.io/documentation/) provide more info on how a new service can be added to the app and the extractor. Please get in touch with us if you intend to add a new one. Currently supported services are:
|
||||||
|
@ -103,6 +98,10 @@ The more is done the better it gets!
|
||||||
|
|
||||||
If you'd like to get involved, check our [contribution notes](.github/CONTRIBUTING.md).
|
If you'd like to get involved, check our [contribution notes](.github/CONTRIBUTING.md).
|
||||||
|
|
||||||
|
<a href="https://hosted.weblate.org/engage/newpipe/">
|
||||||
|
<img src="https://hosted.weblate.org/widgets/newpipe/-/287x66-grey.png" alt="Translation status" />
|
||||||
|
</a>
|
||||||
|
|
||||||
## Donate
|
## Donate
|
||||||
If you like NewPipe we'd be happy about a donation. You can either send bitcoin or donate via Bountysource or Liberapay. For further info on donating to NewPipe, please visit our [website](https://newpipe.schabi.org/donate).
|
If you like NewPipe we'd be happy about a donation. You can either send bitcoin or donate via Bountysource or Liberapay. For further info on donating to NewPipe, please visit our [website](https://newpipe.schabi.org/donate).
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,10 @@ android {
|
||||||
resValue "string", "app_name", "NewPipe"
|
resValue "string", "app_name", "NewPipe"
|
||||||
minSdkVersion 19
|
minSdkVersion 19
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
versionCode 954
|
versionCode 955
|
||||||
versionName "0.20.0"
|
versionName "0.20.1"
|
||||||
|
|
||||||
|
multiDexEnabled true
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
|
@ -28,7 +30,6 @@ android {
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
debug {
|
debug {
|
||||||
multiDexEnabled true
|
|
||||||
debuggable true
|
debuggable true
|
||||||
|
|
||||||
// suffix the app id and the app name with git branch name
|
// suffix the app id and the app name with git branch name
|
||||||
|
@ -69,6 +70,10 @@ android {
|
||||||
encoding 'utf-8'
|
encoding 'utf-8'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
// Required and used only by groupie
|
// Required and used only by groupie
|
||||||
androidExtensions {
|
androidExtensions {
|
||||||
experimental = true
|
experimental = true
|
||||||
|
@ -94,7 +99,7 @@ ext {
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
checkstyle
|
checkstyle
|
||||||
ktlint
|
// ktlint
|
||||||
}
|
}
|
||||||
|
|
||||||
checkstyle {
|
checkstyle {
|
||||||
|
@ -122,20 +127,20 @@ task runCheckstyle(type: Checkstyle) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
task runKtlint(type: JavaExec) {
|
//task runKtlint(type: JavaExec) {
|
||||||
main = "com.pinterest.ktlint.Main"
|
// main = "com.pinterest.ktlint.Main"
|
||||||
classpath = configurations.ktlint
|
// classpath = configurations.ktlint
|
||||||
args "src/**/*.kt"
|
// args "src/**/*.kt"
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
task formatKtlint(type: JavaExec) {
|
//task formatKtlint(type: JavaExec) {
|
||||||
main = "com.pinterest.ktlint.Main"
|
// main = "com.pinterest.ktlint.Main"
|
||||||
classpath = configurations.ktlint
|
// classpath = configurations.ktlint
|
||||||
args "-F", "src/**/*.kt"
|
// args "-F", "src/**/*.kt"
|
||||||
}
|
//}
|
||||||
|
|
||||||
afterEvaluate {
|
afterEvaluate {
|
||||||
preDebugBuild.dependsOn runCheckstyle, runKtlint
|
preDebugBuild.dependsOn runCheckstyle //, runKtlint
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
@ -145,7 +150,7 @@ dependencies {
|
||||||
kapt "frankiesardo:icepick-processor:${icepickVersion}"
|
kapt "frankiesardo:icepick-processor:${icepickVersion}"
|
||||||
|
|
||||||
checkstyle "com.puppycrawl.tools:checkstyle:${checkstyleVersion}"
|
checkstyle "com.puppycrawl.tools:checkstyle:${checkstyleVersion}"
|
||||||
ktlint "com.pinterest:ktlint:0.35.0"
|
// ktlint "com.pinterest:ktlint:0.35.0"
|
||||||
|
|
||||||
debugImplementation "com.facebook.stetho:stetho:${stethoVersion}"
|
debugImplementation "com.facebook.stetho:stetho:${stethoVersion}"
|
||||||
debugImplementation "com.facebook.stetho:stetho-okhttp3:${stethoVersion}"
|
debugImplementation "com.facebook.stetho:stetho-okhttp3:${stethoVersion}"
|
||||||
|
@ -153,9 +158,9 @@ dependencies {
|
||||||
debugImplementation "com.squareup.leakcanary:leakcanary-android:${leakCanaryVersion}"
|
debugImplementation "com.squareup.leakcanary:leakcanary-android:${leakCanaryVersion}"
|
||||||
implementation "com.squareup.leakcanary:leakcanary-object-watcher-android:${leakCanaryVersion}"
|
implementation "com.squareup.leakcanary:leakcanary-object-watcher-android:${leakCanaryVersion}"
|
||||||
|
|
||||||
debugImplementation "androidx.multidex:multidex:2.0.1"
|
implementation "androidx.multidex:multidex:2.0.1"
|
||||||
|
|
||||||
testImplementation 'junit:junit:4.13'
|
testImplementation 'junit:junit:4.13.1'
|
||||||
testImplementation 'org.mockito:mockito-core:3.3.3'
|
testImplementation 'org.mockito:mockito-core:3.3.3'
|
||||||
|
|
||||||
androidTestImplementation "androidx.test.ext:junit:1.1.1"
|
androidTestImplementation "androidx.test.ext:junit:1.1.1"
|
||||||
|
@ -164,9 +169,11 @@ dependencies {
|
||||||
exclude module: 'support-annotations'
|
exclude module: 'support-annotations'
|
||||||
}
|
}
|
||||||
|
|
||||||
implementation 'com.github.TeamNewPipe:NewPipeExtractor:2463884aa8b696df5812f7feff553008bbd2f888'
|
// NewPipe dependencies
|
||||||
|
// You can use a local version by uncommenting a few lines in settings.gradle
|
||||||
|
implementation 'com.github.TeamNewPipe:NewPipeExtractor:350eed6214b93255d788dfa208b1e9a5e5da91e6'
|
||||||
implementation "com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751"
|
implementation "com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751"
|
||||||
|
|
||||||
implementation "org.jsoup:jsoup:1.13.1"
|
implementation "org.jsoup:jsoup:1.13.1"
|
||||||
|
|
||||||
implementation "com.squareup.okhttp3:okhttp:3.12.12"
|
implementation "com.squareup.okhttp3:okhttp:3.12.12"
|
||||||
|
@ -184,6 +191,7 @@ dependencies {
|
||||||
implementation "androidx.recyclerview:recyclerview:1.1.0"
|
implementation "androidx.recyclerview:recyclerview:1.1.0"
|
||||||
implementation "androidx.cardview:cardview:1.0.0"
|
implementation "androidx.cardview:cardview:1.0.0"
|
||||||
implementation "androidx.constraintlayout:constraintlayout:1.1.3"
|
implementation "androidx.constraintlayout:constraintlayout:1.1.3"
|
||||||
|
implementation 'androidx.core:core-ktx:1.3.1'
|
||||||
|
|
||||||
implementation "androidx.lifecycle:lifecycle-livedata:${androidxLifecycleVersion}"
|
implementation "androidx.lifecycle:lifecycle-livedata:${androidxLifecycleVersion}"
|
||||||
implementation "androidx.lifecycle:lifecycle-viewmodel:${androidxLifecycleVersion}"
|
implementation "androidx.lifecycle:lifecycle-viewmodel:${androidxLifecycleVersion}"
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package org.schabi.newpipe
|
package org.schabi.newpipe
|
||||||
|
|
||||||
import androidx.multidex.MultiDex
|
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.facebook.stetho.Stetho
|
import com.facebook.stetho.Stetho
|
||||||
import com.facebook.stetho.okhttp3.StethoInterceptor
|
import com.facebook.stetho.okhttp3.StethoInterceptor
|
||||||
|
@ -28,12 +27,6 @@ class DebugApp : App() {
|
||||||
return downloader
|
return downloader
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun initACRA() {
|
|
||||||
// install MultiDex before initializing ACRA
|
|
||||||
MultiDex.install(this)
|
|
||||||
super.initACRA()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initStetho() {
|
private fun initStetho() {
|
||||||
// Create an InitializerBuilder
|
// Create an InitializerBuilder
|
||||||
val initializerBuilder = Stetho.newInitializerBuilder(this)
|
val initializerBuilder = Stetho.newInitializerBuilder(this)
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
package org.schabi.newpipe.settings;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
|
||||||
|
import leakcanary.LeakCanary;
|
||||||
|
|
||||||
|
public class DebugSettingsFragment extends BasePreferenceFragment {
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
findPreference(getString(R.string.show_memory_leaks_key))
|
||||||
|
.setOnPreferenceClickListener(preference -> {
|
||||||
|
startActivity(LeakCanary.INSTANCE.newLeakDisplayActivityIntent());
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) {
|
||||||
|
addPreferencesFromResource(R.xml.debug_settings);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,57 +1,56 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<PreferenceScreen
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:key="general_preferences"
|
android:key="general_preferences"
|
||||||
android:title="@string/settings">
|
android:title="@string/settings">
|
||||||
|
|
||||||
<PreferenceScreen
|
<PreferenceScreen
|
||||||
app:iconSpaceReserved="false"
|
|
||||||
android:fragment="org.schabi.newpipe.settings.VideoAudioSettingsFragment"
|
android:fragment="org.schabi.newpipe.settings.VideoAudioSettingsFragment"
|
||||||
android:icon="?attr/ic_headset"
|
android:icon="?attr/ic_headset"
|
||||||
android:title="@string/settings_category_video_audio_title"/>
|
android:title="@string/settings_category_video_audio_title"
|
||||||
|
app:iconSpaceReserved="false" />
|
||||||
|
|
||||||
<PreferenceScreen
|
<PreferenceScreen
|
||||||
app:iconSpaceReserved="false"
|
|
||||||
android:fragment="org.schabi.newpipe.settings.DownloadSettingsFragment"
|
android:fragment="org.schabi.newpipe.settings.DownloadSettingsFragment"
|
||||||
android:icon="?attr/ic_file_download"
|
android:icon="?attr/ic_file_download"
|
||||||
android:title="@string/settings_category_downloads_title"/>
|
android:title="@string/settings_category_downloads_title"
|
||||||
|
app:iconSpaceReserved="false" />
|
||||||
|
|
||||||
<PreferenceScreen
|
<PreferenceScreen
|
||||||
app:iconSpaceReserved="false"
|
|
||||||
android:fragment="org.schabi.newpipe.settings.AppearanceSettingsFragment"
|
android:fragment="org.schabi.newpipe.settings.AppearanceSettingsFragment"
|
||||||
android:icon="?attr/ic_palette"
|
android:icon="?attr/ic_palette"
|
||||||
android:title="@string/settings_category_appearance_title"/>
|
android:title="@string/settings_category_appearance_title"
|
||||||
|
app:iconSpaceReserved="false" />
|
||||||
|
|
||||||
<PreferenceScreen
|
<PreferenceScreen
|
||||||
app:iconSpaceReserved="false"
|
|
||||||
android:fragment="org.schabi.newpipe.settings.HistorySettingsFragment"
|
android:fragment="org.schabi.newpipe.settings.HistorySettingsFragment"
|
||||||
android:icon="?attr/ic_history"
|
android:icon="?attr/ic_history"
|
||||||
android:title="@string/settings_category_history_title"/>
|
android:title="@string/settings_category_history_title"
|
||||||
|
app:iconSpaceReserved="false" />
|
||||||
|
|
||||||
<PreferenceScreen
|
<PreferenceScreen
|
||||||
app:iconSpaceReserved="false"
|
|
||||||
android:fragment="org.schabi.newpipe.settings.ContentSettingsFragment"
|
android:fragment="org.schabi.newpipe.settings.ContentSettingsFragment"
|
||||||
android:icon="?attr/ic_language"
|
android:icon="?attr/ic_language"
|
||||||
android:title="@string/content"/>
|
android:title="@string/content"
|
||||||
|
app:iconSpaceReserved="false" />
|
||||||
|
|
||||||
<PreferenceScreen
|
<PreferenceScreen
|
||||||
app:iconSpaceReserved="false"
|
|
||||||
android:fragment="org.schabi.newpipe.settings.NotificationSettingsFragment"
|
android:fragment="org.schabi.newpipe.settings.NotificationSettingsFragment"
|
||||||
android:icon="?attr/ic_play_arrow"
|
android:icon="?attr/ic_play_arrow"
|
||||||
android:title="@string/settings_category_notification_title"/>
|
android:title="@string/settings_category_notification_title"
|
||||||
|
app:iconSpaceReserved="false" />
|
||||||
|
|
||||||
<PreferenceScreen
|
<PreferenceScreen
|
||||||
app:iconSpaceReserved="false"
|
|
||||||
android:fragment="org.schabi.newpipe.settings.UpdateSettingsFragment"
|
android:fragment="org.schabi.newpipe.settings.UpdateSettingsFragment"
|
||||||
android:icon="?attr/ic_settings_update"
|
android:icon="?attr/ic_settings_update"
|
||||||
|
android:key="update_pref_screen_key"
|
||||||
android:title="@string/settings_category_updates_title"
|
android:title="@string/settings_category_updates_title"
|
||||||
android:key="update_pref_screen_key"/>
|
app:iconSpaceReserved="false" />
|
||||||
|
|
||||||
<PreferenceScreen
|
<PreferenceScreen
|
||||||
app:iconSpaceReserved="false"
|
|
||||||
android:fragment="org.schabi.newpipe.settings.DebugSettingsFragment"
|
android:fragment="org.schabi.newpipe.settings.DebugSettingsFragment"
|
||||||
android:icon="?attr/ic_bug_report"
|
android:icon="?attr/ic_bug_report"
|
||||||
|
android:key="@string/debug_pref_screen_key"
|
||||||
android:title="@string/settings_category_debug_title"
|
android:title="@string/settings_category_debug_title"
|
||||||
android:key="@string/debug_pref_screen_key"/>
|
app:iconSpaceReserved="false" />
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
|
@ -1,7 +1,5 @@
|
||||||
package org.schabi.newpipe;
|
package org.schabi.newpipe;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.app.Application;
|
|
||||||
import android.app.NotificationChannel;
|
import android.app.NotificationChannel;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
@ -10,6 +8,7 @@ import android.os.Build;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.multidex.MultiDexApplication;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
import com.nostra13.universalimageloader.cache.memory.impl.LRULimitedMemoryCache;
|
import com.nostra13.universalimageloader.cache.memory.impl.LRULimitedMemoryCache;
|
||||||
|
@ -33,6 +32,7 @@ import org.schabi.newpipe.util.StateSaver;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InterruptedIOException;
|
import java.io.InterruptedIOException;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ import io.reactivex.plugins.RxJavaPlugins;
|
||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class App extends Application {
|
public class App extends MultiDexApplication {
|
||||||
protected static final String TAG = App.class.toString();
|
protected static final String TAG = App.class.toString();
|
||||||
private static App app;
|
private static App app;
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ public class App extends Application {
|
||||||
Localization.init(getApplicationContext());
|
Localization.init(getApplicationContext());
|
||||||
|
|
||||||
StateSaver.init(this);
|
StateSaver.init(this);
|
||||||
initNotificationChannel();
|
initNotificationChannels();
|
||||||
|
|
||||||
ServiceHelper.initServices(this);
|
ServiceHelper.initServices(this);
|
||||||
|
|
||||||
|
@ -219,49 +219,31 @@ public class App extends Application {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initNotificationChannel() {
|
private void initNotificationChannels() {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String id = getString(R.string.notification_channel_id);
|
String id = getString(R.string.notification_channel_id);
|
||||||
final CharSequence name = getString(R.string.notification_channel_name);
|
String name = getString(R.string.notification_channel_name);
|
||||||
final String description = getString(R.string.notification_channel_description);
|
String description = getString(R.string.notification_channel_description);
|
||||||
|
|
||||||
// Keep this below DEFAULT to avoid making noise on every notification update
|
// Keep this below DEFAULT to avoid making noise on every notification update
|
||||||
final int importance = NotificationManager.IMPORTANCE_LOW;
|
final int importance = NotificationManager.IMPORTANCE_LOW;
|
||||||
|
|
||||||
final NotificationChannel mChannel = new NotificationChannel(id, name, importance);
|
final NotificationChannel mainChannel = new NotificationChannel(id, name, importance);
|
||||||
mChannel.setDescription(description);
|
mainChannel.setDescription(description);
|
||||||
|
|
||||||
final NotificationManager mNotificationManager =
|
id = getString(R.string.app_update_notification_channel_id);
|
||||||
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
name = getString(R.string.app_update_notification_channel_name);
|
||||||
mNotificationManager.createNotificationChannel(mChannel);
|
description = getString(R.string.app_update_notification_channel_description);
|
||||||
|
|
||||||
setUpUpdateNotificationChannel(importance);
|
final NotificationChannel appUpdateChannel = new NotificationChannel(id, name, importance);
|
||||||
}
|
appUpdateChannel.setDescription(description);
|
||||||
|
|
||||||
/**
|
final NotificationManager notificationManager = getSystemService(NotificationManager.class);
|
||||||
* Set up notification channel for app update.
|
notificationManager.createNotificationChannels(Arrays.asList(mainChannel,
|
||||||
*
|
appUpdateChannel));
|
||||||
* @param importance
|
|
||||||
*/
|
|
||||||
@TargetApi(Build.VERSION_CODES.O)
|
|
||||||
private void setUpUpdateNotificationChannel(final int importance) {
|
|
||||||
final String appUpdateId
|
|
||||||
= getString(R.string.app_update_notification_channel_id);
|
|
||||||
final CharSequence appUpdateName
|
|
||||||
= getString(R.string.app_update_notification_channel_name);
|
|
||||||
final String appUpdateDescription
|
|
||||||
= getString(R.string.app_update_notification_channel_description);
|
|
||||||
|
|
||||||
final NotificationChannel appUpdateChannel
|
|
||||||
= new NotificationChannel(appUpdateId, appUpdateName, importance);
|
|
||||||
appUpdateChannel.setDescription(appUpdateDescription);
|
|
||||||
|
|
||||||
final NotificationManager appUpdateNotificationManager
|
|
||||||
= (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
|
||||||
appUpdateNotificationManager.createNotificationChannel(appUpdateChannel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isDisposedRxExceptionsReported() {
|
protected boolean isDisposedRxExceptionsReported() {
|
||||||
|
|
|
@ -2,7 +2,6 @@ package org.schabi.newpipe;
|
||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
|
@ -11,11 +10,12 @@ import android.content.pm.Signature;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import androidx.preference.PreferenceManager;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.core.app.NotificationCompat;
|
import androidx.core.app.NotificationCompat;
|
||||||
import androidx.core.app.NotificationManagerCompat;
|
import androidx.core.app.NotificationManagerCompat;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
import com.grack.nanojson.JsonObject;
|
import com.grack.nanojson.JsonObject;
|
||||||
import com.grack.nanojson.JsonParser;
|
import com.grack.nanojson.JsonParser;
|
||||||
|
@ -213,8 +213,8 @@ public class CheckForNewAppVersionTask extends AsyncTask<Void, Void, String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isConnected() {
|
private boolean isConnected() {
|
||||||
final ConnectivityManager cm =
|
final ConnectivityManager cm = ContextCompat.getSystemService(APP,
|
||||||
(ConnectivityManager) APP.getSystemService(Context.CONNECTIVITY_SERVICE);
|
ConnectivityManager.class);
|
||||||
return cm.getActiveNetworkInfo() != null
|
return cm.getActiveNetworkInfo() != null
|
||||||
&& cm.getActiveNetworkInfo().isConnected();
|
&& cm.getActiveNetworkInfo().isConnected();
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,16 +8,18 @@ import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import androidx.fragment.app.FragmentPagerAdapter;
|
import androidx.lifecycle.Lifecycle;
|
||||||
import androidx.fragment.app.FragmentStatePagerAdapter;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import androidx.viewpager.widget.PagerAdapter;
|
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||||
import androidx.viewpager.widget.ViewPager;
|
import androidx.viewpager2.widget.ViewPager2;
|
||||||
|
|
||||||
import com.google.android.material.tabs.TabLayout;
|
import com.google.android.material.tabs.TabLayout;
|
||||||
|
import com.google.android.material.tabs.TabLayoutMediator;
|
||||||
|
|
||||||
import org.schabi.newpipe.BuildConfig;
|
import org.schabi.newpipe.BuildConfig;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
|
@ -64,20 +66,20 @@ public class AboutActivity extends AppCompatActivity {
|
||||||
"https://github.com/lisawray/groupie", StandardLicenses.MIT)
|
"https://github.com/lisawray/groupie", StandardLicenses.MIT)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static final int POS_ABOUT = 0;
|
||||||
|
private static final int POS_LICENSE = 1;
|
||||||
|
private static final int TOTAL_COUNT = 2;
|
||||||
/**
|
/**
|
||||||
* The {@link PagerAdapter} that will provide
|
* The {@link RecyclerView.Adapter} that will provide
|
||||||
* fragments for each of the sections. We use a
|
* fragments for each of the sections. We use a
|
||||||
* {@link FragmentPagerAdapter} derivative, which will keep every
|
* {@link FragmentStateAdapter} derivative, which will keep every
|
||||||
* loaded fragment in memory. If this becomes too memory intensive, it
|
* loaded fragment in memory.
|
||||||
* may be best to switch to a
|
|
||||||
* {@link FragmentStatePagerAdapter}.
|
|
||||||
*/
|
*/
|
||||||
private SectionsPagerAdapter mSectionsPagerAdapter;
|
private SectionsPagerAdapter mSectionsPagerAdapter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@link ViewPager} that will host the section contents.
|
* The {@link ViewPager2} that will host the section contents.
|
||||||
*/
|
*/
|
||||||
private ViewPager mViewPager;
|
private ViewPager2 mViewPager;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(final Bundle savedInstanceState) {
|
protected void onCreate(final Bundle savedInstanceState) {
|
||||||
|
@ -93,14 +95,25 @@ public class AboutActivity extends AppCompatActivity {
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
// Create the adapter that will return a fragment for each of the three
|
// Create the adapter that will return a fragment for each of the three
|
||||||
// primary sections of the activity.
|
// primary sections of the activity.
|
||||||
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
|
mSectionsPagerAdapter =
|
||||||
|
new SectionsPagerAdapter(getSupportFragmentManager(), getLifecycle());
|
||||||
|
|
||||||
// Set up the ViewPager with the sections adapter.
|
// Set up the ViewPager with the sections adapter.
|
||||||
mViewPager = findViewById(R.id.container);
|
mViewPager = findViewById(R.id.container);
|
||||||
mViewPager.setAdapter(mSectionsPagerAdapter);
|
mViewPager.setAdapter(mSectionsPagerAdapter);
|
||||||
|
|
||||||
final TabLayout tabLayout = findViewById(R.id.tabs);
|
final TabLayout tabLayout = findViewById(R.id.tabs);
|
||||||
tabLayout.setupWithViewPager(mViewPager);
|
new TabLayoutMediator(tabLayout, mViewPager, (tab, position) -> {
|
||||||
|
switch (position) {
|
||||||
|
default:
|
||||||
|
case POS_ABOUT:
|
||||||
|
tab.setText(R.string.tab_about);
|
||||||
|
break;
|
||||||
|
case POS_LICENSE:
|
||||||
|
tab.setText(R.string.tab_licenses);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}).attach();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -162,40 +175,30 @@ public class AboutActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link FragmentPagerAdapter} that returns a fragment corresponding to
|
* A {@link FragmentStateAdapter} that returns a fragment corresponding to
|
||||||
* one of the sections/tabs/pages.
|
* one of the sections/tabs/pages.
|
||||||
*/
|
*/
|
||||||
public class SectionsPagerAdapter extends FragmentPagerAdapter {
|
public static class SectionsPagerAdapter extends FragmentStateAdapter {
|
||||||
public SectionsPagerAdapter(final FragmentManager fm) {
|
public SectionsPagerAdapter(final FragmentManager fm, final Lifecycle lifecycle) {
|
||||||
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
|
super(fm, lifecycle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Fragment getItem(final int position) {
|
public Fragment createFragment(final int position) {
|
||||||
switch (position) {
|
switch (position) {
|
||||||
case 0:
|
default:
|
||||||
|
case POS_ABOUT:
|
||||||
return AboutFragment.newInstance();
|
return AboutFragment.newInstance();
|
||||||
case 1:
|
case POS_LICENSE:
|
||||||
return LicenseFragment.newInstance(SOFTWARE_COMPONENTS);
|
return LicenseFragment.newInstance(SOFTWARE_COMPONENTS);
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getCount() {
|
public int getItemCount() {
|
||||||
// Show 2 total pages.
|
// Show 2 total pages.
|
||||||
return 2;
|
return TOTAL_COUNT;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CharSequence getPageTitle(final int position) {
|
|
||||||
switch (position) {
|
|
||||||
case 0:
|
|
||||||
return getString(R.string.tab_about);
|
|
||||||
case 1:
|
|
||||||
return getString(R.string.tab_licenses);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,4 +33,7 @@ public abstract class PlaylistDAO implements BasicDAO<PlaylistEntity> {
|
||||||
|
|
||||||
@Query("DELETE FROM " + PLAYLIST_TABLE + " WHERE " + PLAYLIST_ID + " = :playlistId")
|
@Query("DELETE FROM " + PLAYLIST_TABLE + " WHERE " + PLAYLIST_ID + " = :playlistId")
|
||||||
public abstract int deletePlaylist(long playlistId);
|
public abstract int deletePlaylist(long playlistId);
|
||||||
|
|
||||||
|
@Query("SELECT COUNT(*) FROM " + PLAYLIST_TABLE)
|
||||||
|
public abstract Flowable<Long> getCount();
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,6 +80,7 @@ import org.schabi.newpipe.fragments.EmptyFragment;
|
||||||
import org.schabi.newpipe.fragments.list.comments.CommentsFragment;
|
import org.schabi.newpipe.fragments.list.comments.CommentsFragment;
|
||||||
import org.schabi.newpipe.fragments.list.videos.RelatedVideosFragment;
|
import org.schabi.newpipe.fragments.list.videos.RelatedVideosFragment;
|
||||||
import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
|
import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
|
||||||
|
import org.schabi.newpipe.local.dialog.PlaylistCreationDialog;
|
||||||
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||||
import org.schabi.newpipe.player.BasePlayer;
|
import org.schabi.newpipe.player.BasePlayer;
|
||||||
import org.schabi.newpipe.player.MainPlayer;
|
import org.schabi.newpipe.player.MainPlayer;
|
||||||
|
@ -482,8 +483,14 @@ public class VideoDetailFragment
|
||||||
break;
|
break;
|
||||||
case R.id.detail_controls_playlist_append:
|
case R.id.detail_controls_playlist_append:
|
||||||
if (getFM() != null && currentInfo != null) {
|
if (getFM() != null && currentInfo != null) {
|
||||||
PlaylistAppendDialog.fromStreamInfo(currentInfo)
|
|
||||||
.show(getFM(), TAG);
|
final PlaylistAppendDialog d = PlaylistAppendDialog.fromStreamInfo(currentInfo);
|
||||||
|
disposables.add(
|
||||||
|
PlaylistAppendDialog.onPlaylistFound(getContext(),
|
||||||
|
() -> d.show(getFM(), TAG),
|
||||||
|
() -> PlaylistCreationDialog.newInstance(d).show(getFM(), TAG)
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case R.id.detail_controls_download:
|
case R.id.detail_controls_download:
|
||||||
|
@ -1563,7 +1570,8 @@ public class VideoDetailFragment
|
||||||
}
|
}
|
||||||
|
|
||||||
private void hideAgeRestrictedContent() {
|
private void hideAgeRestrictedContent() {
|
||||||
showError(getString(R.string.restricted_video), false);
|
showError(getString(R.string.restricted_video,
|
||||||
|
getString(R.string.show_age_restricted_content_title)), false);
|
||||||
|
|
||||||
if (relatedStreamsLayout != null) { // tablet
|
if (relatedStreamsLayout != null) { // tablet
|
||||||
relatedStreamsLayout.setVisibility(View.INVISIBLE);
|
relatedStreamsLayout.setVisibility(View.INVISIBLE);
|
||||||
|
@ -2074,8 +2082,7 @@ public class VideoDetailFragment
|
||||||
if (isClearingQueueConfirmationRequired(activity)
|
if (isClearingQueueConfirmationRequired(activity)
|
||||||
&& playerIsNotStopped()
|
&& playerIsNotStopped()
|
||||||
&& activeQueue != null
|
&& activeQueue != null
|
||||||
&& !activeQueue.equals(playQueue)
|
&& !activeQueue.equals(playQueue)) {
|
||||||
&& activeQueue.getStreams().size() > 1) {
|
|
||||||
showClearingQueueConfirmation(onAllow);
|
showClearingQueueConfirmation(onAllow);
|
||||||
} else {
|
} else {
|
||||||
onAllow.run();
|
onAllow.run();
|
||||||
|
|
|
@ -6,7 +6,6 @@ import android.content.SharedPreferences;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import androidx.preference.PreferenceManager;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
|
@ -15,6 +14,7 @@ import android.view.View;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
import androidx.recyclerview.widget.GridLayoutManager;
|
import androidx.recyclerview.widget.GridLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ import org.schabi.newpipe.fragments.BaseStateFragment;
|
||||||
import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
|
import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
|
||||||
import org.schabi.newpipe.info_list.InfoItemDialog;
|
import org.schabi.newpipe.info_list.InfoItemDialog;
|
||||||
import org.schabi.newpipe.info_list.InfoListAdapter;
|
import org.schabi.newpipe.info_list.InfoListAdapter;
|
||||||
|
import org.schabi.newpipe.player.helper.PlayerHolder;
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
import org.schabi.newpipe.util.OnClickGesture;
|
import org.schabi.newpipe.util.OnClickGesture;
|
||||||
|
@ -36,6 +37,8 @@ import org.schabi.newpipe.util.StateSaver;
|
||||||
import org.schabi.newpipe.util.StreamDialogEntry;
|
import org.schabi.newpipe.util.StreamDialogEntry;
|
||||||
import org.schabi.newpipe.views.SuperScrollLayoutManager;
|
import org.schabi.newpipe.views.SuperScrollLayoutManager;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
|
|
||||||
|
@ -336,21 +339,26 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I>
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final ArrayList<StreamDialogEntry> entries = new ArrayList<>();
|
||||||
|
|
||||||
|
if (PlayerHolder.getType() != null) {
|
||||||
|
entries.add(StreamDialogEntry.enqueue);
|
||||||
|
}
|
||||||
if (item.getStreamType() == StreamType.AUDIO_STREAM) {
|
if (item.getStreamType() == StreamType.AUDIO_STREAM) {
|
||||||
StreamDialogEntry.setEnabledEntries(
|
entries.addAll(Arrays.asList(
|
||||||
StreamDialogEntry.enqueue_on_background,
|
|
||||||
StreamDialogEntry.start_here_on_background,
|
StreamDialogEntry.start_here_on_background,
|
||||||
StreamDialogEntry.append_playlist,
|
StreamDialogEntry.append_playlist,
|
||||||
StreamDialogEntry.share);
|
StreamDialogEntry.share
|
||||||
} else {
|
));
|
||||||
StreamDialogEntry.setEnabledEntries(
|
} else {
|
||||||
StreamDialogEntry.enqueue_on_background,
|
entries.addAll(Arrays.asList(
|
||||||
StreamDialogEntry.enqueue_on_popup,
|
|
||||||
StreamDialogEntry.start_here_on_background,
|
StreamDialogEntry.start_here_on_background,
|
||||||
StreamDialogEntry.start_here_on_popup,
|
StreamDialogEntry.start_here_on_popup,
|
||||||
StreamDialogEntry.append_playlist,
|
StreamDialogEntry.append_playlist,
|
||||||
StreamDialogEntry.share);
|
StreamDialogEntry.share
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
StreamDialogEntry.setEnabledEntries(entries);
|
||||||
|
|
||||||
new InfoItemDialog(activity, item, StreamDialogEntry.getCommands(context),
|
new InfoItemDialog(activity, item, StreamDialogEntry.getCommands(context),
|
||||||
(dialog, which) -> StreamDialogEntry.clickOn(which, this, item)).show();
|
(dialog, which) -> StreamDialogEntry.clickOn(which, this, item)).show();
|
||||||
|
|
|
@ -33,6 +33,7 @@ import org.schabi.newpipe.extractor.stream.StreamType;
|
||||||
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
|
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
|
||||||
import org.schabi.newpipe.info_list.InfoItemDialog;
|
import org.schabi.newpipe.info_list.InfoItemDialog;
|
||||||
import org.schabi.newpipe.local.playlist.RemotePlaylistManager;
|
import org.schabi.newpipe.local.playlist.RemotePlaylistManager;
|
||||||
|
import org.schabi.newpipe.player.helper.PlayerHolder;
|
||||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||||
import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue;
|
import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue;
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
|
@ -46,6 +47,7 @@ import org.schabi.newpipe.util.StreamDialogEntry;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
@ -151,25 +153,26 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final ArrayList<StreamDialogEntry> entries = new ArrayList<>();
|
||||||
|
|
||||||
|
if (PlayerHolder.getType() != null) {
|
||||||
|
entries.add(StreamDialogEntry.enqueue);
|
||||||
|
}
|
||||||
if (item.getStreamType() == StreamType.AUDIO_STREAM) {
|
if (item.getStreamType() == StreamType.AUDIO_STREAM) {
|
||||||
StreamDialogEntry.setEnabledEntries(
|
entries.addAll(Arrays.asList(
|
||||||
StreamDialogEntry.enqueue_on_background,
|
|
||||||
StreamDialogEntry.start_here_on_background,
|
StreamDialogEntry.start_here_on_background,
|
||||||
StreamDialogEntry.append_playlist,
|
StreamDialogEntry.append_playlist,
|
||||||
StreamDialogEntry.share);
|
StreamDialogEntry.share
|
||||||
} else {
|
));
|
||||||
StreamDialogEntry.setEnabledEntries(
|
} else {
|
||||||
StreamDialogEntry.enqueue_on_background,
|
entries.addAll(Arrays.asList(
|
||||||
StreamDialogEntry.enqueue_on_popup,
|
|
||||||
StreamDialogEntry.start_here_on_background,
|
StreamDialogEntry.start_here_on_background,
|
||||||
StreamDialogEntry.start_here_on_popup,
|
StreamDialogEntry.start_here_on_popup,
|
||||||
StreamDialogEntry.append_playlist,
|
StreamDialogEntry.append_playlist,
|
||||||
StreamDialogEntry.share);
|
StreamDialogEntry.share
|
||||||
|
));
|
||||||
StreamDialogEntry.start_here_on_popup.setCustomAction((fragment, infoItem) ->
|
|
||||||
NavigationHelper.playOnPopupPlayer(context,
|
|
||||||
getPlayQueueStartingAt(infoItem), true));
|
|
||||||
}
|
}
|
||||||
|
StreamDialogEntry.setEnabledEntries(entries);
|
||||||
|
|
||||||
StreamDialogEntry.start_here_on_background.setCustomAction((fragment, infoItem) ->
|
StreamDialogEntry.start_here_on_background.setCustomAction((fragment, infoItem) ->
|
||||||
NavigationHelper.playOnBackgroundPlayer(context,
|
NavigationHelper.playOnBackgroundPlayer(context,
|
||||||
|
|
|
@ -5,8 +5,6 @@ import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import androidx.core.text.HtmlCompat;
|
|
||||||
import androidx.preference.PreferenceManager;
|
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
@ -30,6 +28,9 @@ 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.widget.TooltipCompat;
|
import androidx.appcompat.widget.TooltipCompat;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.core.text.HtmlCompat;
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
@ -49,9 +50,9 @@ import org.schabi.newpipe.fragments.list.BaseListFragment;
|
||||||
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
import org.schabi.newpipe.report.UserAction;
|
import org.schabi.newpipe.report.UserAction;
|
||||||
import org.schabi.newpipe.util.DeviceUtils;
|
|
||||||
import org.schabi.newpipe.util.AnimationUtils;
|
import org.schabi.newpipe.util.AnimationUtils;
|
||||||
import org.schabi.newpipe.util.Constants;
|
import org.schabi.newpipe.util.Constants;
|
||||||
|
import org.schabi.newpipe.util.DeviceUtils;
|
||||||
import org.schabi.newpipe.util.ExceptionUtils;
|
import org.schabi.newpipe.util.ExceptionUtils;
|
||||||
import org.schabi.newpipe.util.ExtractorHelper;
|
import org.schabi.newpipe.util.ExtractorHelper;
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
|
@ -639,8 +640,8 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
||||||
}
|
}
|
||||||
|
|
||||||
if (searchEditText.requestFocus()) {
|
if (searchEditText.requestFocus()) {
|
||||||
final InputMethodManager imm = (InputMethodManager) activity.getSystemService(
|
final InputMethodManager imm = ContextCompat.getSystemService(activity,
|
||||||
Context.INPUT_METHOD_SERVICE);
|
InputMethodManager.class);
|
||||||
imm.showSoftInput(searchEditText, InputMethodManager.SHOW_FORCED);
|
imm.showSoftInput(searchEditText, InputMethodManager.SHOW_FORCED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -653,8 +654,8 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final InputMethodManager imm = (InputMethodManager) activity
|
final InputMethodManager imm = ContextCompat.getSystemService(activity,
|
||||||
.getSystemService(Context.INPUT_METHOD_SERVICE);
|
InputMethodManager.class);
|
||||||
imm.hideSoftInputFromWindow(searchEditText.getWindowToken(),
|
imm.hideSoftInputFromWindow(searchEditText.getWindowToken(),
|
||||||
InputMethodManager.RESULT_UNCHANGED_SHOWN);
|
InputMethodManager.RESULT_UNCHANGED_SHOWN);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.schabi.newpipe.local.dialog;
|
package org.schabi.newpipe.local.dialog;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
@ -29,6 +30,7 @@ import java.util.List;
|
||||||
|
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.disposables.CompositeDisposable;
|
import io.reactivex.disposables.CompositeDisposable;
|
||||||
|
import io.reactivex.disposables.Disposable;
|
||||||
|
|
||||||
public final class PlaylistAppendDialog extends PlaylistDialog {
|
public final class PlaylistAppendDialog extends PlaylistDialog {
|
||||||
private static final String TAG = PlaylistAppendDialog.class.getCanonicalName();
|
private static final String TAG = PlaylistAppendDialog.class.getCanonicalName();
|
||||||
|
@ -38,6 +40,23 @@ public final class PlaylistAppendDialog extends PlaylistDialog {
|
||||||
|
|
||||||
private CompositeDisposable playlistDisposables = new CompositeDisposable();
|
private CompositeDisposable playlistDisposables = new CompositeDisposable();
|
||||||
|
|
||||||
|
public static Disposable onPlaylistFound(
|
||||||
|
final Context context, final Runnable onSuccess, final Runnable onFailed
|
||||||
|
) {
|
||||||
|
final LocalPlaylistManager playlistManager =
|
||||||
|
new LocalPlaylistManager(NewPipeDatabase.getInstance(context));
|
||||||
|
|
||||||
|
return playlistManager.hasPlaylists()
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(hasPlaylists -> {
|
||||||
|
if (hasPlaylists) {
|
||||||
|
onSuccess.run();
|
||||||
|
} else {
|
||||||
|
onFailed.run();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public static PlaylistAppendDialog fromStreamInfo(final StreamInfo info) {
|
public static PlaylistAppendDialog fromStreamInfo(final StreamInfo info) {
|
||||||
final PlaylistAppendDialog dialog = new PlaylistAppendDialog();
|
final PlaylistAppendDialog dialog = new PlaylistAppendDialog();
|
||||||
dialog.setInfo(Collections.singletonList(new StreamEntity(info)));
|
dialog.setInfo(Collections.singletonList(new StreamEntity(info)));
|
||||||
|
@ -136,11 +155,6 @@ public final class PlaylistAppendDialog extends PlaylistDialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onPlaylistsReceived(@NonNull final List<PlaylistMetadataEntry> playlists) {
|
private void onPlaylistsReceived(@NonNull final List<PlaylistMetadataEntry> playlists) {
|
||||||
if (playlists.isEmpty()) {
|
|
||||||
openCreatePlaylistDialog();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (playlistAdapter != null && playlistRecyclerView != null) {
|
if (playlistAdapter != null && playlistRecyclerView != null) {
|
||||||
playlistAdapter.clearStreamItemList();
|
playlistAdapter.clearStreamItemList();
|
||||||
playlistAdapter.addItems(playlists);
|
playlistAdapter.addItems(playlists);
|
||||||
|
|
|
@ -26,6 +26,12 @@ public final class PlaylistCreationDialog extends PlaylistDialog {
|
||||||
return dialog;
|
return dialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static PlaylistCreationDialog newInstance(final PlaylistAppendDialog appendDialog) {
|
||||||
|
final PlaylistCreationDialog dialog = new PlaylistCreationDialog();
|
||||||
|
dialog.setInfo(appendDialog.getStreams());
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Dialog
|
// Dialog
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
|
@ -29,6 +29,8 @@ import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.core.os.bundleOf
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
|
@ -253,11 +255,9 @@ class FeedFragment : BaseListFragment<FeedState, Unit>() {
|
||||||
|
|
||||||
oldestSubscriptionUpdate = loadedState.oldestUpdate
|
oldestSubscriptionUpdate = loadedState.oldestUpdate
|
||||||
|
|
||||||
|
refresh_subtitle_text.isVisible = loadedState.notLoadedCount > 0
|
||||||
if (loadedState.notLoadedCount > 0) {
|
if (loadedState.notLoadedCount > 0) {
|
||||||
refresh_subtitle_text.visibility = View.VISIBLE
|
|
||||||
refresh_subtitle_text.text = getString(R.string.feed_subscription_not_loaded_count, loadedState.notLoadedCount)
|
refresh_subtitle_text.text = getString(R.string.feed_subscription_not_loaded_count, loadedState.notLoadedCount)
|
||||||
} else {
|
|
||||||
refresh_subtitle_text.visibility = View.GONE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loadedState.itemsErrors.isNotEmpty()) {
|
if (loadedState.itemsErrors.isNotEmpty()) {
|
||||||
|
@ -330,12 +330,7 @@ class FeedFragment : BaseListFragment<FeedState, Unit>() {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun newInstance(groupId: Long = FeedGroupEntity.GROUP_ALL_ID, groupName: String? = null): FeedFragment {
|
fun newInstance(groupId: Long = FeedGroupEntity.GROUP_ALL_ID, groupName: String? = null): FeedFragment {
|
||||||
val feedFragment = FeedFragment()
|
val feedFragment = FeedFragment()
|
||||||
|
feedFragment.arguments = bundleOf(KEY_GROUP_ID to groupId, KEY_GROUP_NAME to groupName)
|
||||||
feedFragment.arguments = Bundle().apply {
|
|
||||||
putLong(KEY_GROUP_ID, groupId)
|
|
||||||
putString(KEY_GROUP_NAME, groupName)
|
|
||||||
}
|
|
||||||
|
|
||||||
return feedFragment
|
return feedFragment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,9 @@ package org.schabi.newpipe.local.history;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import androidx.preference.PreferenceManager;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
import org.schabi.newpipe.NewPipeDatabase;
|
import org.schabi.newpipe.NewPipeDatabase;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
|
@ -101,9 +101,11 @@ public class HistoryRecordManager {
|
||||||
})).subscribeOn(Schedulers.io());
|
})).subscribeOn(Schedulers.io());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Single<Integer> deleteStreamHistory(final long streamId) {
|
public Completable deleteStreamHistoryAndState(final long streamId) {
|
||||||
return Single.fromCallable(() -> streamHistoryTable.deleteStreamHistory(streamId))
|
return Completable.fromAction(() -> {
|
||||||
.subscribeOn(Schedulers.io());
|
streamStateTable.deleteState(streamId);
|
||||||
|
streamHistoryTable.deleteStreamHistory(streamId);
|
||||||
|
}).subscribeOn(Schedulers.io());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Single<Integer> deleteWholeStreamHistory() {
|
public Single<Integer> deleteWholeStreamHistory() {
|
||||||
|
@ -111,7 +113,7 @@ public class HistoryRecordManager {
|
||||||
.subscribeOn(Schedulers.io());
|
.subscribeOn(Schedulers.io());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Single<Integer> deleteCompelteStreamStateHistory() {
|
public Single<Integer> deleteCompleteStreamStateHistory() {
|
||||||
return Single.fromCallable(streamStateTable::deleteAll)
|
return Single.fromCallable(streamStateTable::deleteAll)
|
||||||
.subscribeOn(Schedulers.io());
|
.subscribeOn(Schedulers.io());
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||||
import org.schabi.newpipe.info_list.InfoItemDialog;
|
import org.schabi.newpipe.info_list.InfoItemDialog;
|
||||||
import org.schabi.newpipe.local.BaseLocalListFragment;
|
import org.schabi.newpipe.local.BaseLocalListFragment;
|
||||||
|
import org.schabi.newpipe.player.helper.PlayerHolder;
|
||||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||||
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
|
@ -40,6 +41,7 @@ import org.schabi.newpipe.util.StreamDialogEntry;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -387,27 +389,28 @@ public class StatisticsPlaylistFragment
|
||||||
}
|
}
|
||||||
final StreamInfoItem infoItem = item.toStreamInfoItem();
|
final StreamInfoItem infoItem = item.toStreamInfoItem();
|
||||||
|
|
||||||
|
final ArrayList<StreamDialogEntry> entries = new ArrayList<>();
|
||||||
|
|
||||||
|
if (PlayerHolder.getType() != null) {
|
||||||
|
entries.add(StreamDialogEntry.enqueue);
|
||||||
|
}
|
||||||
if (infoItem.getStreamType() == StreamType.AUDIO_STREAM) {
|
if (infoItem.getStreamType() == StreamType.AUDIO_STREAM) {
|
||||||
StreamDialogEntry.setEnabledEntries(
|
entries.addAll(Arrays.asList(
|
||||||
StreamDialogEntry.enqueue_on_background,
|
|
||||||
StreamDialogEntry.start_here_on_background,
|
StreamDialogEntry.start_here_on_background,
|
||||||
StreamDialogEntry.delete,
|
StreamDialogEntry.delete,
|
||||||
StreamDialogEntry.append_playlist,
|
StreamDialogEntry.append_playlist,
|
||||||
StreamDialogEntry.share);
|
StreamDialogEntry.share
|
||||||
} else {
|
));
|
||||||
StreamDialogEntry.setEnabledEntries(
|
} else {
|
||||||
StreamDialogEntry.enqueue_on_background,
|
entries.addAll(Arrays.asList(
|
||||||
StreamDialogEntry.enqueue_on_popup,
|
|
||||||
StreamDialogEntry.start_here_on_background,
|
StreamDialogEntry.start_here_on_background,
|
||||||
StreamDialogEntry.start_here_on_popup,
|
StreamDialogEntry.start_here_on_popup,
|
||||||
StreamDialogEntry.delete,
|
StreamDialogEntry.delete,
|
||||||
StreamDialogEntry.append_playlist,
|
StreamDialogEntry.append_playlist,
|
||||||
StreamDialogEntry.share);
|
StreamDialogEntry.share
|
||||||
|
));
|
||||||
StreamDialogEntry.start_here_on_popup.setCustomAction((fragment, infoItemDuplicate) ->
|
|
||||||
NavigationHelper
|
|
||||||
.playOnPopupPlayer(context, getPlayQueueStartingAt(item), true));
|
|
||||||
}
|
}
|
||||||
|
StreamDialogEntry.setEnabledEntries(entries);
|
||||||
|
|
||||||
StreamDialogEntry.start_here_on_background.setCustomAction((fragment, infoItemDuplicate) ->
|
StreamDialogEntry.start_here_on_background.setCustomAction((fragment, infoItemDuplicate) ->
|
||||||
NavigationHelper
|
NavigationHelper
|
||||||
|
@ -420,14 +423,14 @@ public class StatisticsPlaylistFragment
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deleteEntry(final int index) {
|
private void deleteEntry(final int index) {
|
||||||
final LocalItem infoItem = itemListAdapter.getItemsList()
|
final LocalItem infoItem = itemListAdapter.getItemsList().get(index);
|
||||||
.get(index);
|
|
||||||
if (infoItem instanceof StreamStatisticsEntry) {
|
if (infoItem instanceof StreamStatisticsEntry) {
|
||||||
final StreamStatisticsEntry entry = (StreamStatisticsEntry) infoItem;
|
final StreamStatisticsEntry entry = (StreamStatisticsEntry) infoItem;
|
||||||
final Disposable onDelete = recordManager.deleteStreamHistory(entry.getStreamId())
|
final Disposable onDelete = recordManager
|
||||||
|
.deleteStreamHistoryAndState(entry.getStreamId())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(
|
.subscribe(
|
||||||
howManyDeleted -> {
|
() -> {
|
||||||
if (getView() != null) {
|
if (getView() != null) {
|
||||||
Snackbar.make(getView(), R.string.one_item_deleted,
|
Snackbar.make(getView(), R.string.one_item_deleted,
|
||||||
Snackbar.LENGTH_SHORT).show();
|
Snackbar.LENGTH_SHORT).show();
|
||||||
|
|
|
@ -104,7 +104,6 @@ public class LocalPlaylistStreamItemHolder extends LocalItemHolder {
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
itemThumbnailView.setOnTouchListener(getOnTouchListener(item));
|
|
||||||
itemHandleView.setOnTouchListener(getOnTouchListener(item));
|
itemHandleView.setOnTouchListener(getOnTouchListener(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ import org.schabi.newpipe.extractor.stream.StreamType;
|
||||||
import org.schabi.newpipe.info_list.InfoItemDialog;
|
import org.schabi.newpipe.info_list.InfoItemDialog;
|
||||||
import org.schabi.newpipe.local.BaseLocalListFragment;
|
import org.schabi.newpipe.local.BaseLocalListFragment;
|
||||||
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||||
|
import org.schabi.newpipe.player.helper.PlayerHolder;
|
||||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||||
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
||||||
import org.schabi.newpipe.report.UserAction;
|
import org.schabi.newpipe.report.UserAction;
|
||||||
|
@ -45,6 +46,7 @@ import org.schabi.newpipe.util.OnClickGesture;
|
||||||
import org.schabi.newpipe.util.StreamDialogEntry;
|
import org.schabi.newpipe.util.StreamDialogEntry;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -756,29 +758,30 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
|
||||||
}
|
}
|
||||||
final StreamInfoItem infoItem = item.toStreamInfoItem();
|
final StreamInfoItem infoItem = item.toStreamInfoItem();
|
||||||
|
|
||||||
|
final ArrayList<StreamDialogEntry> entries = new ArrayList<>();
|
||||||
|
|
||||||
|
if (PlayerHolder.getType() != null) {
|
||||||
|
entries.add(StreamDialogEntry.enqueue);
|
||||||
|
}
|
||||||
if (infoItem.getStreamType() == StreamType.AUDIO_STREAM) {
|
if (infoItem.getStreamType() == StreamType.AUDIO_STREAM) {
|
||||||
StreamDialogEntry.setEnabledEntries(
|
entries.addAll(Arrays.asList(
|
||||||
StreamDialogEntry.enqueue_on_background,
|
|
||||||
StreamDialogEntry.start_here_on_background,
|
StreamDialogEntry.start_here_on_background,
|
||||||
StreamDialogEntry.set_as_playlist_thumbnail,
|
StreamDialogEntry.set_as_playlist_thumbnail,
|
||||||
StreamDialogEntry.delete,
|
StreamDialogEntry.delete,
|
||||||
StreamDialogEntry.append_playlist,
|
StreamDialogEntry.append_playlist,
|
||||||
StreamDialogEntry.share);
|
StreamDialogEntry.share
|
||||||
} else {
|
));
|
||||||
StreamDialogEntry.setEnabledEntries(
|
} else {
|
||||||
StreamDialogEntry.enqueue_on_background,
|
entries.addAll(Arrays.asList(
|
||||||
StreamDialogEntry.enqueue_on_popup,
|
|
||||||
StreamDialogEntry.start_here_on_background,
|
StreamDialogEntry.start_here_on_background,
|
||||||
StreamDialogEntry.start_here_on_popup,
|
StreamDialogEntry.start_here_on_popup,
|
||||||
StreamDialogEntry.set_as_playlist_thumbnail,
|
StreamDialogEntry.set_as_playlist_thumbnail,
|
||||||
StreamDialogEntry.delete,
|
StreamDialogEntry.delete,
|
||||||
StreamDialogEntry.append_playlist,
|
StreamDialogEntry.append_playlist,
|
||||||
StreamDialogEntry.share);
|
StreamDialogEntry.share
|
||||||
|
));
|
||||||
StreamDialogEntry.start_here_on_popup.setCustomAction(
|
|
||||||
(fragment, infoItemDuplicate) -> NavigationHelper.
|
|
||||||
playOnPopupPlayer(context, getPlayQueueStartingAt(item), true));
|
|
||||||
}
|
}
|
||||||
|
StreamDialogEntry.setEnabledEntries(entries);
|
||||||
|
|
||||||
StreamDialogEntry.start_here_on_background.setCustomAction((fragment, infoItemDuplicate) ->
|
StreamDialogEntry.start_here_on_background.setCustomAction((fragment, infoItemDuplicate) ->
|
||||||
NavigationHelper.playOnBackgroundPlayer(context,
|
NavigationHelper.playOnBackgroundPlayer(context,
|
||||||
|
|
|
@ -126,4 +126,10 @@ public class LocalPlaylistManager {
|
||||||
}).subscribeOn(Schedulers.io());
|
}).subscribeOn(Schedulers.io());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Maybe<Boolean> hasPlaylists() {
|
||||||
|
return playlistTable.getCount()
|
||||||
|
.firstElement()
|
||||||
|
.map(count -> count > 0)
|
||||||
|
.subscribeOn(Schedulers.io());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
package org.schabi.newpipe.local.subscription.dialog
|
package org.schabi.newpipe.local.subscription.dialog
|
||||||
|
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.content.Context
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import android.text.Editable
|
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.text.TextWatcher
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.core.content.getSystemService
|
||||||
|
import androidx.core.os.bundleOf
|
||||||
|
import androidx.core.view.isGone
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.core.widget.doOnTextChanged
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
@ -191,16 +193,11 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
|
||||||
}
|
}
|
||||||
|
|
||||||
group_name_input_container.error = null
|
group_name_input_container.error = null
|
||||||
group_name_input.addTextChangedListener(object : TextWatcher {
|
group_name_input.doOnTextChanged { text, _, _, _ ->
|
||||||
override fun afterTextChanged(s: Editable?) {}
|
if (group_name_input_container.isErrorEnabled && !text.isNullOrBlank()) {
|
||||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
group_name_input_container.error = null
|
||||||
|
|
||||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
|
||||||
if (group_name_input_container.isErrorEnabled && !s.isNullOrBlank()) {
|
|
||||||
group_name_input_container.error = null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
confirm_button.setOnClickListener { handlePositiveButton() }
|
confirm_button.setOnClickListener { handlePositiveButton() }
|
||||||
|
|
||||||
|
@ -242,15 +239,11 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toolbar_search_edit_text.addTextChangedListener(object : TextWatcher {
|
toolbar_search_edit_text.doOnTextChanged { _, _, _, _ ->
|
||||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) = Unit
|
val newQuery: String = toolbar_search_edit_text.text.toString()
|
||||||
override fun afterTextChanged(s: Editable) = Unit
|
subscriptionsCurrentSearchQuery = newQuery
|
||||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
viewModel.filterSubscriptionsBy(newQuery)
|
||||||
val newQuery: String = toolbar_search_edit_text.text.toString()
|
}
|
||||||
subscriptionsCurrentSearchQuery = newQuery
|
|
||||||
viewModel.filterSubscriptionsBy(newQuery)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
subscriptionGroupAdapter.setOnItemClickListener(subscriptionPickerItemListener)
|
subscriptionGroupAdapter.setOnItemClickListener(subscriptionPickerItemListener)
|
||||||
}
|
}
|
||||||
|
@ -414,21 +407,14 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
|
||||||
else -> android.R.string.ok
|
else -> android.R.string.ok
|
||||||
})
|
})
|
||||||
|
|
||||||
delete_button.visibility = when {
|
delete_button.isGone = currentScreen != InitialScreen || groupId == NO_GROUP_SELECTED
|
||||||
currentScreen != InitialScreen -> View.GONE
|
|
||||||
groupId == NO_GROUP_SELECTED -> View.GONE
|
|
||||||
else -> View.VISIBLE
|
|
||||||
}
|
|
||||||
|
|
||||||
hideKeyboard()
|
hideKeyboard()
|
||||||
hideSearch()
|
hideSearch()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun View.onlyVisibleIn(vararg screens: ScreenState) {
|
private fun View.onlyVisibleIn(vararg screens: ScreenState) {
|
||||||
visibility = when (currentScreen) {
|
isVisible = currentScreen in screens
|
||||||
in screens -> View.VISIBLE
|
|
||||||
else -> View.GONE
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*///////////////////////////////////////////////////////////////////////////
|
/*///////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -459,7 +445,7 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private val inputMethodManager by lazy {
|
private val inputMethodManager by lazy {
|
||||||
requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
requireActivity().getSystemService<InputMethodManager>()!!
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showKeyboardSearch() {
|
private fun showKeyboardSearch() {
|
||||||
|
@ -501,11 +487,7 @@ class FeedGroupDialog : DialogFragment(), BackPressable {
|
||||||
|
|
||||||
fun newInstance(groupId: Long = NO_GROUP_SELECTED): FeedGroupDialog {
|
fun newInstance(groupId: Long = NO_GROUP_SELECTED): FeedGroupDialog {
|
||||||
val dialog = FeedGroupDialog()
|
val dialog = FeedGroupDialog()
|
||||||
|
dialog.arguments = bundleOf(KEY_GROUP_ID to groupId)
|
||||||
dialog.arguments = Bundle().apply {
|
|
||||||
putLong(KEY_GROUP_ID, groupId)
|
|
||||||
}
|
|
||||||
|
|
||||||
return dialog
|
return dialog
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
package org.schabi.newpipe.local.subscription.item
|
package org.schabi.newpipe.local.subscription.item
|
||||||
|
|
||||||
import android.view.View.GONE
|
|
||||||
import android.view.View.OnClickListener
|
import android.view.View.OnClickListener
|
||||||
import android.view.View.VISIBLE
|
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder
|
import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder
|
||||||
import com.xwray.groupie.kotlinandroidextensions.Item
|
import com.xwray.groupie.kotlinandroidextensions.Item
|
||||||
import kotlinx.android.synthetic.main.header_with_menu_item.header_menu_item
|
import kotlinx.android.synthetic.main.header_with_menu_item.header_menu_item
|
||||||
|
@ -47,6 +46,6 @@ class HeaderWithMenuItem(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateMenuItemVisibility(viewHolder: GroupieViewHolder) {
|
private fun updateMenuItemVisibility(viewHolder: GroupieViewHolder) {
|
||||||
viewHolder.header_menu_item.visibility = if (showMenuItem) VISIBLE else GONE
|
viewHolder.header_menu_item.isVisible = showMenuItem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.schabi.newpipe.local.subscription.item
|
package org.schabi.newpipe.local.subscription.item
|
||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import com.nostra13.universalimageloader.core.ImageLoader
|
import com.nostra13.universalimageloader.core.ImageLoader
|
||||||
import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder
|
import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder
|
||||||
import com.xwray.groupie.kotlinandroidextensions.Item
|
import com.xwray.groupie.kotlinandroidextensions.Item
|
||||||
|
@ -25,7 +26,7 @@ data class PickerSubscriptionItem(
|
||||||
viewHolder.thumbnail_view, ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS)
|
viewHolder.thumbnail_view, ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS)
|
||||||
|
|
||||||
viewHolder.title_view.text = subscriptionEntity.name
|
viewHolder.title_view.text = subscriptionEntity.name
|
||||||
viewHolder.selected_highlight.visibility = if (isSelected) View.VISIBLE else View.GONE
|
viewHolder.selected_highlight.isVisible = isSelected
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun unbind(viewHolder: GroupieViewHolder) {
|
override fun unbind(viewHolder: GroupieViewHolder) {
|
||||||
|
|
|
@ -30,6 +30,8 @@ import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
|
@ -91,7 +93,7 @@ public final class MainPlayer extends Service {
|
||||||
Log.d(TAG, "onCreate() called");
|
Log.d(TAG, "onCreate() called");
|
||||||
}
|
}
|
||||||
assureCorrectAppLanguage(this);
|
assureCorrectAppLanguage(this);
|
||||||
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
|
windowManager = ContextCompat.getSystemService(this, WindowManager.class);
|
||||||
|
|
||||||
ThemeHelper.setTheme(this);
|
ThemeHelper.setTheme(this);
|
||||||
createView();
|
createView();
|
||||||
|
|
|
@ -116,10 +116,11 @@ public final class NotificationUtil {
|
||||||
.setMediaSession(player.mediaSessionManager.getSessionToken())
|
.setMediaSession(player.mediaSessionManager.getSessionToken())
|
||||||
.setShowActionsInCompactView(compactSlots))
|
.setShowActionsInCompactView(compactSlots))
|
||||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||||
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
|
|
||||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||||
.setColor(ContextCompat.getColor(player.context, R.color.gray))
|
|
||||||
.setCategory(NotificationCompat.CATEGORY_TRANSPORT)
|
.setCategory(NotificationCompat.CATEGORY_TRANSPORT)
|
||||||
|
.setShowWhen(false)
|
||||||
|
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
|
||||||
|
.setColor(ContextCompat.getColor(player.context, R.color.gray))
|
||||||
.setDeleteIntent(PendingIntent.getBroadcast(player.context, NOTIFICATION_ID,
|
.setDeleteIntent(PendingIntent.getBroadcast(player.context, NOTIFICATION_ID,
|
||||||
new Intent(ACTION_CLOSE), FLAG_UPDATE_CURRENT));
|
new Intent(ACTION_CLOSE), FLAG_UPDATE_CURRENT));
|
||||||
|
|
||||||
|
@ -148,7 +149,10 @@ public final class NotificationUtil {
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
boolean shouldUpdateBufferingSlot() {
|
boolean shouldUpdateBufferingSlot() {
|
||||||
if (notificationBuilder.mActions.size() < 3) {
|
if (notificationBuilder == null) {
|
||||||
|
// if there is no notification active, there is no point in updating it
|
||||||
|
return false;
|
||||||
|
} else if (notificationBuilder.mActions.size() < 3) {
|
||||||
// this should never happen, but let's make sure notification actions are populated
|
// this should never happen, but let's make sure notification actions are populated
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ import org.schabi.newpipe.extractor.StreamingService;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
|
import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
|
||||||
import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
|
import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
|
||||||
|
import org.schabi.newpipe.local.dialog.PlaylistCreationDialog;
|
||||||
import org.schabi.newpipe.player.event.PlayerEventListener;
|
import org.schabi.newpipe.player.event.PlayerEventListener;
|
||||||
import org.schabi.newpipe.player.helper.PlaybackParameterDialog;
|
import org.schabi.newpipe.player.helper.PlaybackParameterDialog;
|
||||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||||
|
@ -571,8 +572,13 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openPlaylistAppendDialog(final List<PlayQueueItem> playlist) {
|
private void openPlaylistAppendDialog(final List<PlayQueueItem> playlist) {
|
||||||
PlaylistAppendDialog.fromPlayQueueItems(playlist)
|
final PlaylistAppendDialog d = PlaylistAppendDialog.fromPlayQueueItems(playlist);
|
||||||
.show(getSupportFragmentManager(), getTag());
|
|
||||||
|
PlaylistAppendDialog.onPlaylistFound(getApplicationContext(),
|
||||||
|
() -> d.show(getSupportFragmentManager(), getTag()),
|
||||||
|
() -> PlaylistCreationDialog.newInstance(d)
|
||||||
|
.show(getSupportFragmentManager(), getTag()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -32,8 +32,6 @@ import android.graphics.PixelFormat;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.view.DisplayCutout;
|
|
||||||
import androidx.preference.PreferenceManager;
|
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
@ -60,8 +58,13 @@ import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.appcompat.content.res.AppCompatResources;
|
import androidx.appcompat.content.res.AppCompatResources;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.core.view.DisplayCutoutCompat;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
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.google.android.exoplayer2.ExoPlaybackException;
|
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||||
import com.google.android.exoplayer2.Player;
|
import com.google.android.exoplayer2.Player;
|
||||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||||
|
@ -72,6 +75,7 @@ import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
|
||||||
import com.google.android.exoplayer2.ui.SubtitleView;
|
import com.google.android.exoplayer2.ui.SubtitleView;
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
import com.nostra13.universalimageloader.core.assist.FailReason;
|
import com.nostra13.universalimageloader.core.assist.FailReason;
|
||||||
|
|
||||||
import org.schabi.newpipe.MainActivity;
|
import org.schabi.newpipe.MainActivity;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
|
@ -92,9 +96,9 @@ import org.schabi.newpipe.player.playqueue.PlayQueueItemTouchCallback;
|
||||||
import org.schabi.newpipe.player.resolver.AudioPlaybackResolver;
|
import org.schabi.newpipe.player.resolver.AudioPlaybackResolver;
|
||||||
import org.schabi.newpipe.player.resolver.MediaSourceTag;
|
import org.schabi.newpipe.player.resolver.MediaSourceTag;
|
||||||
import org.schabi.newpipe.player.resolver.VideoPlaybackResolver;
|
import org.schabi.newpipe.player.resolver.VideoPlaybackResolver;
|
||||||
import org.schabi.newpipe.util.DeviceUtils;
|
|
||||||
import org.schabi.newpipe.util.AnimationUtils;
|
import org.schabi.newpipe.util.AnimationUtils;
|
||||||
import org.schabi.newpipe.util.Constants;
|
import org.schabi.newpipe.util.Constants;
|
||||||
|
import org.schabi.newpipe.util.DeviceUtils;
|
||||||
import org.schabi.newpipe.util.KoreUtil;
|
import org.schabi.newpipe.util.KoreUtil;
|
||||||
import org.schabi.newpipe.util.ListHelper;
|
import org.schabi.newpipe.util.ListHelper;
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
|
@ -105,7 +109,6 @@ import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static android.content.Context.WINDOW_SERVICE;
|
|
||||||
import static org.schabi.newpipe.player.MainPlayer.ACTION_CLOSE;
|
import static org.schabi.newpipe.player.MainPlayer.ACTION_CLOSE;
|
||||||
import static org.schabi.newpipe.player.MainPlayer.ACTION_FAST_FORWARD;
|
import static org.schabi.newpipe.player.MainPlayer.ACTION_FAST_FORWARD;
|
||||||
import static org.schabi.newpipe.player.MainPlayer.ACTION_FAST_REWIND;
|
import static org.schabi.newpipe.player.MainPlayer.ACTION_FAST_REWIND;
|
||||||
|
@ -270,7 +273,7 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
super("MainPlayer" + TAG, service);
|
super("MainPlayer" + TAG, service);
|
||||||
this.service = service;
|
this.service = service;
|
||||||
this.shouldUpdateOnProgress = true;
|
this.shouldUpdateOnProgress = true;
|
||||||
this.windowManager = (WindowManager) service.getSystemService(WINDOW_SERVICE);
|
this.windowManager = ContextCompat.getSystemService(service, WindowManager.class);
|
||||||
this.defaultPreferences = PreferenceManager.getDefaultSharedPreferences(service);
|
this.defaultPreferences = PreferenceManager.getDefaultSharedPreferences(service);
|
||||||
this.resolver = new AudioPlaybackResolver(context, dataSource);
|
this.resolver = new AudioPlaybackResolver(context, dataSource);
|
||||||
}
|
}
|
||||||
|
@ -498,16 +501,14 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
settingsContentObserver);
|
settingsContentObserver);
|
||||||
getRootView().addOnLayoutChangeListener(this);
|
getRootView().addOnLayoutChangeListener(this);
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
ViewCompat.setOnApplyWindowInsetsListener(queueLayout, (view, windowInsets) -> {
|
||||||
queueLayout.setOnApplyWindowInsetsListener((view, windowInsets) -> {
|
final DisplayCutoutCompat cutout = windowInsets.getDisplayCutout();
|
||||||
final DisplayCutout cutout = windowInsets.getDisplayCutout();
|
if (cutout != null) {
|
||||||
if (cutout != null) {
|
view.setPadding(cutout.getSafeInsetLeft(), cutout.getSafeInsetTop(),
|
||||||
view.setPadding(cutout.getSafeInsetLeft(), cutout.getSafeInsetTop(),
|
cutout.getSafeInsetRight(), cutout.getSafeInsetBottom());
|
||||||
cutout.getSafeInsetRight(), cutout.getSafeInsetBottom());
|
}
|
||||||
}
|
return windowInsets;
|
||||||
return windowInsets;
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// PlaybackControlRoot already consumed window insets but we should pass them to
|
// PlaybackControlRoot already consumed window insets but we should pass them to
|
||||||
// player_overlays too. Without it they will be off-centered
|
// player_overlays too. Without it they will be off-centered
|
||||||
|
@ -1765,13 +1766,10 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
|
|
||||||
updateScreenSize();
|
updateScreenSize();
|
||||||
|
|
||||||
final boolean popupRememberSizeAndPos = PlayerHelper.isRememberingPopupDimensions(service);
|
|
||||||
final float defaultSize = service.getResources().getDimension(R.dimen.popup_default_width);
|
final float defaultSize = service.getResources().getDimension(R.dimen.popup_default_width);
|
||||||
final SharedPreferences sharedPreferences =
|
final SharedPreferences sharedPreferences =
|
||||||
PreferenceManager.getDefaultSharedPreferences(service);
|
PreferenceManager.getDefaultSharedPreferences(service);
|
||||||
popupWidth = popupRememberSizeAndPos
|
popupWidth = sharedPreferences.getFloat(POPUP_SAVED_WIDTH, defaultSize);
|
||||||
? sharedPreferences.getFloat(POPUP_SAVED_WIDTH, defaultSize)
|
|
||||||
: defaultSize;
|
|
||||||
popupHeight = getMinimumVideoHeight(popupWidth);
|
popupHeight = getMinimumVideoHeight(popupWidth);
|
||||||
|
|
||||||
popupLayoutParams = new WindowManager.LayoutParams(
|
popupLayoutParams = new WindowManager.LayoutParams(
|
||||||
|
@ -1785,10 +1783,8 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
|
|
||||||
final int centerX = (int) (screenWidth / 2f - popupWidth / 2f);
|
final int centerX = (int) (screenWidth / 2f - popupWidth / 2f);
|
||||||
final int centerY = (int) (screenHeight / 2f - popupHeight / 2f);
|
final int centerY = (int) (screenHeight / 2f - popupHeight / 2f);
|
||||||
popupLayoutParams.x = popupRememberSizeAndPos
|
popupLayoutParams.x = sharedPreferences.getInt(POPUP_SAVED_X, centerX);
|
||||||
? sharedPreferences.getInt(POPUP_SAVED_X, centerX) : centerX;
|
popupLayoutParams.y = sharedPreferences.getInt(POPUP_SAVED_Y, centerY);
|
||||||
popupLayoutParams.y = popupRememberSizeAndPos
|
|
||||||
? sharedPreferences.getInt(POPUP_SAVED_Y, centerY) : centerY;
|
|
||||||
|
|
||||||
checkPopupPositionBounds();
|
checkPopupPositionBounds();
|
||||||
|
|
||||||
|
@ -2203,6 +2199,10 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
return popupLayoutParams;
|
return popupLayoutParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MainPlayer.PlayerType getPlayerType() {
|
||||||
|
return playerType;
|
||||||
|
}
|
||||||
|
|
||||||
public float getScreenWidth() {
|
public float getScreenWidth() {
|
||||||
return screenWidth;
|
return screenWidth;
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,14 @@ public class CustomBottomSheetBehavior extends BottomSheetBehavior<FrameLayout>
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The interception listens for the child view with the id "fragment_player_holder",
|
||||||
|
// so the following two-finger gesture will be triggered only for the player view on
|
||||||
|
// portrait and for the top controls (visible) on landscape.
|
||||||
|
setSkipCollapsed(event.getPointerCount() == 2);
|
||||||
|
if (event.getPointerCount() == 2) {
|
||||||
|
return super.onInterceptTouchEvent(parent, child, event);
|
||||||
|
}
|
||||||
|
|
||||||
// Don't need to do anything if bottomSheet isn't expanded
|
// Don't need to do anything if bottomSheet isn't expanded
|
||||||
if (getState() == BottomSheetBehavior.STATE_EXPANDED
|
if (getState() == BottomSheetBehavior.STATE_EXPANDED
|
||||||
&& event.getAction() == MotionEvent.ACTION_DOWN) {
|
&& event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||||
|
|
|
@ -12,6 +12,7 @@ import android.os.Build;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||||
import com.google.android.exoplayer2.analytics.AnalyticsListener;
|
import com.google.android.exoplayer2.analytics.AnalyticsListener;
|
||||||
|
@ -39,7 +40,7 @@ public class AudioReactor implements AudioManager.OnAudioFocusChangeListener, An
|
||||||
@NonNull final SimpleExoPlayer player) {
|
@NonNull final SimpleExoPlayer player) {
|
||||||
this.player = player;
|
this.player = player;
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
this.audioManager = ContextCompat.getSystemService(context, AudioManager.class);
|
||||||
player.addAnalyticsListener(this);
|
player.addAnalyticsListener(this);
|
||||||
|
|
||||||
if (SHOULD_BUILD_FOCUS_REQUEST) {
|
if (SHOULD_BUILD_FOCUS_REQUEST) {
|
||||||
|
|
|
@ -5,8 +5,7 @@ import android.net.wifi.WifiManager;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import static android.content.Context.POWER_SERVICE;
|
import androidx.core.content.ContextCompat;
|
||||||
import static android.content.Context.WIFI_SERVICE;
|
|
||||||
|
|
||||||
public class LockManager {
|
public class LockManager {
|
||||||
private final String TAG = "LockManager@" + hashCode();
|
private final String TAG = "LockManager@" + hashCode();
|
||||||
|
@ -18,10 +17,9 @@ public class LockManager {
|
||||||
private WifiManager.WifiLock wifiLock;
|
private WifiManager.WifiLock wifiLock;
|
||||||
|
|
||||||
public LockManager(final Context context) {
|
public LockManager(final Context context) {
|
||||||
powerManager = ((PowerManager) context.getApplicationContext()
|
powerManager = ContextCompat.getSystemService(context.getApplicationContext(),
|
||||||
.getSystemService(POWER_SERVICE));
|
PowerManager.class);
|
||||||
wifiManager = ((WifiManager) context.getApplicationContext()
|
wifiManager = ContextCompat.getSystemService(context, WifiManager.class);
|
||||||
.getSystemService(WIFI_SERVICE));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void acquireWifiAndCpu() {
|
public void acquireWifiAndCpu() {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import android.view.accessibility.CaptioningManager;
|
||||||
import androidx.annotation.IntDef;
|
import androidx.annotation.IntDef;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
import com.google.android.exoplayer2.SeekParameters;
|
import com.google.android.exoplayer2.SeekParameters;
|
||||||
|
@ -209,10 +210,6 @@ public final class PlayerHelper {
|
||||||
return isBrightnessGestureEnabled(context, true);
|
return isBrightnessGestureEnabled(context, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isRememberingPopupDimensions(@NonNull final Context context) {
|
|
||||||
return isRememberingPopupDimensions(context, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isAutoQueueEnabled(@NonNull final Context context) {
|
public static boolean isAutoQueueEnabled(@NonNull final Context context) {
|
||||||
return isAutoQueueEnabled(context, false);
|
return isAutoQueueEnabled(context, false);
|
||||||
}
|
}
|
||||||
|
@ -316,8 +313,8 @@ public final class PlayerHelper {
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public static CaptionStyleCompat getCaptionStyle(@NonNull final Context context) {
|
public static CaptionStyleCompat getCaptionStyle(@NonNull final Context context) {
|
||||||
final CaptioningManager captioningManager = (CaptioningManager)
|
final CaptioningManager captioningManager = ContextCompat.getSystemService(context,
|
||||||
context.getSystemService(Context.CAPTIONING_SERVICE);
|
CaptioningManager.class);
|
||||||
if (captioningManager == null || !captioningManager.isEnabled()) {
|
if (captioningManager == null || !captioningManager.isEnabled()) {
|
||||||
return CaptionStyleCompat.DEFAULT;
|
return CaptionStyleCompat.DEFAULT;
|
||||||
}
|
}
|
||||||
|
@ -340,8 +337,8 @@ public final class PlayerHelper {
|
||||||
* @return caption scaling
|
* @return caption scaling
|
||||||
*/
|
*/
|
||||||
public static float getCaptionScale(@NonNull final Context context) {
|
public static float getCaptionScale(@NonNull final Context context) {
|
||||||
final CaptioningManager captioningManager
|
final CaptioningManager captioningManager = ContextCompat.getSystemService(context,
|
||||||
= (CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE);
|
CaptioningManager.class);
|
||||||
if (captioningManager == null || !captioningManager.isEnabled()) {
|
if (captioningManager == null || !captioningManager.isEnabled()) {
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
|
@ -393,12 +390,6 @@ public final class PlayerHelper {
|
||||||
.getBoolean(context.getString(R.string.brightness_gesture_control_key), b);
|
.getBoolean(context.getString(R.string.brightness_gesture_control_key), b);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isRememberingPopupDimensions(@NonNull final Context context,
|
|
||||||
final boolean b) {
|
|
||||||
return getPreferences(context)
|
|
||||||
.getBoolean(context.getString(R.string.popup_remember_size_pos_key), b);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isUsingInexactSeek(@NonNull final Context context) {
|
private static boolean isUsingInexactSeek(@NonNull final Context context) {
|
||||||
return getPreferences(context)
|
return getPreferences(context)
|
||||||
.getBoolean(context.getString(R.string.use_inexact_seek_key), false);
|
.getBoolean(context.getString(R.string.use_inexact_seek_key), false);
|
||||||
|
|
|
@ -6,8 +6,12 @@ import android.content.Intent;
|
||||||
import android.content.ServiceConnection;
|
import android.content.ServiceConnection;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||||
import com.google.android.exoplayer2.PlaybackParameters;
|
import com.google.android.exoplayer2.PlaybackParameters;
|
||||||
|
|
||||||
import org.schabi.newpipe.App;
|
import org.schabi.newpipe.App;
|
||||||
import org.schabi.newpipe.MainActivity;
|
import org.schabi.newpipe.MainActivity;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
|
@ -31,6 +35,20 @@ public final class PlayerHolder {
|
||||||
private static MainPlayer playerService;
|
private static MainPlayer playerService;
|
||||||
private static VideoPlayerImpl player;
|
private static VideoPlayerImpl player;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current {@link MainPlayer.PlayerType} of the {@link MainPlayer} service,
|
||||||
|
* otherwise `null` if no service running.
|
||||||
|
*
|
||||||
|
* @return Current PlayerType
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static MainPlayer.PlayerType getType() {
|
||||||
|
if (player == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return player.getPlayerType();
|
||||||
|
}
|
||||||
|
|
||||||
public static void setListener(final PlayerServiceExtendedEventListener newListener) {
|
public static void setListener(final PlayerServiceExtendedEventListener newListener) {
|
||||||
listener = newListener;
|
listener = newListener;
|
||||||
// Force reload data from service
|
// Force reload data from service
|
||||||
|
|
|
@ -52,7 +52,6 @@ public class PlayQueueItemBuilder {
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
holder.itemThumbnailView.setOnTouchListener(getOnTouchListener(holder));
|
|
||||||
holder.itemHandle.setOnTouchListener(getOnTouchListener(holder));
|
holder.itemHandle.setOnTouchListener(getOnTouchListener(holder));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@ import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import androidx.preference.PreferenceManager;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
@ -16,6 +15,7 @@ import android.widget.Toast;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
import com.nononsenseapps.filepicker.Utils;
|
import com.nononsenseapps.filepicker.Utils;
|
||||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||||
|
@ -23,6 +23,7 @@ import com.nostra13.universalimageloader.core.ImageLoader;
|
||||||
import org.schabi.newpipe.DownloaderImpl;
|
import org.schabi.newpipe.DownloaderImpl;
|
||||||
import org.schabi.newpipe.NewPipeDatabase;
|
import org.schabi.newpipe.NewPipeDatabase;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.ReCaptchaActivity;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.extractor.localization.ContentCountry;
|
import org.schabi.newpipe.extractor.localization.ContentCountry;
|
||||||
import org.schabi.newpipe.extractor.localization.Localization;
|
import org.schabi.newpipe.extractor.localization.Localization;
|
||||||
|
@ -78,6 +79,22 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
|
||||||
.getPreferredContentCountry(requireContext());
|
.getPreferredContentCountry(requireContext());
|
||||||
initialLanguage = PreferenceManager
|
initialLanguage = PreferenceManager
|
||||||
.getDefaultSharedPreferences(requireContext()).getString("app_language_key", "en");
|
.getDefaultSharedPreferences(requireContext()).getString("app_language_key", "en");
|
||||||
|
|
||||||
|
final Preference clearCookiePref = findPreference(getString(R.string.clear_cookie_key));
|
||||||
|
|
||||||
|
clearCookiePref.setOnPreferenceClickListener(preference -> {
|
||||||
|
defaultPreferences.edit()
|
||||||
|
.putString(getString(R.string.recaptcha_cookies_key), "").apply();
|
||||||
|
DownloaderImpl.getInstance().setCookie(ReCaptchaActivity.RECAPTCHA_COOKIES_KEY, "");
|
||||||
|
Toast.makeText(getActivity(), R.string.recaptcha_cookies_cleared,
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
clearCookiePref.setVisible(false);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (defaultPreferences.getString(getString(R.string.recaptcha_cookies_key), "").isEmpty()) {
|
||||||
|
clearCookiePref.setVisible(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
package org.schabi.newpipe.settings;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
|
||||||
|
|
||||||
public class DebugSettingsFragment extends BasePreferenceFragment {
|
|
||||||
@Override
|
|
||||||
public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) {
|
|
||||||
addPreferencesFromResource(R.xml.debug_settings);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -55,7 +55,7 @@ public class HistorySettingsFragment extends BasePreferenceFragment {
|
||||||
.setNegativeButton(R.string.cancel, ((dialog, which) -> dialog.dismiss()))
|
.setNegativeButton(R.string.cancel, ((dialog, which) -> dialog.dismiss()))
|
||||||
.setPositiveButton(R.string.delete, ((dialog, which) -> {
|
.setPositiveButton(R.string.delete, ((dialog, which) -> {
|
||||||
final Disposable onDeletePlaybackStates
|
final Disposable onDeletePlaybackStates
|
||||||
= recordManager.deleteCompelteStreamStateHistory()
|
= recordManager.deleteCompleteStreamStateHistory()
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(
|
.subscribe(
|
||||||
howManyDeleted -> Toast.makeText(getActivity(),
|
howManyDeleted -> Toast.makeText(getActivity(),
|
||||||
|
@ -113,7 +113,7 @@ public class HistorySettingsFragment extends BasePreferenceFragment {
|
||||||
.setPositiveButton(R.string.delete, ((dialog, which) -> {
|
.setPositiveButton(R.string.delete, ((dialog, which) -> {
|
||||||
|
|
||||||
final Disposable onDeletePlaybackStates
|
final Disposable onDeletePlaybackStates
|
||||||
= recordManager.deleteCompelteStreamStateHistory()
|
= recordManager.deleteCompleteStreamStateHistory()
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(
|
.subscribe(
|
||||||
howManyDeleted -> Toast.makeText(getActivity(),
|
howManyDeleted -> Toast.makeText(getActivity(),
|
||||||
|
|
|
@ -21,10 +21,5 @@ public class MainSettingsFragment extends BasePreferenceFragment {
|
||||||
|
|
||||||
defaultPreferences.edit().putBoolean(getString(R.string.update_app_key), false).apply();
|
defaultPreferences.edit().putBoolean(getString(R.string.update_app_key), false).apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!DEBUG) {
|
|
||||||
final Preference debug = findPreference(getString(R.string.debug_pref_screen_key));
|
|
||||||
getPreferenceScreen().removePreference(debug);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,9 @@ import android.os.Build;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import org.schabi.newpipe.App;
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
import static android.content.Context.BATTERY_SERVICE;
|
import org.schabi.newpipe.App;
|
||||||
import static android.content.Context.UI_MODE_SERVICE;
|
|
||||||
|
|
||||||
public final class DeviceUtils {
|
public final class DeviceUtils {
|
||||||
|
|
||||||
|
@ -30,15 +29,14 @@ public final class DeviceUtils {
|
||||||
final PackageManager pm = App.getApp().getPackageManager();
|
final PackageManager pm = App.getApp().getPackageManager();
|
||||||
|
|
||||||
// from doc: https://developer.android.com/training/tv/start/hardware.html#runtime-check
|
// from doc: https://developer.android.com/training/tv/start/hardware.html#runtime-check
|
||||||
boolean isTv = ((UiModeManager) context.getSystemService(UI_MODE_SERVICE))
|
boolean isTv = ContextCompat.getSystemService(context, UiModeManager.class)
|
||||||
.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION
|
.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION
|
||||||
|| pm.hasSystemFeature(AMAZON_FEATURE_FIRE_TV)
|
|| pm.hasSystemFeature(AMAZON_FEATURE_FIRE_TV)
|
||||||
|| pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION);
|
|| pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION);
|
||||||
|
|
||||||
// from https://stackoverflow.com/a/58932366
|
// from https://stackoverflow.com/a/58932366
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
final boolean isBatteryAbsent
|
final boolean isBatteryAbsent = context.getSystemService(BatteryManager.class)
|
||||||
= ((BatteryManager) context.getSystemService(BATTERY_SERVICE))
|
|
||||||
.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY) == 0;
|
.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY) == 0;
|
||||||
isTv = isTv || (isBatteryAbsent
|
isTv = isTv || (isBatteryAbsent
|
||||||
&& !pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)
|
&& !pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)
|
||||||
|
|
|
@ -3,6 +3,8 @@ package org.schabi.newpipe.util;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
|
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
@ -543,7 +545,7 @@ public final class ListHelper {
|
||||||
*/
|
*/
|
||||||
public static boolean isMeteredNetwork(final Context context) {
|
public static boolean isMeteredNetwork(final Context context) {
|
||||||
final ConnectivityManager manager
|
final ConnectivityManager manager
|
||||||
= (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
= ContextCompat.getSystemService(context, ConnectivityManager.class);
|
||||||
if (manager == null || manager.getActiveNetworkInfo() == null) {
|
if (manager == null || manager.getActiveNetworkInfo() == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,13 +5,15 @@ import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import androidx.preference.PreferenceManager;
|
import android.icu.text.CompactDecimalFormat;
|
||||||
|
import android.os.Build;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.PluralsRes;
|
import androidx.annotation.PluralsRes;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
import org.ocpsoft.prettytime.PrettyTime;
|
import org.ocpsoft.prettytime.PrettyTime;
|
||||||
import org.ocpsoft.prettytime.units.Decade;
|
import org.ocpsoft.prettytime.units.Decade;
|
||||||
|
@ -184,6 +186,11 @@ public final class Localization {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String shortCount(final Context context, final long count) {
|
public static String shortCount(final Context context, final long count) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 24) {
|
||||||
|
return CompactDecimalFormat.getInstance(getAppLocale(context),
|
||||||
|
CompactDecimalFormat.CompactStyle.SHORT).format(count);
|
||||||
|
}
|
||||||
|
|
||||||
final double value = (double) count;
|
final double value = (double) count;
|
||||||
if (count >= 1000000000) {
|
if (count >= 1000000000) {
|
||||||
return localizeNumber(context, round(value / 1000000000, 1))
|
return localizeNumber(context, round(value / 1000000000, 1))
|
||||||
|
|
|
@ -7,17 +7,18 @@ import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import androidx.preference.PreferenceManager;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import androidx.fragment.app.FragmentTransaction;
|
import androidx.fragment.app.FragmentTransaction;
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||||
|
|
||||||
|
@ -174,7 +175,7 @@ public final class NavigationHelper {
|
||||||
Toast.makeText(context, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
|
Toast.makeText(context, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
|
||||||
final Intent intent = getPlayerIntent(context, MainPlayer.class, queue, resumePlayback);
|
final Intent intent = getPlayerIntent(context, MainPlayer.class, queue, resumePlayback);
|
||||||
intent.putExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_POPUP);
|
intent.putExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_POPUP);
|
||||||
startService(context, intent);
|
ContextCompat.startForegroundService(context, intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void playOnBackgroundPlayer(final Context context,
|
public static void playOnBackgroundPlayer(final Context context,
|
||||||
|
@ -184,7 +185,24 @@ public final class NavigationHelper {
|
||||||
.show();
|
.show();
|
||||||
final Intent intent = getPlayerIntent(context, MainPlayer.class, queue, resumePlayback);
|
final Intent intent = getPlayerIntent(context, MainPlayer.class, queue, resumePlayback);
|
||||||
intent.putExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_AUDIO);
|
intent.putExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_AUDIO);
|
||||||
startService(context, intent);
|
ContextCompat.startForegroundService(context, intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void enqueueOnVideoPlayer(final Context context, final PlayQueue queue,
|
||||||
|
final boolean resumePlayback) {
|
||||||
|
enqueueOnVideoPlayer(context, queue, false, resumePlayback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void enqueueOnVideoPlayer(final Context context, final PlayQueue queue,
|
||||||
|
final boolean selectOnAppend,
|
||||||
|
final boolean resumePlayback) {
|
||||||
|
|
||||||
|
Toast.makeText(context, R.string.enqueued, Toast.LENGTH_SHORT).show();
|
||||||
|
final Intent intent = getPlayerEnqueueIntent(
|
||||||
|
context, MainPlayer.class, queue, selectOnAppend, resumePlayback);
|
||||||
|
|
||||||
|
intent.putExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_VIDEO);
|
||||||
|
ContextCompat.startForegroundService(context, intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void enqueueOnPopupPlayer(final Context context, final PlayQueue queue,
|
public static void enqueueOnPopupPlayer(final Context context, final PlayQueue queue,
|
||||||
|
@ -200,11 +218,11 @@ public final class NavigationHelper {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Toast.makeText(context, R.string.popup_playing_append, Toast.LENGTH_SHORT).show();
|
Toast.makeText(context, R.string.enqueued, Toast.LENGTH_SHORT).show();
|
||||||
final Intent intent = getPlayerEnqueueIntent(
|
final Intent intent = getPlayerEnqueueIntent(
|
||||||
context, MainPlayer.class, queue, selectOnAppend, resumePlayback);
|
context, MainPlayer.class, queue, selectOnAppend, resumePlayback);
|
||||||
intent.putExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_POPUP);
|
intent.putExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_POPUP);
|
||||||
startService(context, intent);
|
ContextCompat.startForegroundService(context, intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void enqueueOnBackgroundPlayer(final Context context, final PlayQueue queue,
|
public static void enqueueOnBackgroundPlayer(final Context context, final PlayQueue queue,
|
||||||
|
@ -216,19 +234,11 @@ public final class NavigationHelper {
|
||||||
final PlayQueue queue,
|
final PlayQueue queue,
|
||||||
final boolean selectOnAppend,
|
final boolean selectOnAppend,
|
||||||
final boolean resumePlayback) {
|
final boolean resumePlayback) {
|
||||||
Toast.makeText(context, R.string.background_player_append, Toast.LENGTH_SHORT).show();
|
Toast.makeText(context, R.string.enqueued, Toast.LENGTH_SHORT).show();
|
||||||
final Intent intent = getPlayerEnqueueIntent(
|
final Intent intent = getPlayerEnqueueIntent(
|
||||||
context, MainPlayer.class, queue, selectOnAppend, resumePlayback);
|
context, MainPlayer.class, queue, selectOnAppend, resumePlayback);
|
||||||
intent.putExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_AUDIO);
|
intent.putExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_AUDIO);
|
||||||
startService(context, intent);
|
ContextCompat.startForegroundService(context, intent);
|
||||||
}
|
|
||||||
|
|
||||||
public static void startService(@NonNull final Context context, @NonNull final Intent intent) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
context.startForegroundService(intent);
|
|
||||||
} else {
|
|
||||||
context.startService(intent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -396,7 +406,7 @@ public final class NavigationHelper {
|
||||||
defaultTransaction(fragmentManager)
|
defaultTransaction(fragmentManager)
|
||||||
.replace(R.id.fragment_player_holder, instance)
|
.replace(R.id.fragment_player_holder, instance)
|
||||||
.runOnCommit(() -> sendPlayerStartedEvent(instance.requireActivity()))
|
.runOnCommit(() -> sendPlayerStartedEvent(instance.requireActivity()))
|
||||||
.commit();
|
.commitAllowingStateLoss();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void openChannelFragment(final FragmentManager fragmentManager,
|
public static void openChannelFragment(final FragmentManager fragmentManager,
|
||||||
|
|
|
@ -9,6 +9,8 @@ import android.content.pm.ResolveInfo;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
|
|
||||||
public final class ShareUtils {
|
public final class ShareUtils {
|
||||||
|
@ -95,7 +97,7 @@ public final class ShareUtils {
|
||||||
*/
|
*/
|
||||||
public static void copyToClipboard(final Context context, final String text) {
|
public static void copyToClipboard(final Context context, final String text) {
|
||||||
final ClipboardManager clipboardManager =
|
final ClipboardManager clipboardManager =
|
||||||
(ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
ContextCompat.getSystemService(context, ClipboardManager.class);
|
||||||
|
|
||||||
if (clipboardManager == null) {
|
if (clipboardManager == null) {
|
||||||
Toast.makeText(context,
|
Toast.makeText(context,
|
||||||
|
|
|
@ -7,22 +7,41 @@ import androidx.fragment.app.Fragment;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||||
import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
|
import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
|
||||||
|
import org.schabi.newpipe.local.dialog.PlaylistCreationDialog;
|
||||||
|
import org.schabi.newpipe.player.MainPlayer;
|
||||||
|
import org.schabi.newpipe.player.helper.PlayerHolder;
|
||||||
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.schabi.newpipe.player.MainPlayer.PlayerType.AUDIO;
|
||||||
|
import static org.schabi.newpipe.player.MainPlayer.PlayerType.POPUP;
|
||||||
|
|
||||||
public enum StreamDialogEntry {
|
public enum StreamDialogEntry {
|
||||||
//////////////////////////////////////
|
//////////////////////////////////////
|
||||||
// enum values with DEFAULT actions //
|
// enum values with DEFAULT actions //
|
||||||
//////////////////////////////////////
|
//////////////////////////////////////
|
||||||
|
|
||||||
enqueue_on_background(R.string.enqueue_on_background, (fragment, item) ->
|
/**
|
||||||
NavigationHelper.enqueueOnBackgroundPlayer(fragment.getContext(),
|
* Enqueues the stream automatically to the current PlayerType.<br>
|
||||||
new SinglePlayQueue(item), false)),
|
* <br>
|
||||||
|
* Info: Add this entry within showStreamDialog.
|
||||||
|
*/
|
||||||
|
enqueue(R.string.enqueue_stream, (fragment, item) -> {
|
||||||
|
final MainPlayer.PlayerType type = PlayerHolder.getType();
|
||||||
|
|
||||||
enqueue_on_popup(R.string.enqueue_on_popup, (fragment, item) ->
|
if (type == AUDIO) {
|
||||||
|
NavigationHelper.enqueueOnBackgroundPlayer(fragment.getContext(),
|
||||||
|
new SinglePlayQueue(item), false);
|
||||||
|
} else if (type == POPUP) {
|
||||||
NavigationHelper.enqueueOnPopupPlayer(fragment.getContext(),
|
NavigationHelper.enqueueOnPopupPlayer(fragment.getContext(),
|
||||||
new SinglePlayQueue(item), false)),
|
new SinglePlayQueue(item), false);
|
||||||
|
} else /* type == VIDEO */ {
|
||||||
|
NavigationHelper.enqueueOnVideoPlayer(fragment.getContext(),
|
||||||
|
new SinglePlayQueue(item), false);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
start_here_on_background(R.string.start_here_on_background, (fragment, item) ->
|
start_here_on_background(R.string.start_here_on_background, (fragment, item) ->
|
||||||
NavigationHelper.playOnBackgroundPlayer(fragment.getContext(),
|
NavigationHelper.playOnBackgroundPlayer(fragment.getContext(),
|
||||||
|
@ -40,8 +59,14 @@ public enum StreamDialogEntry {
|
||||||
|
|
||||||
append_playlist(R.string.append_playlist, (fragment, item) -> {
|
append_playlist(R.string.append_playlist, (fragment, item) -> {
|
||||||
if (fragment.getFragmentManager() != null) {
|
if (fragment.getFragmentManager() != null) {
|
||||||
PlaylistAppendDialog.fromStreamInfoItems(Collections.singletonList(item))
|
final PlaylistAppendDialog d = PlaylistAppendDialog
|
||||||
.show(fragment.getFragmentManager(), "StreamDialogEntry@append_playlist");
|
.fromStreamInfoItems(Collections.singletonList(item));
|
||||||
|
|
||||||
|
PlaylistAppendDialog.onPlaylistFound(fragment.getContext(),
|
||||||
|
() -> d.show(fragment.getFragmentManager(), "StreamDialogEntry@append_playlist"),
|
||||||
|
() -> PlaylistCreationDialog.newInstance(d)
|
||||||
|
.show(fragment.getFragmentManager(), "StreamDialogEntry@create_playlist")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
@ -69,6 +94,10 @@ public enum StreamDialogEntry {
|
||||||
// non-static methods to initialize and edit entries //
|
// non-static methods to initialize and edit entries //
|
||||||
///////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public static void setEnabledEntries(final List<StreamDialogEntry> entries) {
|
||||||
|
setEnabledEntries(entries.toArray(new StreamDialogEntry[0]));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To be called before using {@link #setCustomAction(StreamDialogEntryAction)}.
|
* To be called before using {@link #setCustomAction(StreamDialogEntryAction)}.
|
||||||
*
|
*
|
||||||
|
|
|
@ -24,6 +24,8 @@ import android.os.Handler.Callback;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
|
@ -157,8 +159,10 @@ public class DownloadManagerService extends Service {
|
||||||
|
|
||||||
mNotification = builder.build();
|
mNotification = builder.build();
|
||||||
|
|
||||||
mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
mNotificationManager = ContextCompat.getSystemService(this,
|
||||||
mConnectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
|
NotificationManager.class);
|
||||||
|
mConnectivityManager = ContextCompat.getSystemService(this,
|
||||||
|
ConnectivityManager.class);
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
mNetworkStateListenerL = new ConnectivityManager.NetworkCallback() {
|
mNetworkStateListenerL = new ConnectivityManager.NetworkCallback() {
|
||||||
|
|
|
@ -29,6 +29,7 @@ import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.core.content.FileProvider;
|
import androidx.core.content.FileProvider;
|
||||||
import androidx.core.view.ViewCompat;
|
import androidx.core.view.ViewCompat;
|
||||||
import androidx.recyclerview.widget.DiffUtil;
|
import androidx.recyclerview.widget.DiffUtil;
|
||||||
|
@ -120,7 +121,7 @@ public class MissionAdapter extends Adapter<ViewHolder> implements Handler.Callb
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mDownloadManager = downloadManager;
|
mDownloadManager = downloadManager;
|
||||||
|
|
||||||
mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
mInflater = ContextCompat.getSystemService(mContext, LayoutInflater.class);
|
||||||
mLayout = R.layout.mission_item;
|
mLayout = R.layout.mission_item;
|
||||||
|
|
||||||
mHandler = new Handler(context.getMainLooper());
|
mHandler = new Handler(context.getMainLooper());
|
||||||
|
|
|
@ -201,7 +201,7 @@ public class Utility {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void copyToClipboard(Context context, String str) {
|
public static void copyToClipboard(Context context, String str) {
|
||||||
ClipboardManager cm = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
ClipboardManager cm = ContextCompat.getSystemService(context, ClipboardManager.class);
|
||||||
|
|
||||||
if (cm == null) {
|
if (cm == null) {
|
||||||
Toast.makeText(context, R.string.permission_denied, Toast.LENGTH_LONG).show();
|
Toast.makeText(context, R.string.permission_denied, Toast.LENGTH_LONG).show();
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:interpolator="@android:interpolator/decelerate_quint">
|
android:interpolator="@android:interpolator/decelerate_quint">
|
||||||
|
|
||||||
<alpha
|
<alpha
|
||||||
android:duration="150"
|
android:duration="150"
|
||||||
android:fromAlpha="0.00"
|
android:fromAlpha="0.00"
|
||||||
android:toAlpha="1.0"/>
|
android:toAlpha="1.0" />
|
||||||
</set>
|
</set>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:interpolator="@android:interpolator/accelerate_quint">
|
android:interpolator="@android:interpolator/accelerate_quint">
|
||||||
|
|
||||||
<alpha
|
<alpha
|
||||||
android:duration="350"
|
android:duration="350"
|
||||||
android:fromAlpha="1.0"
|
android:fromAlpha="1.0"
|
||||||
android:toAlpha="0.00"/>
|
android:toAlpha="0.00" />
|
||||||
</set>
|
</set>
|
||||||
|
|
|
@ -5,5 +5,5 @@
|
||||||
android:interpolator="@android:interpolator/accelerate_decelerate"
|
android:interpolator="@android:interpolator/accelerate_decelerate"
|
||||||
android:propertyName="alpha"
|
android:propertyName="alpha"
|
||||||
android:valueFrom="0.0f"
|
android:valueFrom="0.0f"
|
||||||
android:valueTo="1.0f"/>
|
android:valueTo="1.0f" />
|
||||||
</set>
|
</set>
|
||||||
|
|
|
@ -5,5 +5,5 @@
|
||||||
android:interpolator="@android:interpolator/accelerate_decelerate"
|
android:interpolator="@android:interpolator/accelerate_decelerate"
|
||||||
android:propertyName="alpha"
|
android:propertyName="alpha"
|
||||||
android:valueFrom="1.0f"
|
android:valueFrom="1.0f"
|
||||||
android:valueTo="0.0f"/>
|
android:valueTo="0.0f" />
|
||||||
</set>
|
</set>
|
||||||
|
|
Before Width: | Height: | Size: 415 B After Width: | Height: | Size: 358 B |
Before Width: | Height: | Size: 302 B After Width: | Height: | Size: 262 B |
Before Width: | Height: | Size: 570 B After Width: | Height: | Size: 480 B |
Before Width: | Height: | Size: 680 B After Width: | Height: | Size: 581 B |
Before Width: | Height: | Size: 621 B After Width: | Height: | Size: 511 B |
Before Width: | Height: | Size: 285 B After Width: | Height: | Size: 244 B |
Before Width: | Height: | Size: 246 B After Width: | Height: | Size: 223 B |
Before Width: | Height: | Size: 328 B After Width: | Height: | Size: 290 B |
Before Width: | Height: | Size: 401 B After Width: | Height: | Size: 345 B |
Before Width: | Height: | Size: 411 B After Width: | Height: | Size: 345 B |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 188 B After Width: | Height: | Size: 124 B |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 8.7 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 9.1 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 602 B After Width: | Height: | Size: 516 B |
Before Width: | Height: | Size: 413 B After Width: | Height: | Size: 362 B |
Before Width: | Height: | Size: 628 B After Width: | Height: | Size: 501 B |
Before Width: | Height: | Size: 734 B After Width: | Height: | Size: 609 B |
Before Width: | Height: | Size: 837 B After Width: | Height: | Size: 693 B |
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 873 B |
Before Width: | Height: | Size: 614 B After Width: | Height: | Size: 509 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 912 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 777 B After Width: | Height: | Size: 687 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 901 B |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 1.6 KiB |
|
@ -2,4 +2,4 @@
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:shape="oval">
|
android:shape="oval">
|
||||||
<solid android:color="#64000000" />
|
<solid android:color="#64000000" />
|
||||||
</shape>
|
</shape>
|
||||||
|
|
|
@ -2,16 +2,16 @@
|
||||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item android:id="@android:id/background">
|
<item android:id="@android:id/background">
|
||||||
<shape>
|
<shape>
|
||||||
<solid android:color="@color/gray"/>
|
<solid android:color="@color/gray" />
|
||||||
</shape>
|
</shape>
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item android:id="@android:id/progress">
|
<item android:id="@android:id/progress">
|
||||||
<clip>
|
<clip>
|
||||||
<shape>
|
<shape>
|
||||||
<solid android:color="@color/middle_gray"/>
|
<solid android:color="@color/middle_gray" />
|
||||||
</shape>
|
</shape>
|
||||||
</clip>
|
</clip>
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
</layer-list>
|
</layer-list>
|
||||||
|
|
|
@ -3,6 +3,6 @@
|
||||||
<stroke
|
<stroke
|
||||||
android:width="1dp"
|
android:width="1dp"
|
||||||
android:color="@color/black_border_color"
|
android:color="@color/black_border_color"
|
||||||
android:dashGap="4dp"
|
android:dashWidth="4dp"
|
||||||
android:dashWidth="4dp"/>
|
android:dashGap="4dp" />
|
||||||
</shape>
|
</shape>
|
||||||
|
|
|
@ -3,6 +3,6 @@
|
||||||
<stroke
|
<stroke
|
||||||
android:width="1dp"
|
android:width="1dp"
|
||||||
android:color="@color/dark_border_color"
|
android:color="@color/dark_border_color"
|
||||||
android:dashGap="4dp"
|
android:dashWidth="4dp"
|
||||||
android:dashWidth="4dp"/>
|
android:dashGap="4dp" />
|
||||||
</shape>
|
</shape>
|
||||||
|
|
|
@ -3,6 +3,6 @@
|
||||||
<stroke
|
<stroke
|
||||||
android:width="1dp"
|
android:width="1dp"
|
||||||
android:color="@color/light_border_color"
|
android:color="@color/light_border_color"
|
||||||
android:dashGap="4dp"
|
android:dashWidth="4dp"
|
||||||
android:dashWidth="4dp"/>
|
android:dashGap="4dp" />
|
||||||
</shape>
|
</shape>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
android:shape="ring"
|
android:shape="ring"
|
||||||
android:thickness="4dp"
|
android:thickness="4dp"
|
||||||
android:useLevel="false">
|
android:useLevel="false">
|
||||||
<solid android:color="@android:color/darker_gray"/>
|
<solid android:color="@android:color/darker_gray" />
|
||||||
</shape>
|
</shape>
|
||||||
</item>
|
</item>
|
||||||
</layer-list>
|
</layer-list>
|
||||||
|
|