diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dbc1c05a5b5..f9201f94851 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,20 +7,20 @@ assignees: '' --- -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. ---> + - +### Checklist + -### Version - -- +- [x] I am using the latest version - x.xx.x +- [ ] I checked, but didn't find any duplicates (open OR closed) of this issue in the repo. +- [ ] 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 + + +### Actual behaviour + + + + ### Expected behavior -### Actual behaviour - + ### Screenshots/Screen recordings + + + + ### Logs - + + + + + + +### Device info + + - Android version/Custom ROM version: + - Device model: diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000000..3ba13e0cec6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 90134a204c2..c4d378d14c1 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -5,35 +5,42 @@ labels: enhancement assignees: '' --- - + + + + + +### Checklist + + +- [x] I checked, but didn't find any duplicates (open OR closed) of this issue in the repo. +- [ ] 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. - #### Describe the feature you want - - + #### Is your feature request related to a problem? Please describe it - - + #### Additional context - + #### How will you/everyone benefit from this feature? - diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index f12eb2fe81b..c3022d93f49 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,28 +1,28 @@ - + #### What is it? -- [ ] Bug fix (user facing) +- [ ] Bugfix (user facing) - [ ] Feature (user facing) -- [ ] Code base improvement (dev facing) +- [ ] Codebase improvement (dev facing) - [ ] Meta improvement to the project (dev facing) #### Description of the changes in your PR - + - record videos - create clones - take over the world #### Fixes the following issue(s) - + - #### Relies on the following changes - -#### Testing apk - +#### APK testing + debug.zip -#### Agreement -- [ ] I carefully read the [contribution guidelines](https://github.com/TeamNewPipe/NewPipe/blob/HEAD/.github/CONTRIBUTING.md) and agree to them. +#### Due diligence +- [ ] I read the [contribution guidelines](https://github.com/TeamNewPipe/NewPipe/blob/HEAD/.github/CONTRIBUTING.md). diff --git a/README.ko.md b/README.ko.md new file mode 100644 index 00000000000..bb6bd653b0a --- /dev/null +++ b/README.ko.md @@ -0,0 +1,144 @@ +

+

NewPipe

+

A libre lightweight streaming frontend for Android.

+

+ +

+ + + + + + +

+
+

ScreenshotsDescriptionFeaturesUpdatesContributionDonateLicense

+

WebsiteBlogFAQPress

+
+ +*Read this in other languages: [English](README.md), [한국어](README.ko.md).* + +경고: 이 버전은 베타 버전이므로, 버그가 발생할 수도 있습니다. 만약 버그가 발생하였다면, 우리의 GITHUB 저장소에서 ISSUE를 열람하여 주십시오. + +NEWPIPE 또는 이것의 FORK을 구글 플레이스토어에 올리는 것은 그들의 이용약관을 위반합니다. + +## Screenshots + +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_01.png) +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_02.png) +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_03.png) +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_04.png) +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_05.png) +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_06.png) +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_07.png) +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_08.png) +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_09.png) +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_10.png) +[](fastlane/metadata/android/en-US/images/tenInchScreenshots/shot_11.png) +[](fastlane/metadata/android/en-US/images/tenInchScreenshots/shot_12.png) + +## Description + +NewPipe는 어떤 구글 프레임워크 라이브러리나, 유튜브 API를 사용하지 않습니다. 웹사이트는 단지 필요한 정보를 가져오기 위해 구문 분석 됩니다. 따라서 이 앱은 구글 서비스의 설치 없이 기기에서 사용될 수 있습니다. 또한, 카피레프트 자유 소프트웨어인 NewPipe를 사용하기 위해 유튜브 계정이 필요하지 않습니다. + +### Features + +* 영상 검색 +* 영상의 일반적인 정보 표시 +* 유튜브 영상 보기 +* 유튜브 영상 듣기 +* 팝업 모드 (floating player) +* 영상 공유 +* 영상 다운로드 +* 음성만 다운로드 +* Kodi에서 영상 열람 +* 다음/관련된 영상 표시 +* 특정 언어로 유튜브 검색 +* 연령 제한 컨텐츠 시청/차단 +* 채널에 대한 일반적인 정보 표시 +* 채널 검색 +* 채널에서 영상 시청 +* Orbot/Tor 지원 (아직 직접적이지 않음) +* 1080p/2K/4K 지원 +* 기록 보기 +* 채널 구독 +* 기록 검색 +* 재생목록 검색/시청 +* 추가된 재생목록 시청 +* 영상 추가 +* 지역 재생목록 +* 자막 +* 실시간 방송 지원 +* 댓글 표시 + +### Supported Services + +NewPipe는 여러가지 서비스를 지원합니다. 우리의 [문서](https://teamnewpipe.github.io/documentation/)는 새로운 서비스가 앱과 추출기에 어떻게 추가될 수 있는지에 대한 더 많은 정보를 제공합니다. 만약 새로운 서비스를 추가하고자 한다면, 우리에게 연락해 주시기 바랍니다. 현재 지원되는 서비스: + +* YouTube +* SoundCloud \[beta\] +* media.ccc.de \[beta\] +* PeerTube instances \[beta\] + +## Updates +NewPipe 코드의 변경이 있을 때(기능 추가 또는 버그 수정으로 인해), 결국 릴리즈가 발생할 것입니다. 이것들의 형식은 x.xx.x 입니다. +이 새로운 버전을 얻기 위해서, 당신은: + 1. 직접 디버그 APK를 생성할 수 있습니다. 이 방법은 당신의 기기에서 새로운 기능을 얻을 수 있는 가장 빠른 방법이지만, 꽤 많이 복잡합니다. + 따라서 우리는 다른 방법들 중 하나를 사용하는 것을 추천합니다. + 2. 우리의 커스텀 저장소를 F-Droid에 추가하고 우리가 릴리즈를 게시하는 대로 저곳에서 릴리즈를 설치할 수 있습니다. + 이에 대한 설명서는 이곳에서 확인할 수 있습니다: https://newpipe.schabi.org/FAQ/tutorials/install-add-fdroid-repo/ + 3. 우리가 릴리즈를 게시하는 대로 [Github Releases](https://github.com/TeamNewPipe/NewPipe/releases)에서 APK를 다운받고 이것을 설치할 수 있습니다. + 4. F-Droid를 통해 업데이트 할 수 있습니다. F-Droid는 변화를 인식하고, 스스로 APK를 생성하고, 이것에 서명하고, 사용자들에서 업데이트를 전달해야만 하기 때문에, + 이것은 업데이트를 받는 가장 느린 방법입니다. + +우리는 대부분의 사용자에게 2번쨰 방법을 추천합니다. 방법 2 또는 3을 사용하여 설치된 APK는 서로 호환되지만, 방법 4를 사용하여 설치된 것들과는 호환되지 않습니다. 이것은 방법 2 또는 3에서는 같은 (우리의)서명 키가 사용되지만, 방법 4에서는 다른 (F-Droid의)서명 키가 사용되기 때문입니다. 방법 1을 사용하여 디버그 APK를 생성하는 것에서는 키가 완전히 제외됩니다. 서명 키는 사용자가 앱에 악의적인 업데이트를 설치하는 것에 대해 속지 않도록 보장하는 것을 도와줍니다. + +한편, 만약 어떠한 이유(예. NewPipe의 핵심 기능이 손상되었고 F-Droid가 아직 업데이트를 가지지 않는 경우) 때문에 소스를 바꾸길 원한다면, +우리는 다음과 같은 절차를 따르는 것을 권장합니다: +1. 당신의 기록, 구독, 그리고 재생목록을 유지할 수 있도록 Settings > Content > Export Database 를 통해 데이터를 백업하십시오. +2. NewPipe를 삭제하십시오. +3. 새로운 소스에서 APK를 다운로드하고 이것을 설치하십시오. +4. Step 1의 Settings > Content > Export Database 을 통해 데이터를 불러오십시오. + +## Contribution +당신이 아이디어, 번역, 디자인 변경, 코드 정리, 또는 정말 큰 코드 수정에 대한 의견이 있다면, 도움은 항상 환영합니다. +더 많이 수행될수록 더 많이 발전할 수 있습니다! + +만약 참여하고 싶다면, 우리의 [컨트리뷰션 공지](.github/CONTRIBUTING.md)를 참고하십시오. + + +Translation status + + +## Donate +만약 NewPipe가 마음에 들었다면, 우리는 기부에 대해 기꺼이 환영합니다. bitcoin을 보내거나, Bountysource 또는 Liberapay를 통해 기부할 수 있습니다. NewPipe에 기부하는 것에 대한 자세한 정보를 원한다면, 우리의 [웹사이트](https://newpipe.schabi.org/donate)를 방문하여 주십시오. + + + + + + + + + + + + + + + + + +
BitcoinBitcoin QR code16A9J59ahMRqkLSZjhYj33n9j3fMztFxnh
LiberapayVisit NewPipe at liberapay.comDonate via Liberapay
BountysourceVisit NewPipe at bountysource.comCheck out how many bounties you can earn.
+ +## Privacy Policy + +NewPipe 프로젝트는 미디어 웹 서비스를 사용하는 것에 대한 사적의, 익명의 경험을 제공하는 것을 목표로 하고 있습니다. +그러므로, 앱은 당신의 동의 없이 어떤 데이터도 수집하지 않습니다. NewPipe의 개인정보보호정책은 당신이 충돌 리포트를 보내거나, 또는 우리의 블로그에 글을 남길 때 어떤 데이터가 보내지고 저장되는지에 대해 상세히 설명합니다. 이 문서는 [여기](https://newpipe.schabi.org/legal/privacy/)에서 확인할 수 있습니다. + +## License +[![GNU GPLv3 Image](https://www.gnu.org/graphics/gplv3-127x51.png)](http://www.gnu.org/licenses/gpl-3.0.en.html) + +NewPipe는 자유 소프트웨어입니다: 당신의 마음대로 이것을 사용하고, 연구하고, 공유하고, 개선할 수 있습니다. +구체적으로 당신은 자유 소프트웨어 재단에서 발행되는, 버전 3 또는 (당신의 선택에 따라)이후 버전의, +[GNU General Public License](https://www.gnu.org/licenses/gpl.html) 하에서 이것을 재배포 및/또는 수정할 수 있습니다. diff --git a/README.md b/README.md index 50eb40594ae..f11262da17b 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@

WebsiteBlogFAQPress


+*Read this in other languages: [English](README.md), [한국어](README.ko.md).* + WARNING: THIS IS A BETA VERSION, THEREFORE YOU MAY ENCOUNTER BUGS. IF YOU DO, OPEN AN ISSUE VIA OUR GITHUB REPOSITORY. PUTTING NEWPIPE OR ANY FORK OF IT INTO GOOGLE PLAYSTORE VIOLATES THEIR TERMS OF CONDITIONS. @@ -69,11 +71,6 @@ NewPipe does not use any Google framework libraries, nor the YouTube API. Websit * Livestream support * Show comments -### Coming Features - -* Cast to UPnP and Cast -* … and many more - ### 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: @@ -85,17 +82,18 @@ NewPipe supports multiple services. Our [docs](https://teamnewpipe.github.io/doc ## Updates When a change to the NewPipe code occurs (due to either adding features or bug fixing), eventually a release will occur. These are in the format x.xx.x . In order to get this new version, you can: - * Build a debug APK yourself. This is the fastest way to get new features on your device, but is much more complicated, so we recommend using one of the other methods. - * Download the APK from [releases](https://github.com/TeamNewPipe/NewPipe/releases) and install it. - * Update via F-droid. This is the slowest method of getting updates, as F-Droid must recognize changes, build the APK itself, sign it, then push the update to users. + 1. Build a debug APK yourself. This is the fastest way to get new features on your device, but is much more complicated, so we recommend using one of the other methods. + 2. Add our custom repo to F-Droid and install it from there as soon as we publish a release. The instructions are here: https://newpipe.schabi.org/FAQ/tutorials/install-add-fdroid-repo/ + 3. Download the APK from [Github Releases](https://github.com/TeamNewPipe/NewPipe/releases) and install it as soon as we publish a release. + 4. Update via F-droid. This is the slowest method of getting updates, as F-Droid must recognize changes, build the APK itself, sign it, then push the update to users. -When you install an APK from one of these options, it will be incompatible with an APK from one of the other options. This is due to different signing keys being used. Signing keys help ensure that a user isn't tricked into installing a malicious update to an app, and are independent. F-Droid and GitHub use different signing keys, and building an APK debug excludes a key. The signing key issue is being discussed in issue [#1981](https://github.com/TeamNewPipe/NewPipe/issues/1981), and may be fixed by setting up our own repository on F-Droid. +We recommend method 2 for most users. APKs installed using method 2 or 3 are compatible with each other, but not with those installed using method 4. This is due to the same signing key (ours) being using for 2 and 3, but a different signing key (F-Droid's) being used for 4. Building a debug APK using method 1 excludes a key entirely. Signing keys help ensure that a user isn't tricked into installing a malicious update to an app. In the meanwhile, if you want to switch sources for some reason (e.g. NewPipe's core functionality was broken and F-Droid doesn't have the update yet), we recommend following this procedure: -1. Back up your data via "Settings>Content>Export Database" so you keep your history, subscriptions, and playlists +1. Back up your data via Settings > Content > Export Database so you keep your history, subscriptions, and playlists 2. Uninstall NewPipe 3. Download the APK from the new source and install it -4. Import the data from step 1 via "Settings>Content>Import Database" +4. Import the data from step 1 via Settings > Content > Import Database ## Contribution Whether you have ideas, translations, design changes, code cleaning, or real heavy code changes, help is always welcome. @@ -103,6 +101,10 @@ The more is done the better it gets! If you'd like to get involved, check our [contribution notes](.github/CONTRIBUTING.md). + +Translation status + + ## 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). diff --git a/app/build.gradle b/app/build.gradle index 13f3c1bb608..2050cd8efe5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,8 +13,10 @@ android { resValue "string", "app_name", "NewPipe" minSdkVersion 19 targetSdkVersion 29 - versionCode 956 - versionName "0.20.2" + versionCode 957 + versionName "0.20.3" + + multiDexEnabled true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true @@ -28,7 +30,6 @@ android { buildTypes { debug { - multiDexEnabled true debuggable true // suffix the app id and the app name with git branch name @@ -64,11 +65,18 @@ android { } compileOptions { + // Flag to enable support for the new language APIs + coreLibraryDesugaringEnabled true + sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 encoding 'utf-8' } + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8 + } + // Required and used only by groupie androidExtensions { experimental = true @@ -81,7 +89,7 @@ android { ext { icepickVersion = '3.2.0' - checkstyleVersion = '8.32' + checkstyleVersion = '8.36.2' stethoVersion = '1.5.1' leakCanaryVersion = '2.2' exoPlayerVersion = '2.11.8' @@ -94,7 +102,7 @@ ext { configurations { checkstyle - ktlint +// ktlint } checkstyle { @@ -122,30 +130,32 @@ task runCheckstyle(type: Checkstyle) { } } -task runKtlint(type: JavaExec) { - main = "com.pinterest.ktlint.Main" - classpath = configurations.ktlint - args "src/**/*.kt" -} - -task formatKtlint(type: JavaExec) { - main = "com.pinterest.ktlint.Main" - classpath = configurations.ktlint - args "-F", "src/**/*.kt" -} +//task runKtlint(type: JavaExec) { +// main = "com.pinterest.ktlint.Main" +// classpath = configurations.ktlint +// args "src/**/*.kt" +//} +// +//task formatKtlint(type: JavaExec) { +// main = "com.pinterest.ktlint.Main" +// classpath = configurations.ktlint +// args "-F", "src/**/*.kt" +//} afterEvaluate { - preDebugBuild.dependsOn runCheckstyle, runKtlint + preDebugBuild.dependsOn runCheckstyle //, runKtlint } dependencies { + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10' + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "frankiesardo:icepick:${icepickVersion}" kapt "frankiesardo:icepick-processor:${icepickVersion}" 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-okhttp3:${stethoVersion}" @@ -153,9 +163,9 @@ dependencies { debugImplementation "com.squareup.leakcanary:leakcanary-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' androidTestImplementation "androidx.test.ext:junit:1.1.1" @@ -164,9 +174,11 @@ dependencies { exclude module: 'support-annotations' } - implementation 'com.github.TeamNewPipe:NewPipeExtractor:19c0e8700db3af35e5dfc58e4eea50e75d7c4c61' - + // NewPipe dependencies + // You can use a local version by uncommenting a few lines in settings.gradle + implementation 'com.github.TeamNewPipe:NewPipeExtractor:6701b0fe718f6bdc385221341fa473e8aaab560e' implementation "com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751" + implementation "org.jsoup:jsoup:1.13.1" implementation "com.squareup.okhttp3:okhttp:3.12.12" @@ -184,6 +196,7 @@ dependencies { implementation "androidx.recyclerview:recyclerview:1.1.0" implementation "androidx.cardview:cardview:1.0.0" 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-viewmodel:${androidxLifecycleVersion}" diff --git a/app/src/debug/java/org/schabi/newpipe/DebugApp.kt b/app/src/debug/java/org/schabi/newpipe/DebugApp.kt index 9ea3bdabec8..3ace75f0bcd 100644 --- a/app/src/debug/java/org/schabi/newpipe/DebugApp.kt +++ b/app/src/debug/java/org/schabi/newpipe/DebugApp.kt @@ -1,6 +1,5 @@ package org.schabi.newpipe -import androidx.multidex.MultiDex import androidx.preference.PreferenceManager import com.facebook.stetho.Stetho import com.facebook.stetho.okhttp3.StethoInterceptor @@ -28,12 +27,6 @@ class DebugApp : App() { return downloader } - override fun initACRA() { - // install MultiDex before initializing ACRA - MultiDex.install(this) - super.initACRA() - } - private fun initStetho() { // Create an InitializerBuilder val initializerBuilder = Stetho.newInitializerBuilder(this) diff --git a/app/src/debug/java/org/schabi/newpipe/settings/DebugSettingsFragment.java b/app/src/debug/java/org/schabi/newpipe/settings/DebugSettingsFragment.java new file mode 100644 index 00000000000..3d63414a823 --- /dev/null +++ b/app/src/debug/java/org/schabi/newpipe/settings/DebugSettingsFragment.java @@ -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); + } +} diff --git a/app/src/main/res/xml/main_settings.xml b/app/src/debug/res/xml/main_settings.xml similarity index 73% rename from app/src/main/res/xml/main_settings.xml rename to app/src/debug/res/xml/main_settings.xml index af093a7575c..dfb8ffa34d0 100644 --- a/app/src/main/res/xml/main_settings.xml +++ b/app/src/debug/res/xml/main_settings.xml @@ -1,57 +1,56 @@ - + android:title="@string/settings_category_video_audio_title" + app:iconSpaceReserved="false" /> + android:title="@string/settings_category_downloads_title" + app:iconSpaceReserved="false" /> + android:title="@string/settings_category_appearance_title" + app:iconSpaceReserved="false" /> + android:title="@string/settings_category_history_title" + app:iconSpaceReserved="false" /> + android:title="@string/content" + app:iconSpaceReserved="false" /> + android:title="@string/settings_category_notification_title" + app:iconSpaceReserved="false" /> + app:iconSpaceReserved="false" /> + app:iconSpaceReserved="false" /> diff --git a/app/src/main/assets/gpl_2.html b/app/src/main/assets/gpl_2.html deleted file mode 100644 index 0e1b8827e5b..00000000000 --- a/app/src/main/assets/gpl_2.html +++ /dev/null @@ -1,400 +0,0 @@ - - - - - - GNU General Public License v2.0 - GNU Project - Free Software Foundation (FSF) - - - -

GNU GENERAL PUBLIC LICENSE

-

-Version 2, June 1991 -

- -
-Copyright (C) 1989, 1991 Free Software Foundation, Inc.
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
-
-Everyone is permitted to copy and distribute verbatim copies -of this license document, but changing it is not allowed. -
- -

Preamble

- -

- The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. -

- -

- When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. -

- -

- To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. -

- -

- For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. -

- -

- We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. -

- -

- Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. -

- -

- Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. -

- -

- The precise terms and conditions for copying, distribution and -modification follow. -

- - -

TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

- - -

-0. - This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". -

- -

-Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. -

- -

-1. - You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. -

- -

-You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. -

- -

-2. - You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: -

- -
-
-
- a) - You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. -
-
-
- b) - You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. -
-
-
- c) - If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) -
-
- -

-These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. -

- -

-Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. -

- -

-In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. -

- -

-3. - You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: -

- - - - -
-
-
- a) - Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, -
-
-
- b) - Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, -
-
-
- c) - Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) -
-
- -

-The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major softwareComponents (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. -

- -

-If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. -

- -

-4. - You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. -

- -

-5. - You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. -

- -

-6. - Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. -

- -

-7. - If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. -

- -

-If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. -

- -

-It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. -

- -

-This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. -

- -

-8. - If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. -

- -

-9. - The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. -

- -

-Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. -

- -

-10. - If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. -

- -

NO WARRANTY

- -

-11. - BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. -

- -

-12. - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. -

- diff --git a/app/src/main/java/org/schabi/newpipe/App.java b/app/src/main/java/org/schabi/newpipe/App.java index 962679cd0b8..5fdc1058aad 100644 --- a/app/src/main/java/org/schabi/newpipe/App.java +++ b/app/src/main/java/org/schabi/newpipe/App.java @@ -1,7 +1,5 @@ package org.schabi.newpipe; -import android.annotation.TargetApi; -import android.app.Application; import android.app.NotificationChannel; import android.app.NotificationManager; import android.content.Context; @@ -10,6 +8,7 @@ import android.util.Log; import androidx.annotation.NonNull; +import androidx.multidex.MultiDexApplication; import androidx.preference.PreferenceManager; import com.nostra13.universalimageloader.cache.memory.impl.LRULimitedMemoryCache; @@ -33,9 +32,11 @@ import java.io.IOException; import java.io.InterruptedIOException; import java.net.SocketException; +import java.util.Arrays; import java.util.Collections; import java.util.List; +import io.reactivex.disposables.Disposable; import io.reactivex.exceptions.CompositeException; import io.reactivex.exceptions.MissingBackpressureException; import io.reactivex.exceptions.OnErrorNotImplementedException; @@ -61,10 +62,13 @@ * along with NewPipe. If not, see . */ -public class App extends Application { +public class App extends MultiDexApplication { protected static final String TAG = App.class.toString(); private static App app; + private Disposable disposable = null; + + @NonNull public static App getApp() { return app; } @@ -90,7 +94,7 @@ public void onCreate() { Localization.init(getApplicationContext()); StateSaver.init(this); - initNotificationChannel(); + initNotificationChannels(); ServiceHelper.initServices(this); @@ -100,7 +104,15 @@ public void onCreate() { configureRxJavaErrorHandler(); // Check for new version - new CheckForNewAppVersionTask().execute(); + disposable = CheckForNewAppVersion.checkNewVersion(this); + } + + @Override + public void onTerminate() { + if (disposable != null) { + disposable.dispose(); + } + super.onTerminate(); } protected Downloader getDownloader() { @@ -219,49 +231,31 @@ protected void initACRA() { } } - public void initNotificationChannel() { + private void initNotificationChannels() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { return; } - final String id = getString(R.string.notification_channel_id); - final CharSequence name = getString(R.string.notification_channel_name); - final String description = getString(R.string.notification_channel_description); + String id = getString(R.string.notification_channel_id); + String name = getString(R.string.notification_channel_name); + String description = getString(R.string.notification_channel_description); // Keep this below DEFAULT to avoid making noise on every notification update final int importance = NotificationManager.IMPORTANCE_LOW; - final NotificationChannel mChannel = new NotificationChannel(id, name, importance); - mChannel.setDescription(description); + final NotificationChannel mainChannel = new NotificationChannel(id, name, importance); + mainChannel.setDescription(description); - final NotificationManager mNotificationManager = - (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - mNotificationManager.createNotificationChannel(mChannel); + id = getString(R.string.app_update_notification_channel_id); + name = getString(R.string.app_update_notification_channel_name); + description = getString(R.string.app_update_notification_channel_description); - setUpUpdateNotificationChannel(importance); - } + final NotificationChannel appUpdateChannel = new NotificationChannel(id, name, importance); + appUpdateChannel.setDescription(description); - /** - * Set up notification channel for app update. - * - * @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); + final NotificationManager notificationManager = getSystemService(NotificationManager.class); + notificationManager.createNotificationChannels(Arrays.asList(mainChannel, + appUpdateChannel)); } protected boolean isDisposedRxExceptionsReported() { diff --git a/app/src/main/java/org/schabi/newpipe/CheckForNewAppVersionTask.java b/app/src/main/java/org/schabi/newpipe/CheckForNewAppVersion.java similarity index 57% rename from app/src/main/java/org/schabi/newpipe/CheckForNewAppVersionTask.java rename to app/src/main/java/org/schabi/newpipe/CheckForNewAppVersion.java index d9123076560..a193149e202 100644 --- a/app/src/main/java/org/schabi/newpipe/CheckForNewAppVersionTask.java +++ b/app/src/main/java/org/schabi/newpipe/CheckForNewAppVersion.java @@ -2,7 +2,6 @@ import android.app.Application; import android.app.PendingIntent; -import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageInfo; @@ -10,12 +9,13 @@ import android.content.pm.Signature; import android.net.ConnectivityManager; import android.net.Uri; -import android.os.AsyncTask; -import androidx.preference.PreferenceManager; import android.util.Log; +import androidx.annotation.NonNull; import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationManagerCompat; +import androidx.core.content.ContextCompat; +import androidx.preference.PreferenceManager; import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonParser; @@ -35,16 +35,18 @@ import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; -/** - * AsyncTask to check if there is a newer version of the NewPipe github apk available or not. - * If there is a newer version we show a notification, informing the user. On tapping - * the notification, the user will be directed to the download link. - */ -public class CheckForNewAppVersionTask extends AsyncTask { +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.disposables.Disposables; +import io.reactivex.schedulers.Schedulers; + +public final class CheckForNewAppVersion { + private CheckForNewAppVersion() { } + private static final boolean DEBUG = MainActivity.DEBUG; - private static final String TAG = CheckForNewAppVersionTask.class.getSimpleName(); + private static final String TAG = CheckForNewAppVersion.class.getSimpleName(); - private static final Application APP = App.getApp(); private static final String GITHUB_APK_SHA1 = "B0:2E:90:7C:1C:D6:FC:57:C3:35:F0:88:D0:8F:50:5F:94:E4:D2:15"; private static final String NEWPIPE_API_URL = "https://newpipe.schabi.org/api/data.json"; @@ -52,18 +54,19 @@ public class CheckForNewAppVersionTask extends AsyncTask { /** * Method to get the apk's SHA1 key. See https://stackoverflow.com/questions/9293019/#22506133. * + * @param application The application * @return String with the apk's SHA1 fingeprint in hexadecimal */ - private static String getCertificateSHA1Fingerprint() { - final PackageManager pm = APP.getPackageManager(); - final String packageName = APP.getPackageName(); + private static String getCertificateSHA1Fingerprint(@NonNull final Application application) { + final PackageManager pm = application.getPackageManager(); + final String packageName = application.getPackageName(); final int flags = PackageManager.GET_SIGNATURES; PackageInfo packageInfo = null; try { packageInfo = pm.getPackageInfo(packageName, flags); } catch (final PackageManager.NameNotFoundException e) { - ErrorActivity.reportError(APP, e, null, null, + ErrorActivity.reportError(application, e, null, null, ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none", "Could not find package info", R.string.app_ui_crash)); } @@ -78,7 +81,7 @@ private static String getCertificateSHA1Fingerprint() { final CertificateFactory cf = CertificateFactory.getInstance("X509"); c = (X509Certificate) cf.generateCertificate(input); } catch (final CertificateException e) { - ErrorActivity.reportError(APP, e, null, null, + ErrorActivity.reportError(application, e, null, null, ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none", "Certificate error", R.string.app_ui_crash)); } @@ -90,7 +93,7 @@ private static String getCertificateSHA1Fingerprint() { final byte[] publicKey = md.digest(c.getEncoded()); hexString = byte2HexFormatted(publicKey); } catch (NoSuchAlgorithmException | CertificateEncodingException e) { - ErrorActivity.reportError(APP, e, null, null, + ErrorActivity.reportError(application, e, null, null, ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none", "Could not retrieve SHA1 key", R.string.app_ui_crash)); } @@ -118,104 +121,108 @@ private static String byte2HexFormatted(final byte[] arr) { return str.toString(); } - public static boolean isGithubApk() { - return getCertificateSHA1Fingerprint().equals(GITHUB_APK_SHA1); - } - - @Override - protected void onPreExecute() { - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(APP); - - // Check if user has enabled/disabled update checking - // and if the current apk is a github one or not. - if (!prefs.getBoolean(APP.getString(R.string.update_app_key), true) || !isGithubApk()) { - this.cancel(true); - } - } - - @Override - protected String doInBackground(final Void... voids) { - if (isCancelled() || !isConnected()) { - return null; - } - - // Make a network request to get latest NewPipe data. - try { - return DownloaderImpl.getInstance().get(NEWPIPE_API_URL).responseBody(); - } catch (IOException | ReCaptchaException e) { - // connectivity problems, do not alarm user and fail silently - if (DEBUG) { - Log.w(TAG, Log.getStackTraceString(e)); - } - } - - return null; - } - - @Override - protected void onPostExecute(final String response) { - // Parse the json from the response. - if (response != null) { - - try { - final JsonObject githubStableObject = JsonParser.object().from(response) - .getObject("flavors").getObject("github").getObject("stable"); - - final String versionName = githubStableObject.getString("version"); - final int versionCode = githubStableObject.getInt("version_code"); - final String apkLocationUrl = githubStableObject.getString("apk"); - - compareAppVersionAndShowNotification(versionName, apkLocationUrl, versionCode); - - } catch (final JsonParserException e) { - // connectivity problems, do not alarm user and fail silently - if (DEBUG) { - Log.w(TAG, Log.getStackTraceString(e)); - } - } - } - } - /** * Method to compare the current and latest available app version. * If a newer version is available, we show the update notification. * + * @param application The application * @param versionName Name of new version * @param apkLocationUrl Url with the new apk * @param versionCode Code of new version */ - private void compareAppVersionAndShowNotification(final String versionName, - final String apkLocationUrl, - final int versionCode) { + private static void compareAppVersionAndShowNotification(@NonNull final Application application, + final String versionName, + final String apkLocationUrl, + final int versionCode) { final int notificationId = 2000; if (BuildConfig.VERSION_CODE < versionCode) { - // A pending intent to open the apk location url in the browser. final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(apkLocationUrl)); final PendingIntent pendingIntent - = PendingIntent.getActivity(APP, 0, intent, 0); + = PendingIntent.getActivity(application, 0, intent, 0); - final NotificationCompat.Builder notificationBuilder = new NotificationCompat - .Builder(APP, APP.getString(R.string.app_update_notification_channel_id)) + final String channelId = application + .getString(R.string.app_update_notification_channel_id); + final NotificationCompat.Builder notificationBuilder + = new NotificationCompat.Builder(application, channelId) .setSmallIcon(R.drawable.ic_newpipe_update) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setContentIntent(pendingIntent) .setAutoCancel(true) - .setContentTitle(APP.getString(R.string.app_update_notification_content_title)) - .setContentText(APP.getString(R.string.app_update_notification_content_text) + .setContentTitle(application + .getString(R.string.app_update_notification_content_title)) + .setContentText(application + .getString(R.string.app_update_notification_content_text) + " " + versionName); final NotificationManagerCompat notificationManager - = NotificationManagerCompat.from(APP); + = NotificationManagerCompat.from(application); notificationManager.notify(notificationId, notificationBuilder.build()); } } - private boolean isConnected() { - final ConnectivityManager cm = - (ConnectivityManager) APP.getSystemService(Context.CONNECTIVITY_SERVICE); + private static boolean isConnected(@NonNull final App app) { + final ConnectivityManager cm = ContextCompat.getSystemService(app, + ConnectivityManager.class); return cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().isConnected(); } + + public static boolean isGithubApk(@NonNull final App app) { + return getCertificateSHA1Fingerprint(app).equals(GITHUB_APK_SHA1); + } + + @NonNull + public static Disposable checkNewVersion(@NonNull final App app) { + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(app); + + // Check if user has enabled/disabled update checking + // and if the current apk is a github one or not. + if (!prefs.getBoolean(app.getString(R.string.update_app_key), true) + || !isGithubApk(app)) { + return Disposables.empty(); + } + + return Observable.fromCallable(() -> { + if (!isConnected(app)) { + return null; + } + + // Make a network request to get latest NewPipe data. + try { + return DownloaderImpl.getInstance().get(NEWPIPE_API_URL).responseBody(); + } catch (IOException | ReCaptchaException e) { + // connectivity problems, do not alarm user and fail silently + if (DEBUG) { + Log.w(TAG, Log.getStackTraceString(e)); + } + } + + return null; + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(response -> { + // Parse the json from the response. + if (response != null) { + try { + final JsonObject githubStableObject = JsonParser.object().from(response) + .getObject("flavors").getObject("github").getObject("stable"); + + final String versionName = githubStableObject.getString("version"); + final int versionCode = githubStableObject.getInt("version_code"); + final String apkLocationUrl = githubStableObject.getString("apk"); + + compareAppVersionAndShowNotification(app, versionName, apkLocationUrl, + versionCode); + } catch (final JsonParserException e) { + // connectivity problems, do not alarm user and fail silently + if (DEBUG) { + Log.w(TAG, Log.getStackTraceString(e)); + } + } + } + }); + } } diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java index e72d4609e43..9bcbe4ff1e1 100644 --- a/app/src/main/java/org/schabi/newpipe/MainActivity.java +++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java @@ -30,9 +30,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import androidx.preference.PreferenceManager; import android.util.Log; - import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; @@ -46,6 +44,7 @@ import android.widget.ImageView; import android.widget.Spinner; import android.widget.TextView; + import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBarDrawerToggle; @@ -55,6 +54,7 @@ import androidx.drawerlayout.widget.DrawerLayout; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; +import androidx.preference.PreferenceManager; import com.google.android.material.bottomsheet.BottomSheetBehavior; import com.google.android.material.navigation.NavigationView; @@ -69,10 +69,11 @@ import org.schabi.newpipe.fragments.list.search.SearchFragment; import org.schabi.newpipe.player.VideoPlayer; import org.schabi.newpipe.player.event.OnKeyDownListener; +import org.schabi.newpipe.player.helper.PlayerHolder; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.report.ErrorActivity; -import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.Constants; +import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.KioskTranslator; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; @@ -87,6 +88,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; @@ -152,7 +154,7 @@ && getSupportFragmentManager().getBackStackEntryCount() == 0) { if (DeviceUtils.isTv(this)) { FocusOverlayView.setupFocusObserver(this); } - setupBroadcastReceiver(); + openMiniPlayerUponPlayerStarted(); } private void setupDrawer() throws Exception { @@ -758,32 +760,36 @@ private void handleIntent(final Intent intent) { if (intent.hasExtra(Constants.KEY_LINK_TYPE)) { final String url = intent.getStringExtra(Constants.KEY_URL); final int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0); - final String title = intent.getStringExtra(Constants.KEY_TITLE); - switch (((StreamingService.LinkType) intent - .getSerializableExtra(Constants.KEY_LINK_TYPE))) { + String title = intent.getStringExtra(Constants.KEY_TITLE); + if (title == null) { + title = ""; + } + + final StreamingService.LinkType linkType = ((StreamingService.LinkType) intent + .getSerializableExtra(Constants.KEY_LINK_TYPE)); + assert linkType != null; + switch (linkType) { case STREAM: - final boolean autoPlay = intent - .getBooleanExtra(VideoDetailFragment.AUTO_PLAY, false); - final String intentCacheKey = intent - .getStringExtra(VideoPlayer.PLAY_QUEUE_KEY); + final String intentCacheKey = intent.getStringExtra( + VideoPlayer.PLAY_QUEUE_KEY); final PlayQueue playQueue = intentCacheKey != null ? SerializedCache.getInstance() .take(intentCacheKey, PlayQueue.class) : null; - NavigationHelper.openVideoDetailFragment(getSupportFragmentManager(), - serviceId, url, title, autoPlay, playQueue); + + final boolean switchingPlayers = intent.getBooleanExtra( + VideoDetailFragment.KEY_SWITCHING_PLAYERS, false); + NavigationHelper.openVideoDetailFragment( + getApplicationContext(), getSupportFragmentManager(), + serviceId, url, title, playQueue, switchingPlayers); break; case CHANNEL: NavigationHelper.openChannelFragment(getSupportFragmentManager(), - serviceId, - url, - title); + serviceId, url, title); break; case PLAYLIST: NavigationHelper.openPlaylistFragment(getSupportFragmentManager(), - serviceId, - url, - title); + serviceId, url, title); break; } } else if (intent.hasExtra(Constants.KEY_OPEN_SEARCH)) { @@ -805,34 +811,47 @@ private void handleIntent(final Intent intent) { } } - private void setupBroadcastReceiver() { - broadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(final Context context, final Intent intent) { - if (intent.getAction().equals(VideoDetailFragment.ACTION_PLAYER_STARTED)) { - final Fragment fragmentPlayer = getSupportFragmentManager() - .findFragmentById(R.id.fragment_player_holder); - if (fragmentPlayer == null) { - /* - * We still don't have a fragment attached to the activity. - * It can happen when a user started popup or background players - * without opening a stream inside the fragment. - * Adding it in a collapsed state (only mini player will be visible) - * */ - NavigationHelper.showMiniPlayer(getSupportFragmentManager()); + private void openMiniPlayerIfMissing() { + final Fragment fragmentPlayer = getSupportFragmentManager() + .findFragmentById(R.id.fragment_player_holder); + if (fragmentPlayer == null) { + // We still don't have a fragment attached to the activity. It can happen when a user + // started popup or background players without opening a stream inside the fragment. + // Adding it in a collapsed state (only mini player will be visible). + NavigationHelper.showMiniPlayer(getSupportFragmentManager()); + } + } + + private void openMiniPlayerUponPlayerStarted() { + if (getIntent().getSerializableExtra(Constants.KEY_LINK_TYPE) + == StreamingService.LinkType.STREAM) { + // handleIntent() already takes care of opening video detail fragment + // due to an intent containing a STREAM link + return; + } + + if (PlayerHolder.isPlayerOpen()) { + // if the player is already open, no need for a broadcast receiver + openMiniPlayerIfMissing(); + } else { + // listen for player start intent being sent around + broadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(final Context context, final Intent intent) { + if (Objects.equals(intent.getAction(), + VideoDetailFragment.ACTION_PLAYER_STARTED)) { + openMiniPlayerIfMissing(); + // At this point the player is added 100%, we can unregister. Other actions + // are useless since the fragment will not be removed after that. + unregisterReceiver(broadcastReceiver); + broadcastReceiver = null; } - /* - * At this point the player is added 100%, we can unregister. - * Other actions are useless since the fragment will not be removed after that - * */ - unregisterReceiver(broadcastReceiver); - broadcastReceiver = null; } - } - }; - final IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(VideoDetailFragment.ACTION_PLAYER_STARTED); - registerReceiver(broadcastReceiver, intentFilter); + }; + final IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(VideoDetailFragment.ACTION_PLAYER_STARTED); + registerReceiver(broadcastReceiver, intentFilter); + } } private boolean bottomSheetHiddenOrCollapsed() { diff --git a/app/src/main/java/org/schabi/newpipe/RouterActivity.java b/app/src/main/java/org/schabi/newpipe/RouterActivity.java index 251affaed22..537d71901ec 100644 --- a/app/src/main/java/org/schabi/newpipe/RouterActivity.java +++ b/app/src/main/java/org/schabi/newpipe/RouterActivity.java @@ -8,7 +8,6 @@ import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.os.Bundle; -import androidx.preference.PreferenceManager; import android.text.TextUtils; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; @@ -27,7 +26,9 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.content.res.AppCompatResources; import androidx.core.app.NotificationCompat; +import androidx.core.widget.TextViewCompat; import androidx.fragment.app.FragmentManager; +import androidx.preference.PreferenceManager; import org.schabi.newpipe.download.DownloadDialog; import org.schabi.newpipe.extractor.Info; @@ -39,14 +40,16 @@ import org.schabi.newpipe.extractor.playlist.PlaylistInfo; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.VideoStream; +import org.schabi.newpipe.player.MainPlayer; import org.schabi.newpipe.player.helper.PlayerHelper; +import org.schabi.newpipe.player.helper.PlayerHolder; import org.schabi.newpipe.player.playqueue.ChannelPlayQueue; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue; import org.schabi.newpipe.player.playqueue.SinglePlayQueue; import org.schabi.newpipe.report.UserAction; -import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.Constants; +import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.ExtractorHelper; import org.schabi.newpipe.util.ListHelper; import org.schabi.newpipe.util.NavigationHelper; @@ -59,8 +62,6 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; import java.util.List; import icepick.Icepick; @@ -115,8 +116,6 @@ protected void onCreate(final Bundle savedInstanceState) { } } - internalRoute = getIntent().getBooleanExtra(INTERNAL_ROUTE_KEY, false); - setTheme(ThemeHelper.isLightThemeSelected(this) ? R.style.RouterActivityThemeLight : R.style.RouterActivityThemeDark); } @@ -326,7 +325,7 @@ private void showDialog(final List choices) { final RadioButton radioButton = (RadioButton) inflater.inflate(R.layout.list_radio_icon_item, null); radioButton.setText(item.description); - radioButton.setCompoundDrawablesWithIntrinsicBounds( + TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(radioButton, AppCompatResources.getDrawable(getApplicationContext(), item.icon), null, null, null); radioButton.setChecked(false); @@ -397,14 +396,22 @@ private List getChoicesForService(final StreamingService serv // show both "show info" and "video player", they are two different activities returnList.add(showInfo); returnList.add(videoPlayer); - } else if (capabilities.contains(VIDEO) - && PlayerHelper.isAutoplayAllowedByUser(context)) { - // show only "video player" since the details activity will be opened and the video - // will be autoplayed there and "show info" would do the exact same thing - returnList.add(videoPlayer); } else { - // show only "show info" if video player is not applicable or autoplay is disabled - returnList.add(showInfo); + final MainPlayer.PlayerType playerType = PlayerHolder.getType(); + if (capabilities.contains(VIDEO) + && PlayerHelper.isAutoplayAllowedByUser(context) + && playerType == null || playerType == MainPlayer.PlayerType.VIDEO) { + // show only "video player" since the details activity will be opened and the + // video will be auto played there. Since "show info" would do the exact same + // thing, use that as a key to let VideoDetailFragment load the stream instead + // of using FetcherService (see comment in handleChoice()) + returnList.add(new AdapterChoiceItem( + showInfo.key, videoPlayer.description, videoPlayer.icon)); + } else { + // show only "show info" if video player is not applicable, auto play is + // disabled or a video is playing in a player different than the main one + returnList.add(showInfo); + } } if (capabilities.contains(VIDEO)) { @@ -491,12 +498,7 @@ private void handleChoice(final String selectedChoiceKey) { .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(intent -> { - if (!internalRoute) { - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); - } startActivity(intent); - finish(); }, throwable -> handleError(throwable, currentUrl)) ); @@ -514,7 +516,7 @@ private void handleChoice(final String selectedChoiceKey) { @SuppressLint("CheckResult") private void openDownloadDialog() { - ExtractorHelper.getStreamInfo(currentServiceId, currentUrl, true) + disposables.add(ExtractorHelper.getStreamInfo(currentServiceId, currentUrl, true) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe((@NonNull StreamInfo result) -> { @@ -531,10 +533,10 @@ private void openDownloadDialog() { downloadDialog.setSelectedVideoStream(selectedVideoStreamIndex); downloadDialog.show(fm, "downloadDialog"); fm.executePendingTransactions(); - downloadDialog.getDialog().setOnDismissListener(dialog -> finish()); + downloadDialog.requireDialog().setOnDismissListener(dialog -> finish()); }, (@NonNull Throwable throwable) -> { showUnsupportedUrlDialog(currentUrl); - }); + })); } @Override @@ -552,66 +554,6 @@ public void onRequestPermissionsResult(final int requestCode, } } - /*////////////////////////////////////////////////////////////////////////// - // Service Fetcher - //////////////////////////////////////////////////////////////////////////*/ - - private String removeHeadingGibberish(final String input) { - int start = 0; - for (int i = input.indexOf("://") - 1; i >= 0; i--) { - if (!input.substring(i, i + 1).matches("\\p{L}")) { - start = i + 1; - break; - } - } - return input.substring(start); - } - - /*////////////////////////////////////////////////////////////////////////// - // Utils - //////////////////////////////////////////////////////////////////////////*/ - - private String trim(final String input) { - if (input == null || input.length() < 1) { - return input; - } else { - String output = input; - while (output.length() > 0 && output.substring(0, 1).matches(REGEX_REMOVE_FROM_URL)) { - output = output.substring(1); - } - while (output.length() > 0 - && output.substring(output.length() - 1).matches(REGEX_REMOVE_FROM_URL)) { - output = output.substring(0, output.length() - 1); - } - return output; - } - } - - /** - * Retrieves all Strings which look remotely like URLs from a text. - * Used if NewPipe was called through share menu. - * - * @param sharedText text to scan for URLs. - * @return potential URLs - */ - protected String[] getUris(final String sharedText) { - final Collection result = new HashSet<>(); - if (sharedText != null) { - final String[] array = sharedText.split("\\p{Space}"); - for (String s : array) { - s = trim(s); - if (s.length() != 0) { - if (s.matches(".+://.+")) { - result.add(removeHeadingGibberish(s)); - } else if (s.matches(".+\\..+")) { - result.add("http://" + s); - } - } - } - } - return result.toArray(new String[0]); - } - private static class AdapterChoiceItem { final String description; final String key; @@ -724,50 +666,34 @@ public Consumer getResultHandler(final Choice choice) { final boolean isExtAudioEnabled = preferences.getBoolean( getString(R.string.use_external_audio_player_key), false); - PlayQueue playQueue; - final String playerChoice = choice.playerChoice; - + final PlayQueue playQueue; if (info instanceof StreamInfo) { - if (playerChoice.equals(backgroundPlayerKey) && isExtAudioEnabled) { + if (choice.playerChoice.equals(backgroundPlayerKey) && isExtAudioEnabled) { NavigationHelper.playOnExternalAudioPlayer(this, (StreamInfo) info); - - } else if (playerChoice.equals(videoPlayerKey) && isExtVideoEnabled) { + return; + } else if (choice.playerChoice.equals(videoPlayerKey) && isExtVideoEnabled) { NavigationHelper.playOnExternalVideoPlayer(this, (StreamInfo) info); - - } else { - playQueue = new SinglePlayQueue((StreamInfo) info); - - if (playerChoice.equals(videoPlayerKey)) { - openMainPlayer(playQueue, choice); - } else if (playerChoice.equals(backgroundPlayerKey)) { - NavigationHelper.enqueueOnBackgroundPlayer(this, playQueue, true); - } else if (playerChoice.equals(popupPlayerKey)) { - NavigationHelper.enqueueOnPopupPlayer(this, playQueue, true); - } + return; } + playQueue = new SinglePlayQueue((StreamInfo) info); + } else if (info instanceof ChannelInfo) { + playQueue = new ChannelPlayQueue((ChannelInfo) info); + } else if (info instanceof PlaylistInfo) { + playQueue = new PlaylistPlayQueue((PlaylistInfo) info); + } else { + return; } - if (info instanceof ChannelInfo || info instanceof PlaylistInfo) { - playQueue = info instanceof ChannelInfo - ? new ChannelPlayQueue((ChannelInfo) info) - : new PlaylistPlayQueue((PlaylistInfo) info); - - if (playerChoice.equals(videoPlayerKey)) { - openMainPlayer(playQueue, choice); - } else if (playerChoice.equals(backgroundPlayerKey)) { - NavigationHelper.playOnBackgroundPlayer(this, playQueue, true); - } else if (playerChoice.equals(popupPlayerKey)) { - NavigationHelper.playOnPopupPlayer(this, playQueue, true); - } + if (choice.playerChoice.equals(videoPlayerKey)) { + NavigationHelper.playOnMainPlayer(this, playQueue, false); + } else if (choice.playerChoice.equals(backgroundPlayerKey)) { + NavigationHelper.playOnBackgroundPlayer(this, playQueue, true); + } else if (choice.playerChoice.equals(popupPlayerKey)) { + NavigationHelper.playOnPopupPlayer(this, playQueue, true); } }; } - private void openMainPlayer(final PlayQueue playQueue, final Choice choice) { - NavigationHelper.playOnMainPlayer(this, playQueue, choice.linkType, - choice.url, "", true, true); - } - @Override public void onDestroy() { super.onDestroy(); diff --git a/app/src/main/java/org/schabi/newpipe/about/AboutActivity.java b/app/src/main/java/org/schabi/newpipe/about/AboutActivity.java index 81ba78ba997..a15bb9e41c6 100644 --- a/app/src/main/java/org/schabi/newpipe/about/AboutActivity.java +++ b/app/src/main/java/org/schabi/newpipe/about/AboutActivity.java @@ -8,16 +8,17 @@ import android.view.ViewGroup; import android.widget.TextView; +import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentPagerAdapter; -import androidx.fragment.app.FragmentStatePagerAdapter; -import androidx.viewpager.widget.PagerAdapter; -import androidx.viewpager.widget.ViewPager; +import androidx.fragment.app.FragmentActivity; +import androidx.recyclerview.widget.RecyclerView; +import androidx.viewpager2.adapter.FragmentStateAdapter; +import androidx.viewpager2.widget.ViewPager2; import com.google.android.material.tabs.TabLayout; +import com.google.android.material.tabs.TabLayoutMediator; import org.schabi.newpipe.BuildConfig; import org.schabi.newpipe.R; @@ -32,7 +33,7 @@ public class AboutActivity extends AppCompatActivity { */ private static final SoftwareComponent[] SOFTWARE_COMPONENTS = new SoftwareComponent[]{ new SoftwareComponent("Giga Get", "2014 - 2015", "Peter Cai", - "https://github.com/PaperAirplane-Dev-Team/GigaGet", StandardLicenses.GPL2), + "https://github.com/PaperAirplane-Dev-Team/GigaGet", StandardLicenses.GPL3), new SoftwareComponent("NewPipe Extractor", "2017 - 2020", "Christian Schabesberger", "https://github.com/TeamNewPipe/NewPipeExtractor", StandardLicenses.GPL3), new SoftwareComponent("Jsoup", "2017", "Jonathan Hedley", @@ -64,20 +65,20 @@ public class AboutActivity extends AppCompatActivity { "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 - * {@link FragmentPagerAdapter} derivative, which will keep every - * loaded fragment in memory. If this becomes too memory intensive, it - * may be best to switch to a - * {@link FragmentStatePagerAdapter}. + * {@link FragmentStateAdapter} derivative, which will keep every + * loaded fragment in memory. */ 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 protected void onCreate(final Bundle savedInstanceState) { @@ -93,14 +94,24 @@ protected void onCreate(final Bundle savedInstanceState) { getSupportActionBar().setDisplayHomeAsUpEnabled(true); // Create the adapter that will return a fragment for each of the three // primary sections of the activity. - mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager()); + mSectionsPagerAdapter = new SectionsPagerAdapter(this); // Set up the ViewPager with the sections adapter. mViewPager = findViewById(R.id.container); mViewPager.setAdapter(mSectionsPagerAdapter); 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 @@ -162,40 +173,30 @@ public View onCreateView(final LayoutInflater inflater, final ViewGroup containe } /** - * A {@link FragmentPagerAdapter} that returns a fragment corresponding to + * A {@link FragmentStateAdapter} that returns a fragment corresponding to * one of the sections/tabs/pages. */ - public class SectionsPagerAdapter extends FragmentPagerAdapter { - public SectionsPagerAdapter(final FragmentManager fm) { - super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); + public static class SectionsPagerAdapter extends FragmentStateAdapter { + public SectionsPagerAdapter(final FragmentActivity fa) { + super(fa); } + @NonNull @Override - public Fragment getItem(final int position) { + public Fragment createFragment(final int position) { switch (position) { - case 0: + default: + case POS_ABOUT: return AboutFragment.newInstance(); - case 1: + case POS_LICENSE: return LicenseFragment.newInstance(SOFTWARE_COMPONENTS); } - return null; } @Override - public int getCount() { + public int getItemCount() { // Show 2 total pages. - return 2; - } - - @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; + return TOTAL_COUNT; } } } diff --git a/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.java b/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.java index e869dbb147d..bac789dbd1b 100644 --- a/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.java +++ b/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.java @@ -1,6 +1,5 @@ package org.schabi.newpipe.about; -import android.app.Activity; import android.os.Bundle; import android.view.ContextMenu; import android.view.LayoutInflater; @@ -19,16 +18,21 @@ import java.io.Serializable; import java.util.Arrays; +import java.util.Comparator; + +import io.reactivex.disposables.CompositeDisposable; /** * Fragment containing the software licenses. */ public class LicenseFragment extends Fragment { private static final String ARG_COMPONENTS = "components"; + private static final String LICENSE_KEY = "ACTIVE_LICENSE"; + private SoftwareComponent[] softwareComponents; private SoftwareComponent componentForContextMenu; private License activeLicense; - private static final String LICENSE_KEY = "ACTIVE_LICENSE"; + private final CompositeDisposable compositeDisposable = new CompositeDisposable(); public static LicenseFragment newInstance(final SoftwareComponent[] softwareComponents) { if (softwareComponents == null) { @@ -41,16 +45,6 @@ public static LicenseFragment newInstance(final SoftwareComponent[] softwareComp return fragment; } - /** - * Shows a popup containing the license. - * - * @param context the context to use - * @param license the license to show - */ - private static void showLicense(final Activity context, final License license) { - new LicenseFragmentHelper(context).execute(license); - } - @Override public void onCreate(@Nullable final Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -64,7 +58,13 @@ public void onCreate(@Nullable final Bundle savedInstanceState) { } } // Sort components by name - Arrays.sort(softwareComponents, (o1, o2) -> o1.getName().compareTo(o2.getName())); + Arrays.sort(softwareComponents, Comparator.comparing(SoftwareComponent::getName)); + } + + @Override + public void onDestroy() { + compositeDisposable.dispose(); + super.onDestroy(); } @Nullable @@ -76,8 +76,9 @@ public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGrou final View licenseLink = rootView.findViewById(R.id.app_read_license); licenseLink.setOnClickListener(v -> { - activeLicense = StandardLicenses.GPL3; - showLicense(getActivity(), StandardLicenses.GPL3); + activeLicense = StandardLicenses.GPL3; + compositeDisposable.add(LicenseFragmentHelper.showLicense(getActivity(), + StandardLicenses.GPL3)); }); for (final SoftwareComponent component : softwareComponents) { @@ -94,13 +95,15 @@ public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGrou componentView.setTag(component); componentView.setOnClickListener(v -> { activeLicense = component.getLicense(); - showLicense(getActivity(), component.getLicense()); + compositeDisposable.add(LicenseFragmentHelper.showLicense(getActivity(), + component.getLicense())); }); softwareComponentsView.addView(componentView); registerForContextMenu(componentView); } if (activeLicense != null) { - showLicense(getActivity(), activeLicense); + compositeDisposable.add(LicenseFragmentHelper.showLicense(getActivity(), + activeLicense)); } return rootView; } @@ -128,7 +131,8 @@ public boolean onContextItemSelected(@NonNull final MenuItem item) { ShareUtils.openUrlInBrowser(getActivity(), component.getLink()); return true; case R.id.action_show_license: - showLicense(getActivity(), component.getLicense()); + compositeDisposable.add(LicenseFragmentHelper.showLicense(getActivity(), + component.getLicense())); } return false; } diff --git a/app/src/main/java/org/schabi/newpipe/about/LicenseFragmentHelper.java b/app/src/main/java/org/schabi/newpipe/about/LicenseFragmentHelper.java index 01a01bc8834..8a2ab6fa9be 100644 --- a/app/src/main/java/org/schabi/newpipe/about/LicenseFragmentHelper.java +++ b/app/src/main/java/org/schabi/newpipe/about/LicenseFragmentHelper.java @@ -1,8 +1,6 @@ package org.schabi.newpipe.about; -import android.app.Activity; import android.content.Context; -import android.os.AsyncTask; import android.util.Base64; import android.webkit.WebView; @@ -16,18 +14,18 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; -import java.lang.ref.WeakReference; import java.nio.charset.StandardCharsets; -import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.disposables.Disposables; +import io.reactivex.schedulers.Schedulers; -public class LicenseFragmentHelper extends AsyncTask { - private final WeakReference weakReference; - private License license; +import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; - public LicenseFragmentHelper(@Nullable final Activity activity) { - weakReference = new WeakReference<>(activity); - } +public final class LicenseFragmentHelper { + private LicenseFragmentHelper() { } /** * @param context the context to use @@ -62,7 +60,7 @@ private static String getFormattedLicense(@NonNull final Context context, * @param context * @return String which is a CSS stylesheet according to the context's theme */ - private static String getLicenseStylesheet(final Context context) { + private static String getLicenseStylesheet(@NonNull final Context context) { final boolean isLightTheme = ThemeHelper.isLightThemeSelected(context); return "body{padding:12px 15px;margin:0;" + "background:#" + getHexRGBColor(context, isLightTheme @@ -84,45 +82,31 @@ private static String getLicenseStylesheet(final Context context) { * @param color the color number from R.color * @return a six characters long String with hexadecimal RGB values */ - private static String getHexRGBColor(final Context context, final int color) { + private static String getHexRGBColor(@NonNull final Context context, final int color) { return context.getResources().getString(color).substring(3); } - @Nullable - private Activity getActivity() { - final Activity activity = weakReference.get(); - - if (activity != null && activity.isFinishing()) { - return null; - } else { - return activity; - } - } - - @Override - protected Integer doInBackground(final Object... objects) { - license = (License) objects[0]; - return 1; - } - - @Override - protected void onPostExecute(final Integer result) { - final Activity activity = getActivity(); - if (activity == null) { - return; + static Disposable showLicense(@Nullable final Context context, @NonNull final License license) { + if (context == null) { + return Disposables.empty(); } - final String webViewData = Base64.encodeToString(getFormattedLicense(activity, license) - .getBytes(StandardCharsets.UTF_8), Base64.NO_PADDING); - final WebView webView = new WebView(activity); - webView.loadData(webViewData, "text/html; charset=UTF-8", "base64"); - - final AlertDialog.Builder alert = new AlertDialog.Builder(activity); - alert.setTitle(license.getName()); - alert.setView(webView); - assureCorrectAppLanguage(activity); - alert.setNegativeButton(activity.getString(R.string.finish), - (dialog, which) -> dialog.dismiss()); - alert.show(); + return Observable.fromCallable(() -> getFormattedLicense(context, license)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(formattedLicense -> { + final String webViewData = Base64.encodeToString(formattedLicense + .getBytes(StandardCharsets.UTF_8), Base64.NO_PADDING); + final WebView webView = new WebView(context); + webView.loadData(webViewData, "text/html; charset=UTF-8", "base64"); + + final AlertDialog.Builder alert = new AlertDialog.Builder(context); + alert.setTitle(license.getName()); + alert.setView(webView); + assureCorrectAppLanguage(context); + alert.setNegativeButton(context.getString(R.string.finish), + (dialog, which) -> dialog.dismiss()); + alert.show(); + }); } } diff --git a/app/src/main/java/org/schabi/newpipe/about/StandardLicenses.java b/app/src/main/java/org/schabi/newpipe/about/StandardLicenses.java index 75a7a861317..50ee5ebc37a 100644 --- a/app/src/main/java/org/schabi/newpipe/about/StandardLicenses.java +++ b/app/src/main/java/org/schabi/newpipe/about/StandardLicenses.java @@ -4,8 +4,6 @@ * Class containing information about standard software licenses. */ public final class StandardLicenses { - public static final License GPL2 - = new License("GNU General Public License, Version 2.0", "GPLv2", "gpl_2.html"); public static final License GPL3 = new License("GNU General Public License, Version 3.0", "GPLv3", "gpl_3.html"); public static final License APACHE2 diff --git a/app/src/main/java/org/schabi/newpipe/database/Converters.java b/app/src/main/java/org/schabi/newpipe/database/Converters.java index ca2d8d875c7..c46b5f4275c 100644 --- a/app/src/main/java/org/schabi/newpipe/database/Converters.java +++ b/app/src/main/java/org/schabi/newpipe/database/Converters.java @@ -5,31 +5,35 @@ import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.local.subscription.FeedGroupIcon; -import java.util.Date; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; public final class Converters { private Converters() { } /** - * Convert a long value to a date. + * Convert a long value to a {@link OffsetDateTime}. * * @param value the long value - * @return the date + * @return the {@code OffsetDateTime} */ @TypeConverter - public static Date fromTimestamp(final Long value) { - return value == null ? null : new Date(value); + public static OffsetDateTime offsetDateTimeFromTimestamp(final Long value) { + return value == null ? null : OffsetDateTime.ofInstant(Instant.ofEpochMilli(value), + ZoneOffset.UTC); } /** - * Convert a date to a long value. + * Convert a {@link OffsetDateTime} to a long value. * - * @param date the date + * @param offsetDateTime the {@code OffsetDateTime} * @return the long value */ @TypeConverter - public static Long dateToTimestamp(final Date date) { - return date == null ? null : date.getTime(); + public static Long offsetDateTimeToTimestamp(final OffsetDateTime offsetDateTime) { + return offsetDateTime == null ? null : offsetDateTime.withOffsetSameInstant(ZoneOffset.UTC) + .toInstant().toEpochMilli(); } @TypeConverter diff --git a/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedDAO.kt b/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedDAO.kt index 74f5b369e04..d8b4f72cce4 100644 --- a/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedDAO.kt +++ b/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedDAO.kt @@ -7,7 +7,7 @@ import androidx.room.Query import androidx.room.Transaction import androidx.room.Update import io.reactivex.Flowable -import java.util.Date +import java.time.OffsetDateTime import org.schabi.newpipe.database.feed.model.FeedEntity import org.schabi.newpipe.database.feed.model.FeedLastUpdatedEntity import org.schabi.newpipe.database.stream.model.StreamEntity @@ -58,10 +58,10 @@ abstract class FeedDAO { INNER JOIN feed f ON s.uid = f.stream_id - WHERE s.upload_date < :date + WHERE s.upload_date < :offsetDateTime ) """) - abstract fun unlinkStreamsOlderThan(date: Date) + abstract fun unlinkStreamsOlderThan(offsetDateTime: OffsetDateTime) @Query(""" DELETE FROM feed @@ -106,10 +106,10 @@ abstract class FeedDAO { INNER JOIN feed_group_subscription_join fgs ON fgs.subscription_id = lu.subscription_id AND fgs.group_id = :groupId """) - abstract fun oldestSubscriptionUpdate(groupId: Long): Flowable> + abstract fun oldestSubscriptionUpdate(groupId: Long): Flowable> @Query("SELECT MIN(last_updated) FROM feed_last_updated") - abstract fun oldestSubscriptionUpdateFromAll(): Flowable> + abstract fun oldestSubscriptionUpdateFromAll(): Flowable> @Query("SELECT COUNT(*) FROM feed_last_updated WHERE last_updated IS NULL") abstract fun notLoadedCount(): Flowable @@ -135,7 +135,7 @@ abstract class FeedDAO { WHERE lu.last_updated IS NULL OR lu.last_updated < :outdatedThreshold """) - abstract fun getAllOutdated(outdatedThreshold: Date): Flowable> + abstract fun getAllOutdated(outdatedThreshold: OffsetDateTime): Flowable> @Query(""" SELECT s.* FROM subscriptions s @@ -148,5 +148,5 @@ abstract class FeedDAO { WHERE lu.last_updated IS NULL OR lu.last_updated < :outdatedThreshold """) - abstract fun getAllOutdatedForGroup(groupId: Long, outdatedThreshold: Date): Flowable> + abstract fun getAllOutdatedForGroup(groupId: Long, outdatedThreshold: OffsetDateTime): Flowable> } diff --git a/app/src/main/java/org/schabi/newpipe/database/feed/model/FeedLastUpdatedEntity.kt b/app/src/main/java/org/schabi/newpipe/database/feed/model/FeedLastUpdatedEntity.kt index 78b2550a55b..069d1138fbb 100644 --- a/app/src/main/java/org/schabi/newpipe/database/feed/model/FeedLastUpdatedEntity.kt +++ b/app/src/main/java/org/schabi/newpipe/database/feed/model/FeedLastUpdatedEntity.kt @@ -4,7 +4,7 @@ import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.ForeignKey import androidx.room.PrimaryKey -import java.util.Date +import java.time.OffsetDateTime import org.schabi.newpipe.database.feed.model.FeedLastUpdatedEntity.Companion.FEED_LAST_UPDATED_TABLE import org.schabi.newpipe.database.feed.model.FeedLastUpdatedEntity.Companion.SUBSCRIPTION_ID import org.schabi.newpipe.database.subscription.SubscriptionEntity @@ -25,9 +25,8 @@ data class FeedLastUpdatedEntity( var subscriptionId: Long, @ColumnInfo(name = LAST_UPDATED) - var lastUpdated: Date? = null + var lastUpdated: OffsetDateTime? = null ) { - companion object { const val FEED_LAST_UPDATED_TABLE = "feed_last_updated" diff --git a/app/src/main/java/org/schabi/newpipe/database/history/model/SearchHistoryEntry.java b/app/src/main/java/org/schabi/newpipe/database/history/model/SearchHistoryEntry.java index 7528351823e..fd45887000d 100644 --- a/app/src/main/java/org/schabi/newpipe/database/history/model/SearchHistoryEntry.java +++ b/app/src/main/java/org/schabi/newpipe/database/history/model/SearchHistoryEntry.java @@ -6,7 +6,7 @@ import androidx.room.Index; import androidx.room.PrimaryKey; -import java.util.Date; +import java.time.OffsetDateTime; import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.SEARCH; @@ -24,7 +24,7 @@ public class SearchHistoryEntry { private long id; @ColumnInfo(name = CREATION_DATE) - private Date creationDate; + private OffsetDateTime creationDate; @ColumnInfo(name = SERVICE_ID) private int serviceId; @@ -32,7 +32,8 @@ public class SearchHistoryEntry { @ColumnInfo(name = SEARCH) private String search; - public SearchHistoryEntry(final Date creationDate, final int serviceId, final String search) { + public SearchHistoryEntry(final OffsetDateTime creationDate, final int serviceId, + final String search) { this.serviceId = serviceId; this.creationDate = creationDate; this.search = search; @@ -46,11 +47,11 @@ public void setId(final long id) { this.id = id; } - public Date getCreationDate() { + public OffsetDateTime getCreationDate() { return creationDate; } - public void setCreationDate(final Date creationDate) { + public void setCreationDate(final OffsetDateTime creationDate) { this.creationDate = creationDate; } diff --git a/app/src/main/java/org/schabi/newpipe/database/history/model/StreamHistoryEntity.java b/app/src/main/java/org/schabi/newpipe/database/history/model/StreamHistoryEntity.java index bf1f7a9dda1..ad1941adbd0 100644 --- a/app/src/main/java/org/schabi/newpipe/database/history/model/StreamHistoryEntity.java +++ b/app/src/main/java/org/schabi/newpipe/database/history/model/StreamHistoryEntity.java @@ -9,7 +9,7 @@ import org.schabi.newpipe.database.stream.model.StreamEntity; -import java.util.Date; +import java.time.OffsetDateTime; import static androidx.room.ForeignKey.CASCADE; import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.JOIN_STREAM_ID; @@ -37,12 +37,12 @@ public class StreamHistoryEntity { @NonNull @ColumnInfo(name = STREAM_ACCESS_DATE) - private Date accessDate; + private OffsetDateTime accessDate; @ColumnInfo(name = STREAM_REPEAT_COUNT) private long repeatCount; - public StreamHistoryEntity(final long streamUid, @NonNull final Date accessDate, + public StreamHistoryEntity(final long streamUid, @NonNull final OffsetDateTime accessDate, final long repeatCount) { this.streamUid = streamUid; this.accessDate = accessDate; @@ -50,7 +50,7 @@ public StreamHistoryEntity(final long streamUid, @NonNull final Date accessDate, } @Ignore - public StreamHistoryEntity(final long streamUid, @NonNull final Date accessDate) { + public StreamHistoryEntity(final long streamUid, @NonNull final OffsetDateTime accessDate) { this(streamUid, accessDate, 1); } @@ -62,11 +62,12 @@ public void setStreamUid(final long streamUid) { this.streamUid = streamUid; } - public Date getAccessDate() { + @NonNull + public OffsetDateTime getAccessDate() { return accessDate; } - public void setAccessDate(@NonNull final Date accessDate) { + public void setAccessDate(@NonNull final OffsetDateTime accessDate) { this.accessDate = accessDate; } diff --git a/app/src/main/java/org/schabi/newpipe/database/history/model/StreamHistoryEntry.kt b/app/src/main/java/org/schabi/newpipe/database/history/model/StreamHistoryEntry.kt index c653e6c6ffc..b928b00bfc3 100644 --- a/app/src/main/java/org/schabi/newpipe/database/history/model/StreamHistoryEntry.kt +++ b/app/src/main/java/org/schabi/newpipe/database/history/model/StreamHistoryEntry.kt @@ -2,7 +2,7 @@ package org.schabi.newpipe.database.history.model import androidx.room.ColumnInfo import androidx.room.Embedded -import java.util.Date +import java.time.OffsetDateTime import org.schabi.newpipe.database.stream.model.StreamEntity data class StreamHistoryEntry( @@ -13,7 +13,7 @@ data class StreamHistoryEntry( val streamId: Long, @ColumnInfo(name = StreamHistoryEntity.STREAM_ACCESS_DATE) - val accessDate: Date, + val accessDate: OffsetDateTime, @ColumnInfo(name = StreamHistoryEntity.STREAM_REPEAT_COUNT) val repeatCount: Long @@ -25,6 +25,6 @@ data class StreamHistoryEntry( fun hasEqualValues(other: StreamHistoryEntry): Boolean { return this.streamEntity.uid == other.streamEntity.uid && streamId == other.streamId && - accessDate.compareTo(other.accessDate) == 0 + accessDate.isEqual(other.accessDate) } } diff --git a/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistLocalItem.java b/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistLocalItem.java index 3ce95631c06..43dbd89ea46 100644 --- a/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistLocalItem.java +++ b/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistLocalItem.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.List; public interface PlaylistLocalItem extends LocalItem { @@ -18,15 +19,8 @@ static List merge( items.addAll(localPlaylists); items.addAll(remotePlaylists); - Collections.sort(items, (left, right) -> { - final String on1 = left.getOrderingName(); - final String on2 = right.getOrderingName(); - if (on1 == null) { - return on2 == null ? 0 : 1; - } else { - return on2 == null ? -1 : on1.compareToIgnoreCase(on2); - } - }); + Collections.sort(items, Comparator.comparing(PlaylistLocalItem::getOrderingName, + Comparator.nullsLast(String.CASE_INSENSITIVE_ORDER))); return items; } diff --git a/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistDAO.java b/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistDAO.java index 2cfe5440c97..ad9c148ca73 100644 --- a/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistDAO.java +++ b/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistDAO.java @@ -33,4 +33,7 @@ public Flowable> listByService(final int serviceId) { @Query("DELETE FROM " + PLAYLIST_TABLE + " WHERE " + PLAYLIST_ID + " = :playlistId") public abstract int deletePlaylist(long playlistId); + + @Query("SELECT COUNT(*) FROM " + PLAYLIST_TABLE) + public abstract Flowable getCount(); } diff --git a/app/src/main/java/org/schabi/newpipe/database/stream/StreamStatisticsEntry.kt b/app/src/main/java/org/schabi/newpipe/database/stream/StreamStatisticsEntry.kt index dde1f0392bc..1e4c672ab4b 100644 --- a/app/src/main/java/org/schabi/newpipe/database/stream/StreamStatisticsEntry.kt +++ b/app/src/main/java/org/schabi/newpipe/database/stream/StreamStatisticsEntry.kt @@ -2,26 +2,25 @@ package org.schabi.newpipe.database.stream import androidx.room.ColumnInfo import androidx.room.Embedded -import java.util.Date +import java.time.OffsetDateTime import org.schabi.newpipe.database.LocalItem import org.schabi.newpipe.database.history.model.StreamHistoryEntity import org.schabi.newpipe.database.stream.model.StreamEntity import org.schabi.newpipe.extractor.stream.StreamInfoItem class StreamStatisticsEntry( - @Embedded + @Embedded val streamEntity: StreamEntity, - @ColumnInfo(name = StreamHistoryEntity.JOIN_STREAM_ID) + @ColumnInfo(name = StreamHistoryEntity.JOIN_STREAM_ID) val streamId: Long, - @ColumnInfo(name = STREAM_LATEST_DATE) - val latestAccessDate: Date, + @ColumnInfo(name = STREAM_LATEST_DATE) + val latestAccessDate: OffsetDateTime, - @ColumnInfo(name = STREAM_WATCH_COUNT) + @ColumnInfo(name = STREAM_WATCH_COUNT) val watchCount: Long ) : LocalItem { - fun toStreamInfoItem(): StreamInfoItem { val item = StreamInfoItem(streamEntity.serviceId, streamEntity.url, streamEntity.title, streamEntity.streamType) item.duration = streamEntity.duration diff --git a/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.kt b/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.kt index 921c08b4623..89757c17d71 100644 --- a/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.kt +++ b/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamDAO.kt @@ -7,7 +7,7 @@ import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Transaction import io.reactivex.Flowable -import java.util.Date +import java.time.OffsetDateTime import org.schabi.newpipe.database.BasicDAO import org.schabi.newpipe.database.stream.model.StreamEntity import org.schabi.newpipe.database.stream.model.StreamEntity.Companion.STREAM_ID @@ -129,7 +129,7 @@ abstract class StreamDAO : BasicDAO { var textualUploadDate: String? = null, @ColumnInfo(name = StreamEntity.STREAM_UPLOAD_DATE) - var uploadDate: Date? = null, + var uploadDate: OffsetDateTime? = null, @ColumnInfo(name = StreamEntity.STREAM_IS_UPLOAD_DATE_APPROXIMATION) var isUploadDateApproximation: Boolean? = null, diff --git a/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamEntity.kt b/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamEntity.kt index d13f5cc2d13..defcb7acf5a 100644 --- a/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamEntity.kt +++ b/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamEntity.kt @@ -6,8 +6,7 @@ import androidx.room.Ignore import androidx.room.Index import androidx.room.PrimaryKey import java.io.Serializable -import java.util.Calendar -import java.util.Date +import java.time.OffsetDateTime import org.schabi.newpipe.database.stream.model.StreamEntity.Companion.STREAM_SERVICE_ID import org.schabi.newpipe.database.stream.model.StreamEntity.Companion.STREAM_TABLE import org.schabi.newpipe.database.stream.model.StreamEntity.Companion.STREAM_URL @@ -55,18 +54,17 @@ data class StreamEntity( var textualUploadDate: String? = null, @ColumnInfo(name = STREAM_UPLOAD_DATE) - var uploadDate: Date? = null, + var uploadDate: OffsetDateTime? = null, @ColumnInfo(name = STREAM_IS_UPLOAD_DATE_APPROXIMATION) var isUploadDateApproximation: Boolean? = null ) : Serializable { - @Ignore constructor(item: StreamInfoItem) : this( serviceId = item.serviceId, url = item.url, title = item.name, streamType = item.streamType, duration = item.duration, uploader = item.uploaderName, thumbnailUrl = item.thumbnailUrl, viewCount = item.viewCount, - textualUploadDate = item.textualUploadDate, uploadDate = item.uploadDate?.date()?.time, + textualUploadDate = item.textualUploadDate, uploadDate = item.uploadDate?.offsetDateTime(), isUploadDateApproximation = item.uploadDate?.isApproximation ) @@ -75,7 +73,7 @@ data class StreamEntity( serviceId = info.serviceId, url = info.url, title = info.name, streamType = info.streamType, duration = info.duration, uploader = info.uploaderName, thumbnailUrl = info.thumbnailUrl, viewCount = info.viewCount, - textualUploadDate = info.textualUploadDate, uploadDate = info.uploadDate?.date()?.time, + textualUploadDate = info.textualUploadDate, uploadDate = info.uploadDate?.offsetDateTime(), isUploadDateApproximation = info.uploadDate?.isApproximation ) @@ -95,8 +93,7 @@ data class StreamEntity( if (viewCount != null) item.viewCount = viewCount as Long item.textualUploadDate = textualUploadDate item.uploadDate = uploadDate?.let { - DateWrapper(Calendar.getInstance().apply { time = it }, isUploadDateApproximation - ?: false) + DateWrapper(it, isUploadDateApproximation ?: false) } return item diff --git a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java index 17d079d50ea..93398d990af 100644 --- a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java +++ b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java @@ -646,7 +646,7 @@ private void prepareSelectedDownload() { mainStorage = mainStorageVideo; // subtitle & video files go together format = subtitleStreamsAdapter.getItem(selectedSubtitleIndex).getFormat(); mime = format.mimeType; - filename += format == MediaFormat.TTML ? MediaFormat.SRT.suffix : format.suffix; + filename += (format == MediaFormat.TTML ? MediaFormat.SRT : format).suffix; break; default: throw new RuntimeException("No stream selected"); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index 28a67173b15..67bc51c58c7 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -80,6 +80,7 @@ import org.schabi.newpipe.fragments.list.comments.CommentsFragment; import org.schabi.newpipe.fragments.list.videos.RelatedVideosFragment; 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.player.BasePlayer; import org.schabi.newpipe.player.MainPlayer; @@ -109,6 +110,7 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Objects; import java.util.concurrent.TimeUnit; import icepick.State; @@ -127,7 +129,7 @@ import static org.schabi.newpipe.player.playqueue.PlayQueueItem.RECOVERY_UNSET; import static org.schabi.newpipe.util.AnimationUtils.animateView; -public class VideoDetailFragment +public final class VideoDetailFragment extends BaseStateFragment implements BackPressable, SharedPreferences.OnSharedPreferenceChangeListener, @@ -135,7 +137,7 @@ public class VideoDetailFragment View.OnLongClickListener, PlayerServiceExtendedEventListener, OnKeyDownListener { - public static final String AUTO_PLAY = "auto_play"; + public static final String KEY_SWITCHING_PLAYERS = "switching_players"; private static final int RELATED_STREAMS_UPDATE_FLAG = 0x1; private static final int COMMENTS_UPDATE_FLAG = 0x2; @@ -166,19 +168,23 @@ public class VideoDetailFragment @State protected int serviceId = Constants.NO_SERVICE_ID; @State - protected String name; + @NonNull + protected String title = ""; @State - protected String url; - protected static PlayQueue playQueue; + @Nullable + protected String url = null; + @Nullable + protected PlayQueue playQueue = null; @State int bottomSheetState = BottomSheetBehavior.STATE_EXPANDED; @State protected boolean autoPlayEnabled = true; - private static StreamInfo currentInfo; + @Nullable + private StreamInfo currentInfo = null; private Disposable currentWorker; @NonNull - private CompositeDisposable disposables = new CompositeDisposable(); + private final CompositeDisposable disposables = new CompositeDisposable(); @Nullable private Disposable positionSubscriber = null; @@ -283,6 +289,7 @@ public void onServiceConnected(final VideoPlayerImpl connectedPlayer, || (currentInfo != null && isAutoplayEnabled() && player.getParentActivity() == null)) { + autoPlayEnabled = true; // forcefully start playing openVideoPlayer(); } } @@ -297,8 +304,10 @@ public void onServiceDisconnected() { /*////////////////////////////////////////////////////////////////////////*/ - public static VideoDetailFragment getInstance(final int serviceId, final String videoUrl, - final String name, final PlayQueue queue) { + public static VideoDetailFragment getInstance(final int serviceId, + @Nullable final String videoUrl, + @NonNull final String name, + @Nullable final PlayQueue queue) { final VideoDetailFragment instance = new VideoDetailFragment(); instance.setInitialData(serviceId, videoUrl, name, queue); return instance; @@ -443,8 +452,8 @@ public void onActivityResult(final int requestCode, final int resultCode, final switch (requestCode) { case ReCaptchaActivity.RECAPTCHA_REQUEST: if (resultCode == Activity.RESULT_OK) { - NavigationHelper - .openVideoDetailFragment(getFM(), serviceId, url, name); + NavigationHelper.openVideoDetailFragment(requireContext(), getFM(), + serviceId, url, title, null, false); } else { Log.e(TAG, "ReCaptcha failed"); } @@ -482,8 +491,14 @@ public void onClick(final View v) { break; case R.id.detail_controls_playlist_append: 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; case R.id.detail_controls_download: @@ -507,6 +522,7 @@ public void onClick(final View v) { } break; case R.id.detail_thumbnail_root_layout: + autoPlayEnabled = true; // forcefully start playing openVideoPlayer(); break; case R.id.detail_title_root_layout: @@ -523,6 +539,7 @@ public void onClick(final View v) { player.hideControls(0, 0); showSystemUi(); } else { + autoPlayEnabled = true; // forcefully start playing openVideoPlayer(); } @@ -784,7 +801,7 @@ public boolean onBackPressed() { player.onPause(); } restoreDefaultOrientation(); - setAutoplay(false); + setAutoPlay(false); return true; } @@ -812,14 +829,11 @@ public boolean onBackPressed() { } private void setupFromHistoryItem(final StackItem item) { - setAutoplay(false); + setAutoPlay(false); hideMainPlayer(); - setInitialData( - item.getServiceId(), - item.getUrl(), - !TextUtils.isEmpty(item.getTitle()) ? item.getTitle() : "", - item.getPlayQueue()); + setInitialData(item.getServiceId(), item.getUrl(), + item.getTitle() == null ? "" : item.getTitle(), item.getPlayQueue()); startLoading(false); // Maybe an item was deleted in background activity @@ -853,18 +867,17 @@ protected void doInitialLoadLogic() { } } - public void selectAndLoadVideo(final int sid, final String videoUrl, final String title, - final PlayQueue queue) { - // Situation when user switches from players to main player. - // All needed data is here, we can start watching - if (this.playQueue != null && this.playQueue.equals(queue)) { - openVideoPlayer(); - return; - } - setInitialData(sid, videoUrl, title, queue); - if (player != null) { + public void selectAndLoadVideo(final int newServiceId, + @Nullable final String newUrl, + @NonNull final String newTitle, + @Nullable final PlayQueue newQueue) { + if (player != null && newQueue != null && playQueue != null + && !Objects.equals(newQueue.getItem(), playQueue.getItem())) { + // Preloading can be disabled since playback is surely being replaced. player.disablePreloadingOfCurrentTrack(); } + + setInitialData(newServiceId, newUrl, newTitle, newQueue); startLoading(false, true); } @@ -949,7 +962,7 @@ private void runWorker(final boolean forceLoad, final boolean addToBackStack) { playQueue = new SinglePlayQueue(result); } if (stack.isEmpty() || !stack.peek().getPlayQueue().equals(playQueue)) { - stack.push(new StackItem(serviceId, url, name, playQueue)); + stack.push(new StackItem(serviceId, url, title, playQueue)); } } if (isAutoplayEnabled()) { @@ -970,7 +983,7 @@ private void initTabs() { if (shouldShowComments()) { pageAdapter.addFragment( - CommentsFragment.getInstance(serviceId, url, name), COMMENTS_TAB_TAG); + CommentsFragment.getInstance(serviceId, url, title), COMMENTS_TAB_TAG); } if (showRelatedStreams && null == relatedStreamsLayout) { @@ -1061,7 +1074,7 @@ private void openPopupPlayer(final boolean append) { } } - private void openVideoPlayer() { + public void openVideoPlayer() { if (PreferenceManager.getDefaultSharedPreferences(activity) .getBoolean(this.getString(R.string.use_external_video_player_key), false)) { showExternalPlaybackDialog(); @@ -1087,7 +1100,7 @@ private void openNormalBackgroundPlayer(final boolean append) { private void openMainPlayer() { if (playerService == null) { - PlayerHolder.startService(App.getApp(), true, this); + PlayerHolder.startService(App.getApp(), autoPlayEnabled, this); return; } if (currentInfo == null) { @@ -1098,11 +1111,13 @@ private void openMainPlayer() { // Video view can have elements visible from popup, // We hide it here but once it ready the view will be shown in handleIntent() - playerService.getView().setVisibility(View.GONE); + if (playerService.getView() != null) { + playerService.getView().setVisibility(View.GONE); + } addVideoPlayerView(); final Intent playerIntent = NavigationHelper - .getPlayerIntent(requireContext(), MainPlayer.class, queue, null, true); + .getPlayerIntent(requireContext(), MainPlayer.class, queue, true, autoPlayEnabled); activity.startService(playerIntent); } @@ -1136,8 +1151,8 @@ private PlayQueue setupPlayQueueForIntent(final boolean append) { // Utils //////////////////////////////////////////////////////////////////////////*/ - public void setAutoplay(final boolean autoplay) { - this.autoPlayEnabled = autoplay; + public void setAutoPlay(final boolean autoPlay) { + this.autoPlayEnabled = autoPlay; } private void startOnExternalPlayer(@NonNull final Context context, @@ -1159,7 +1174,7 @@ private boolean isExternalPlayerEnabled() { .getBoolean(getString(R.string.use_external_video_player_key), false); } - // This method overrides default behaviour when setAutoplay() is called. + // This method overrides default behaviour when setAutoPlay() is called. // Don't auto play if the user selected an external player or disabled it in settings private boolean isAutoplayEnabled() { return autoPlayEnabled @@ -1239,9 +1254,9 @@ public boolean onPreDraw() { final DisplayMetrics metrics = getResources().getDisplayMetrics(); if (getView() != null) { - final int height = isInMultiWindow() - ? requireView().getHeight() - : activity.getWindow().getDecorView().getHeight(); + final int height = (isInMultiWindow() + ? requireView() + : activity.getWindow().getDecorView()).getHeight(); setHeightThumbnail(height, metrics); getView().getViewTreeObserver().removeOnPreDrawListener(preDrawListener); } @@ -1262,9 +1277,9 @@ private void setHeightThumbnail() { requireView().getViewTreeObserver().removeOnPreDrawListener(preDrawListener); if (player != null && player.isFullscreen()) { - final int height = isInMultiWindow() - ? requireView().getHeight() - : activity.getWindow().getDecorView().getHeight(); + final int height = (isInMultiWindow() + ? requireView() + : activity.getWindow().getDecorView()).getHeight(); // Height is zero when the view is not yet displayed like after orientation change if (height != 0) { setHeightThumbnail(height, metrics); @@ -1272,9 +1287,9 @@ private void setHeightThumbnail() { requireView().getViewTreeObserver().addOnPreDrawListener(preDrawListener); } } else { - final int height = isPortrait - ? (int) (metrics.widthPixels / (16.0f / 9.0f)) - : (int) (metrics.heightPixels / 2.0f); + final int height = (int) (isPortrait + ? metrics.widthPixels / (16.0f / 9.0f) + : metrics.heightPixels / 2.0f); setHeightThumbnail(height, metrics); } } @@ -1295,12 +1310,14 @@ private void showContent() { contentRootLayoutHiding.setVisibility(View.VISIBLE); } - protected void setInitialData(final int sid, final String u, final String title, - final PlayQueue queue) { - this.serviceId = sid; - this.url = u; - this.name = !TextUtils.isEmpty(title) ? title : ""; - this.playQueue = queue; + protected void setInitialData(final int newServiceId, + @Nullable final String newUrl, + @NonNull final String newTitle, + @Nullable final PlayQueue newPlayQueue) { + this.serviceId = newServiceId; + this.url = newUrl; + this.title = newTitle; + this.playQueue = newPlayQueue; } private void setErrorImage(final int imageResource) { @@ -1393,7 +1410,7 @@ public void showLoading() { animateView(detailPositionView, false, 100); animateView(positionView, false, 50); - videoTitleTextView.setText(name != null ? name : ""); + videoTitleTextView.setText(title); videoTitleTextView.setMaxLines(1); animateView(videoTitleTextView, true, 0); @@ -1438,7 +1455,7 @@ public void handleResult(@NonNull final StreamInfo info) { } } animateView(thumbnailPlayButton, true, 200); - videoTitleTextView.setText(name); + videoTitleTextView.setText(title); if (!TextUtils.isEmpty(info.getSubChannelName())) { displayBothUploaderAndSubChannel(info); @@ -1520,7 +1537,7 @@ public void handleResult(@NonNull final StreamInfo info) { if (info.getUploadDate() != null) { videoUploadDateView.setText(Localization - .localizeUploadDate(activity, info.getUploadDate().date().getTime())); + .localizeUploadDate(activity, info.getUploadDate().offsetDateTime())); videoUploadDateView.setVisibility(View.VISIBLE); } else { videoUploadDateView.setText(null); @@ -1563,7 +1580,8 @@ public void handleResult(@NonNull final StreamInfo info) { } 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 relatedStreamsLayout.setVisibility(View.INVISIBLE); @@ -1633,8 +1651,8 @@ protected boolean onError(final Throwable exception) { return true; } - final int errorId = exception instanceof YoutubeStreamExtractor.DecryptException - ? R.string.youtube_signature_decryption_error + final int errorId = exception instanceof YoutubeStreamExtractor.DeobfuscateException + ? R.string.youtube_signature_deobfuscation_error : exception instanceof ExtractionException ? R.string.parsing_error : R.string.general_error; @@ -1727,31 +1745,33 @@ private void showPlaybackProgress(final long progress, final long duration) { @Override public void onQueueUpdate(final PlayQueue queue) { playQueue = queue; + if (DEBUG) { + Log.d(TAG, "onQueueUpdate() called with: serviceId = [" + + serviceId + "], videoUrl = [" + url + "], name = [" + + title + "], playQueue = [" + playQueue + "]"); + } + // This should be the only place where we push data to stack. // It will allow to have live instance of PlayQueue with actual information about // deleted/added items inside Channel/Playlist queue and makes possible to have // a history of played items - if ((stack.isEmpty() || !stack.peek().getPlayQueue().equals(queue) - && queue.getItem() != null)) { - stack.push(new StackItem(queue.getItem().getServiceId(), - queue.getItem().getUrl(), - queue.getItem().getTitle(), - queue)); - } else { - final StackItem stackWithQueue = findQueueInStack(queue); - if (stackWithQueue != null) { - // On every MainPlayer service's destroy() playQueue gets disposed and - // no longer able to track progress. That's why we update our cached disposed - // queue with the new one that is active and have the same history. - // Without that the cached playQueue will have an old recovery position - stackWithQueue.setPlayQueue(queue); - } + @Nullable final StackItem stackPeek = stack.peek(); + if (stackPeek != null && !stackPeek.getPlayQueue().equals(queue)) { + @Nullable final PlayQueueItem playQueueItem = queue.getItem(); + if (playQueueItem != null) { + stack.push(new StackItem(playQueueItem.getServiceId(), playQueueItem.getUrl(), + playQueueItem.getTitle(), queue)); + return; + } // else continue below } - if (DEBUG) { - Log.d(TAG, "onQueueUpdate() called with: serviceId = [" - + serviceId + "], videoUrl = [" + url + "], name = [" - + name + "], playQueue = [" + playQueue + "]"); + @Nullable final StackItem stackWithQueue = findQueueInStack(queue); + if (stackWithQueue != null) { + // On every MainPlayer service's destroy() playQueue gets disposed and + // no longer able to track progress. That's why we update our cached disposed + // queue with the new one that is active and have the same history. + // Without that the cached playQueue will have an old recovery position + stackWithQueue.setPlayQueue(queue); } } @@ -1813,7 +1833,7 @@ public void onMetadataUpdate(final StreamInfo info, final PlayQueue queue) { currentInfo = info; setInitialData(info.getServiceId(), info.getUrl(), info.getName(), queue); - setAutoplay(false); + setAutoPlay(false); // Delay execution just because it freezes the main thread, and while playing // next/previous video you see visual glitches // (when non-vertical video goes after vertical video) @@ -2027,7 +2047,7 @@ private void setupBrightness() { private void checkLandscape() { if ((!player.isPlaying() && player.getPlayQueue() != playQueue) || player.getPlayQueue() == null) { - setAutoplay(true); + setAutoPlay(true); } player.checkLandscape(); @@ -2054,6 +2074,7 @@ private boolean wasCleared() { return url == null; } + @Nullable private StackItem findQueueInStack(final PlayQueue queue) { StackItem item = null; final Iterator iterator = stack.descendingIterator(); @@ -2074,8 +2095,7 @@ private void replaceQueueIfUserConfirms(final Runnable onAllow) { if (isClearingQueueConfirmationRequired(activity) && playerIsNotStopped() && activeQueue != null - && !activeQueue.equals(playQueue) - && activeQueue.getStreams().size() > 1) { + && !activeQueue.equals(playQueue)) { showClearingQueueConfirmation(onAllow); } else { onAllow.run(); @@ -2277,10 +2297,10 @@ public void onSlide(@NonNull final View bottomSheet, final float slideOffset) { }); } - private void updateOverlayData(@Nullable final String title, + private void updateOverlayData(@Nullable final String overlayTitle, @Nullable final String uploader, @Nullable final String thumbnailUrl) { - overlayTitleTextView.setText(TextUtils.isEmpty(title) ? "" : title); + overlayTitleTextView.setText(TextUtils.isEmpty(overlayTitle) ? "" : overlayTitle); overlayChannelTextView.setText(TextUtils.isEmpty(uploader) ? "" : uploader); overlayThumbnailImageView.setImageResource(R.drawable.dummy_thumbnail_dark); if (!TextUtils.isEmpty(thumbnailUrl)) { diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java index 37598eb1aec..41263bc34f5 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java @@ -6,7 +6,6 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.os.Bundle; -import androidx.preference.PreferenceManager; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; @@ -15,6 +14,7 @@ import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; +import androidx.preference.PreferenceManager; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -29,6 +29,7 @@ import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; import org.schabi.newpipe.info_list.InfoItemDialog; import org.schabi.newpipe.info_list.InfoListAdapter; +import org.schabi.newpipe.player.helper.PlayerHolder; import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.OnClickGesture; @@ -36,6 +37,8 @@ import org.schabi.newpipe.util.StreamDialogEntry; import org.schabi.newpipe.views.SuperScrollLayoutManager; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Queue; @@ -318,8 +321,9 @@ public void onScrolledDown(final RecyclerView recyclerView) { private void onStreamSelected(final StreamInfoItem selectedItem) { onItemSelected(selectedItem); - NavigationHelper.openVideoDetailFragment(getFM(), - selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName()); + NavigationHelper.openVideoDetailFragment(requireContext(), getFM(), + selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName(), + null, false); } protected void onScrollToBottom() { @@ -336,21 +340,26 @@ protected void showStreamDialog(final StreamInfoItem item) { return; } + final ArrayList entries = new ArrayList<>(); + + if (PlayerHolder.getType() != null) { + entries.add(StreamDialogEntry.enqueue); + } if (item.getStreamType() == StreamType.AUDIO_STREAM) { - StreamDialogEntry.setEnabledEntries( - StreamDialogEntry.enqueue_on_background, + entries.addAll(Arrays.asList( StreamDialogEntry.start_here_on_background, StreamDialogEntry.append_playlist, - StreamDialogEntry.share); - } else { - StreamDialogEntry.setEnabledEntries( - StreamDialogEntry.enqueue_on_background, - StreamDialogEntry.enqueue_on_popup, + StreamDialogEntry.share + )); + } else { + entries.addAll(Arrays.asList( StreamDialogEntry.start_here_on_background, StreamDialogEntry.start_here_on_popup, StreamDialogEntry.append_playlist, - StreamDialogEntry.share); + StreamDialogEntry.share + )); } + StreamDialogEntry.setEnabledEntries(entries); new InfoItemDialog(activity, item, StreamDialogEntry.getCommands(context), (dialog, which) -> StreamDialogEntry.clickOn(which, this, item)).show(); @@ -370,11 +379,7 @@ public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { final ActionBar supportActionBar = activity.getSupportActionBar(); if (supportActionBar != null) { supportActionBar.setDisplayShowTitleEnabled(true); - if (useAsFrontPage) { - supportActionBar.setDisplayHomeAsUpEnabled(false); - } else { - supportActionBar.setDisplayHomeAsUpEnabled(true); - } + supportActionBar.setDisplayHomeAsUpEnabled(!useAsFrontPage); } } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java index 8902834e4e3..6ec818909ef 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java @@ -50,7 +50,6 @@ import org.schabi.newpipe.util.ThemeHelper; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import java.util.concurrent.TimeUnit; @@ -495,13 +494,12 @@ public void handleResult(@NonNull final ChannelInfo result) { // handling ContentNotSupportedException not to show the error but an appropriate string // so that crashes won't be sent uselessly and the user will understand what happened - for (Iterator it = errors.iterator(); it.hasNext();) { - final Throwable throwable = it.next(); + errors.removeIf(throwable -> { if (throwable instanceof ContentNotSupportedException) { showContentNotSupported(); - it.remove(); } - } + return throwable instanceof ContentNotSupportedException; + }); if (!errors.isEmpty()) { showSnackBarError(errors, UserAction.REQUESTED_CHANNEL, @@ -519,7 +517,7 @@ public void handleResult(@NonNull final ChannelInfo result) { monitorSubscription(result); headerPlayAllButton.setOnClickListener(view -> NavigationHelper - .playOnMainPlayer(activity, getPlayQueue(), true)); + .playOnMainPlayer(activity, getPlayQueue())); headerPopupButton.setOnClickListener(view -> NavigationHelper .playOnPopupPlayer(activity, getPlayQueue(), false)); headerBackgroundButton.setOnClickListener(view -> NavigationHelper diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java index 38594553b13..71b51f9a13f 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java @@ -33,6 +33,7 @@ import org.schabi.newpipe.fragments.list.BaseListInfoFragment; import org.schabi.newpipe.info_list.InfoItemDialog; 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.PlaylistPlayQueue; import org.schabi.newpipe.report.ErrorActivity; @@ -46,6 +47,7 @@ import org.schabi.newpipe.util.ThemeHelper; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -151,25 +153,26 @@ protected void showStreamDialog(final StreamInfoItem item) { return; } + final ArrayList entries = new ArrayList<>(); + + if (PlayerHolder.getType() != null) { + entries.add(StreamDialogEntry.enqueue); + } if (item.getStreamType() == StreamType.AUDIO_STREAM) { - StreamDialogEntry.setEnabledEntries( - StreamDialogEntry.enqueue_on_background, + entries.addAll(Arrays.asList( StreamDialogEntry.start_here_on_background, StreamDialogEntry.append_playlist, - StreamDialogEntry.share); - } else { - StreamDialogEntry.setEnabledEntries( - StreamDialogEntry.enqueue_on_background, - StreamDialogEntry.enqueue_on_popup, + StreamDialogEntry.share + )); + } else { + entries.addAll(Arrays.asList( StreamDialogEntry.start_here_on_background, StreamDialogEntry.start_here_on_popup, StreamDialogEntry.append_playlist, - StreamDialogEntry.share); - - StreamDialogEntry.start_here_on_popup.setCustomAction((fragment, infoItem) -> - NavigationHelper.playOnPopupPlayer(context, - getPlayQueueStartingAt(infoItem), true)); + StreamDialogEntry.share + )); } + StreamDialogEntry.setEnabledEntries(entries); StreamDialogEntry.start_here_on_background.setCustomAction((fragment, infoItem) -> NavigationHelper.playOnBackgroundPlayer(context, @@ -316,7 +319,7 @@ public void handleResult(@NonNull final PlaylistInfo result) { .subscribe(getPlaylistBookmarkSubscriber()); headerPlayAllButton.setOnClickListener(view -> - NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), true)); + NavigationHelper.playOnMainPlayer(activity, getPlayQueue())); headerPopupButton.setOnClickListener(view -> NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false)); headerBackgroundButton.setOnClickListener(view -> diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java index 64eaf3a3d00..1e54176d476 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java @@ -5,8 +5,6 @@ import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; -import androidx.core.text.HtmlCompat; -import androidx.preference.PreferenceManager; import android.text.Editable; import android.text.Html; import android.text.TextUtils; @@ -30,6 +28,9 @@ import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AlertDialog; 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.RecyclerView; @@ -49,9 +50,9 @@ import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.report.UserAction; -import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.AnimationUtils; import org.schabi.newpipe.util.Constants; +import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.ExceptionUtils; import org.schabi.newpipe.util.ExtractorHelper; import org.schabi.newpipe.util.NavigationHelper; @@ -60,7 +61,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Queue; @@ -639,8 +639,8 @@ private void showKeyboardSearch() { } if (searchEditText.requestFocus()) { - final InputMethodManager imm = (InputMethodManager) activity.getSystemService( - Context.INPUT_METHOD_SERVICE); + final InputMethodManager imm = ContextCompat.getSystemService(activity, + InputMethodManager.class); imm.showSoftInput(searchEditText, InputMethodManager.SHOW_FORCED); } } @@ -653,8 +653,8 @@ private void hideKeyboardSearch() { return; } - final InputMethodManager imm = (InputMethodManager) activity - .getSystemService(Context.INPUT_METHOD_SERVICE); + final InputMethodManager imm = ContextCompat.getSystemService(activity, + InputMethodManager.class); imm.hideSoftInputFromWindow(searchEditText.getWindowToken(), InputMethodManager.RESULT_UNCHANGED_SHOWN); @@ -757,16 +757,9 @@ private void initSuggestionObserver() { } // Remove duplicates - final Iterator iterator = networkResult.iterator(); - while (iterator.hasNext() && localResult.size() > 0) { - final SuggestionItem next = iterator.next(); - for (final SuggestionItem item : localResult) { - if (item.query.equals(next.query)) { - iterator.remove(); - break; - } - } - } + networkResult.removeIf(networkItem -> + localResult.stream().anyMatch(localItem -> + localItem.query.equals(networkItem.query))); if (networkResult.size() > 0) { result.addAll(networkResult); diff --git a/app/src/main/java/org/schabi/newpipe/ktx/OffsetDateTime.kt b/app/src/main/java/org/schabi/newpipe/ktx/OffsetDateTime.kt new file mode 100644 index 00000000000..b3df83c2546 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/ktx/OffsetDateTime.kt @@ -0,0 +1,10 @@ +package org.schabi.newpipe.ktx + +import java.time.OffsetDateTime +import java.time.ZoneId +import java.util.Calendar +import java.util.GregorianCalendar + +fun OffsetDateTime.toCalendar(zoneId: ZoneId = ZoneId.systemDefault()): Calendar { + return GregorianCalendar.from(if (zoneId != offset) atZoneSameInstant(zoneId) else toZonedDateTime()) +} diff --git a/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java b/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java index 5b67f51da9d..da8902c083c 100644 --- a/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java @@ -26,7 +26,8 @@ import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.OnClickGesture; -import java.text.DateFormat; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; import java.util.ArrayList; import java.util.List; @@ -69,7 +70,7 @@ public class LocalItemListAdapter extends RecyclerView.Adapter localItems; private final HistoryRecordManager recordManager; - private final DateFormat dateFormat; + private final DateTimeFormatter dateTimeFormatter; private boolean showFooter = false; private boolean useGridVariant = false; @@ -80,8 +81,8 @@ public LocalItemListAdapter(final Context context) { recordManager = new HistoryRecordManager(context); localItemBuilder = new LocalItemBuilder(context); localItems = new ArrayList<>(); - dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, - Localization.getPreferredLocale(context)); + dateTimeFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT) + .withLocale(Localization.getPreferredLocale(context)); } public void setSelectedListener(final OnClickGesture listener) { @@ -303,7 +304,7 @@ public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, int } ((LocalItemHolder) holder) - .updateFromItem(localItems.get(position), recordManager, dateFormat); + .updateFromItem(localItems.get(position), recordManager, dateTimeFormatter); } else if (holder instanceof HeaderFooterHolder && position == 0 && header != null) { ((HeaderFooterHolder) holder).view = header; } else if (holder instanceof HeaderFooterHolder && position == sizeConsideringHeader() diff --git a/app/src/main/java/org/schabi/newpipe/local/dialog/PlaylistAppendDialog.java b/app/src/main/java/org/schabi/newpipe/local/dialog/PlaylistAppendDialog.java index 5bb03f531dd..7a63bee3053 100644 --- a/app/src/main/java/org/schabi/newpipe/local/dialog/PlaylistAppendDialog.java +++ b/app/src/main/java/org/schabi/newpipe/local/dialog/PlaylistAppendDialog.java @@ -1,5 +1,6 @@ package org.schabi.newpipe.local.dialog; +import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -29,6 +30,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.disposables.Disposable; public final class PlaylistAppendDialog extends PlaylistDialog { private static final String TAG = PlaylistAppendDialog.class.getCanonicalName(); @@ -38,6 +40,23 @@ public final class PlaylistAppendDialog extends PlaylistDialog { 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) { final PlaylistAppendDialog dialog = new PlaylistAppendDialog(); dialog.setInfo(Collections.singletonList(new StreamEntity(info))); @@ -136,11 +155,6 @@ public void openCreatePlaylistDialog() { } private void onPlaylistsReceived(@NonNull final List playlists) { - if (playlists.isEmpty()) { - openCreatePlaylistDialog(); - return; - } - if (playlistAdapter != null && playlistRecyclerView != null) { playlistAdapter.clearStreamItemList(); playlistAdapter.addItems(playlists); diff --git a/app/src/main/java/org/schabi/newpipe/local/dialog/PlaylistCreationDialog.java b/app/src/main/java/org/schabi/newpipe/local/dialog/PlaylistCreationDialog.java index ff696664401..55782b45253 100644 --- a/app/src/main/java/org/schabi/newpipe/local/dialog/PlaylistCreationDialog.java +++ b/app/src/main/java/org/schabi/newpipe/local/dialog/PlaylistCreationDialog.java @@ -26,6 +26,12 @@ public static PlaylistCreationDialog newInstance(final List stream return dialog; } + public static PlaylistCreationDialog newInstance(final PlaylistAppendDialog appendDialog) { + final PlaylistCreationDialog dialog = new PlaylistCreationDialog(); + dialog.setInfo(appendDialog.getStreams()); + return dialog; + } + /*////////////////////////////////////////////////////////////////////////// // Dialog //////////////////////////////////////////////////////////////////////////*/ diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt index d319c9fa345..8bd8c198627 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt @@ -7,8 +7,9 @@ import io.reactivex.Flowable import io.reactivex.Maybe import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers -import java.util.Calendar -import java.util.Date +import java.time.LocalDate +import java.time.OffsetDateTime +import java.time.ZoneOffset import org.schabi.newpipe.MainActivity.DEBUG import org.schabi.newpipe.NewPipeDatabase import org.schabi.newpipe.database.feed.model.FeedEntity @@ -29,13 +30,8 @@ class FeedDatabaseManager(context: Context) { /** * Only items that are newer than this will be saved. */ - val FEED_OLDEST_ALLOWED_DATE: Calendar = Calendar.getInstance().apply { - add(Calendar.WEEK_OF_YEAR, -13) - set(Calendar.HOUR_OF_DAY, 0) - set(Calendar.MINUTE, 0) - set(Calendar.SECOND, 0) - set(Calendar.MILLISECOND, 0) - } + val FEED_OLDEST_ALLOWED_DATE: OffsetDateTime = LocalDate.now().minusWeeks(13) + .atStartOfDay().atOffset(ZoneOffset.UTC) } fun groups() = feedGroupTable.getAll() @@ -50,12 +46,12 @@ class FeedDatabaseManager(context: Context) { return streams.map> { val items = ArrayList(it.size) - for (streamEntity in it) items.add(streamEntity.toStreamInfoItem()) + it.mapTo(items) { it.toStreamInfoItem() } return@map items } } - fun outdatedSubscriptions(outdatedThreshold: Date) = feedTable.getAllOutdated(outdatedThreshold) + fun outdatedSubscriptions(outdatedThreshold: OffsetDateTime) = feedTable.getAllOutdated(outdatedThreshold) fun notLoadedCount(groupId: Long = FeedGroupEntity.GROUP_ALL_ID): Flowable { return when (groupId) { @@ -64,7 +60,7 @@ class FeedDatabaseManager(context: Context) { } } - fun outdatedSubscriptionsForGroup(groupId: Long = FeedGroupEntity.GROUP_ALL_ID, outdatedThreshold: Date) = + fun outdatedSubscriptionsForGroup(groupId: Long = FeedGroupEntity.GROUP_ALL_ID, outdatedThreshold: OffsetDateTime) = feedTable.getAllOutdatedForGroup(groupId, outdatedThreshold) fun markAsOutdated(subscriptionId: Long) = feedTable @@ -73,7 +69,7 @@ class FeedDatabaseManager(context: Context) { fun upsertAll( subscriptionId: Long, items: List, - oldestAllowedDate: Date = FEED_OLDEST_ALLOWED_DATE.time + oldestAllowedDate: OffsetDateTime = FEED_OLDEST_ALLOWED_DATE ) { val itemsToInsert = ArrayList() loop@ for (streamItem in items) { @@ -81,7 +77,7 @@ class FeedDatabaseManager(context: Context) { itemsToInsert += when { uploadDate == null && streamItem.streamType == StreamType.LIVE_STREAM -> streamItem - uploadDate != null && uploadDate.date().time >= oldestAllowedDate -> streamItem + uploadDate != null && uploadDate.offsetDateTime() >= oldestAllowedDate -> streamItem else -> continue@loop } } @@ -96,10 +92,11 @@ class FeedDatabaseManager(context: Context) { feedTable.insertAll(feedEntities) } - feedTable.setLastUpdatedForSubscription(FeedLastUpdatedEntity(subscriptionId, Calendar.getInstance().time)) + feedTable.setLastUpdatedForSubscription(FeedLastUpdatedEntity(subscriptionId, + OffsetDateTime.now(ZoneOffset.UTC))) } - fun removeOrphansOrOlderStreams(oldestAllowedDate: Date = FEED_OLDEST_ALLOWED_DATE.time) { + fun removeOrphansOrOlderStreams(oldestAllowedDate: OffsetDateTime = FEED_OLDEST_ALLOWED_DATE) { feedTable.unlinkStreamsOlderThan(oldestAllowedDate) streamTable.deleteOrphans() } @@ -159,7 +156,7 @@ class FeedDatabaseManager(context: Context) { .observeOn(AndroidSchedulers.mainThread()) } - fun oldestSubscriptionUpdate(groupId: Long): Flowable> { + fun oldestSubscriptionUpdate(groupId: Long): Flowable> { return when (groupId) { FeedGroupEntity.GROUP_ALL_ID -> feedTable.oldestSubscriptionUpdateFromAll() else -> feedTable.oldestSubscriptionUpdate(groupId) diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt index 915bc3ec063..e4d10fb6142 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt @@ -29,6 +29,8 @@ import android.view.MenuItem import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AlertDialog +import androidx.core.os.bundleOf +import androidx.core.view.isVisible import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider import androidx.preference.PreferenceManager @@ -253,11 +255,9 @@ class FeedFragment : BaseListFragment() { oldestSubscriptionUpdate = loadedState.oldestUpdate + refresh_subtitle_text.isVisible = 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) - } else { - refresh_subtitle_text.visibility = View.GONE } if (loadedState.itemsErrors.isNotEmpty()) { @@ -330,12 +330,7 @@ class FeedFragment : BaseListFragment() { @JvmStatic fun newInstance(groupId: Long = FeedGroupEntity.GROUP_ALL_ID, groupName: String? = null): FeedFragment { val feedFragment = FeedFragment() - - feedFragment.arguments = Bundle().apply { - putLong(KEY_GROUP_ID, groupId) - putString(KEY_GROUP_NAME, groupName) - } - + feedFragment.arguments = bundleOf(KEY_GROUP_ID to groupId, KEY_GROUP_NAME to groupName) return feedFragment } } diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedViewModel.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedViewModel.kt index da2b5ffa4d2..13c3183da9e 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedViewModel.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedViewModel.kt @@ -9,11 +9,11 @@ import io.reactivex.Flowable import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.functions.Function4 import io.reactivex.schedulers.Schedulers -import java.util.Calendar -import java.util.Date +import java.time.OffsetDateTime import java.util.concurrent.TimeUnit import org.schabi.newpipe.database.feed.model.FeedGroupEntity import org.schabi.newpipe.extractor.stream.StreamInfoItem +import org.schabi.newpipe.ktx.toCalendar import org.schabi.newpipe.local.feed.service.FeedEventManager import org.schabi.newpipe.local.feed.service.FeedEventManager.Event.ErrorResultEvent import org.schabi.newpipe.local.feed.service.FeedEventManager.Event.IdleEvent @@ -41,7 +41,7 @@ class FeedViewModel(applicationContext: Context, val groupId: Long = FeedGroupEn feedDatabaseManager.notLoadedCount(groupId), feedDatabaseManager.oldestSubscriptionUpdate(groupId), - Function4 { t1: FeedEventManager.Event, t2: List, t3: Long, t4: List -> + Function4 { t1: FeedEventManager.Event, t2: List, t3: Long, t4: List -> return@Function4 CombineResultHolder(t1, t2, t3, t4.firstOrNull()) } ) @@ -51,8 +51,7 @@ class FeedViewModel(applicationContext: Context, val groupId: Long = FeedGroupEn .subscribe { val (event, listFromDB, notLoadedCount, oldestUpdate) = it - val oldestUpdateCalendar = - oldestUpdate?.let { Calendar.getInstance().apply { time = it } } + val oldestUpdateCalendar = oldestUpdate?.toCalendar() mutableStateLiveData.postValue(when (event) { is IdleEvent -> FeedState.LoadedState(listFromDB, oldestUpdateCalendar, notLoadedCount) @@ -71,5 +70,5 @@ class FeedViewModel(applicationContext: Context, val groupId: Long = FeedGroupEn combineDisposable.dispose() } - private data class CombineResultHolder(val t1: FeedEventManager.Event, val t2: List, val t3: Long, val t4: Date?) + private data class CombineResultHolder(val t1: FeedEventManager.Event, val t2: List, val t3: Long, val t4: OffsetDateTime?) } diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadService.kt b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadService.kt index 0181f2711ad..8d3afbc7e49 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadService.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadService.kt @@ -41,7 +41,8 @@ import io.reactivex.functions.Function import io.reactivex.processors.PublishProcessor import io.reactivex.schedulers.Schedulers import java.io.IOException -import java.util.Calendar +import java.time.OffsetDateTime +import java.time.ZoneOffset import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicInteger @@ -161,8 +162,8 @@ class FeedLoadService : Service() { companion object { fun wrapList(subscriptionId: Long, info: ListInfo): List { val toReturn = ArrayList(info.errors.size) - for (error in info.errors) { - toReturn.add(RequestException(subscriptionId, info.serviceId.toString() + ":" + info.url, error)) + info.errors.mapTo(toReturn) { + RequestException(subscriptionId, info.serviceId.toString() + ":" + info.url, it) } return toReturn } @@ -172,9 +173,7 @@ class FeedLoadService : Service() { private fun startLoading(groupId: Long = FeedGroupEntity.GROUP_ALL_ID, useFeedExtractor: Boolean, thresholdOutdatedSeconds: Int) { feedResultsHolder = ResultsHolder() - val outdatedThreshold = Calendar.getInstance().apply { - add(Calendar.SECOND, -thresholdOutdatedSeconds) - }.time + val outdatedThreshold = OffsetDateTime.now(ZoneOffset.UTC).minusSeconds(thresholdOutdatedSeconds.toLong()) val subscriptions = when (groupId) { FeedGroupEntity.GROUP_ALL_ID -> feedDatabaseManager.outdatedSubscriptions(outdatedThreshold) diff --git a/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java b/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java index 01d909c3c17..7f5c4f7a70d 100644 --- a/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java +++ b/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java @@ -20,9 +20,9 @@ import android.content.Context; import android.content.SharedPreferences; -import androidx.preference.PreferenceManager; import androidx.annotation.NonNull; +import androidx.preference.PreferenceManager; import org.schabi.newpipe.NewPipeDatabase; import org.schabi.newpipe.R; @@ -44,9 +44,10 @@ import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.player.playqueue.PlayQueueItem; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; import java.util.ArrayList; import java.util.Collection; -import java.util.Date; import java.util.List; import io.reactivex.Completable; @@ -85,7 +86,7 @@ public Maybe onViewed(final StreamInfo info) { return Maybe.empty(); } - final Date currentTime = new Date(); + final OffsetDateTime currentTime = OffsetDateTime.now(ZoneOffset.UTC); return Maybe.fromCallable(() -> database.runInTransaction(() -> { final long streamId = streamTable.upsert(new StreamEntity(info)); final StreamHistoryEntity latestEntry = streamHistoryTable.getLatestEntry(streamId); @@ -101,9 +102,11 @@ public Maybe onViewed(final StreamInfo info) { })).subscribeOn(Schedulers.io()); } - public Single deleteStreamHistory(final long streamId) { - return Single.fromCallable(() -> streamHistoryTable.deleteStreamHistory(streamId)) - .subscribeOn(Schedulers.io()); + public Completable deleteStreamHistoryAndState(final long streamId) { + return Completable.fromAction(() -> { + streamStateTable.deleteState(streamId); + streamHistoryTable.deleteStreamHistory(streamId); + }).subscribeOn(Schedulers.io()); } public Single deleteWholeStreamHistory() { @@ -111,7 +114,7 @@ public Single deleteWholeStreamHistory() { .subscribeOn(Schedulers.io()); } - public Single deleteCompelteStreamStateHistory() { + public Single deleteCompleteStreamStateHistory() { return Single.fromCallable(streamStateTable::deleteAll) .subscribeOn(Schedulers.io()); } @@ -159,7 +162,7 @@ public Maybe onSearched(final int serviceId, final String search) { return Maybe.empty(); } - final Date currentTime = new Date(); + final OffsetDateTime currentTime = OffsetDateTime.now(ZoneOffset.UTC); final SearchHistoryEntry newEntry = new SearchHistoryEntry(currentTime, serviceId, search); return Maybe.fromCallable(() -> database.runInTransaction(() -> { diff --git a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java index 887e5d12494..8cd4e4c7e29 100644 --- a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java @@ -25,10 +25,12 @@ import org.schabi.newpipe.R; import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.database.stream.StreamStatisticsEntry; +import org.schabi.newpipe.database.stream.model.StreamEntity; import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.info_list.InfoItemDialog; 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.SinglePlayQueue; import org.schabi.newpipe.report.ErrorActivity; @@ -40,7 +42,9 @@ import org.schabi.newpipe.util.ThemeHelper; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; import java.util.List; import icepick.State; @@ -66,18 +70,19 @@ public class StatisticsPlaylistFragment private HistoryRecordManager recordManager; private List processResult(final List results) { + final Comparator comparator; switch (sortMode) { case LAST_PLAYED: - Collections.sort(results, (left, right) -> - right.getLatestAccessDate().compareTo(left.getLatestAccessDate())); - return results; + comparator = Comparator.comparing(StreamStatisticsEntry::getLatestAccessDate); + break; case MOST_PLAYED: - Collections.sort(results, (left, right) -> - Long.compare(right.getWatchCount(), left.getWatchCount())); - return results; + comparator = Comparator.comparingLong(StreamStatisticsEntry::getWatchCount); + break; default: return null; } + Collections.sort(results, comparator.reversed()); + return results; } /////////////////////////////////////////////////////////////////////////// @@ -145,11 +150,10 @@ protected void initListeners() { @Override public void selected(final LocalItem selectedItem) { if (selectedItem instanceof StreamStatisticsEntry) { - final StreamStatisticsEntry item = (StreamStatisticsEntry) selectedItem; - NavigationHelper.openVideoDetailFragment(getFM(), - item.getStreamEntity().getServiceId(), - item.getStreamEntity().getUrl(), - item.getStreamEntity().getTitle()); + final StreamEntity item = + ((StreamStatisticsEntry) selectedItem).getStreamEntity(); + NavigationHelper.openVideoDetailFragment(requireContext(), getFM(), + item.getServiceId(), item.getUrl(), item.getTitle(), null, false); } } @@ -321,7 +325,7 @@ public void handleResult(@NonNull final List result) { } headerPlayAllButton.setOnClickListener(view -> - NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), true)); + NavigationHelper.playOnMainPlayer(activity, getPlayQueue())); headerPopupButton.setOnClickListener(view -> NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false)); headerBackgroundButton.setOnClickListener(view -> @@ -387,27 +391,28 @@ private void showStreamDialog(final StreamStatisticsEntry item) { } final StreamInfoItem infoItem = item.toStreamInfoItem(); + final ArrayList entries = new ArrayList<>(); + + if (PlayerHolder.getType() != null) { + entries.add(StreamDialogEntry.enqueue); + } if (infoItem.getStreamType() == StreamType.AUDIO_STREAM) { - StreamDialogEntry.setEnabledEntries( - StreamDialogEntry.enqueue_on_background, + entries.addAll(Arrays.asList( StreamDialogEntry.start_here_on_background, StreamDialogEntry.delete, StreamDialogEntry.append_playlist, - StreamDialogEntry.share); - } else { - StreamDialogEntry.setEnabledEntries( - StreamDialogEntry.enqueue_on_background, - StreamDialogEntry.enqueue_on_popup, + StreamDialogEntry.share + )); + } else { + entries.addAll(Arrays.asList( StreamDialogEntry.start_here_on_background, StreamDialogEntry.start_here_on_popup, StreamDialogEntry.delete, StreamDialogEntry.append_playlist, - StreamDialogEntry.share); - - StreamDialogEntry.start_here_on_popup.setCustomAction((fragment, infoItemDuplicate) -> - NavigationHelper - .playOnPopupPlayer(context, getPlayQueueStartingAt(item), true)); + StreamDialogEntry.share + )); } + StreamDialogEntry.setEnabledEntries(entries); StreamDialogEntry.start_here_on_background.setCustomAction((fragment, infoItemDuplicate) -> NavigationHelper @@ -420,14 +425,14 @@ private void showStreamDialog(final StreamStatisticsEntry item) { } private void deleteEntry(final int index) { - final LocalItem infoItem = itemListAdapter.getItemsList() - .get(index); + final LocalItem infoItem = itemListAdapter.getItemsList().get(index); if (infoItem instanceof StreamStatisticsEntry) { final StreamStatisticsEntry entry = (StreamStatisticsEntry) infoItem; - final Disposable onDelete = recordManager.deleteStreamHistory(entry.getStreamId()) + final Disposable onDelete = recordManager + .deleteStreamHistoryAndState(entry.getStreamId()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( - howManyDeleted -> { + () -> { if (getView() != null) { Snackbar.make(getView(), R.string.one_item_deleted, Snackbar.LENGTH_SHORT).show(); diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalItemHolder.java index c4307fcde10..a093d93e1a0 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalItemHolder.java @@ -9,7 +9,7 @@ import org.schabi.newpipe.local.LocalItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; -import java.text.DateFormat; +import java.time.format.DateTimeFormatter; /* * Created by Christian Schabesberger on 12.02.17. @@ -41,7 +41,7 @@ public LocalItemHolder(final LocalItemBuilder itemBuilder, final int layoutId, } public abstract void updateFromItem(LocalItem item, HistoryRecordManager historyRecordManager, - DateFormat dateFormat); + DateTimeFormatter dateTimeFormatter); public void updateState(final LocalItem localItem, final HistoryRecordManager historyRecordManager) { } diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java index 458b3c30eae..5560df3e068 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java @@ -10,7 +10,7 @@ import org.schabi.newpipe.util.ImageDisplayConstants; import org.schabi.newpipe.util.Localization; -import java.text.DateFormat; +import java.time.format.DateTimeFormatter; public class LocalPlaylistItemHolder extends PlaylistItemHolder { public LocalPlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final ViewGroup parent) { @@ -25,7 +25,7 @@ public LocalPlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final Vie @Override public void updateFromItem(final LocalItem localItem, final HistoryRecordManager historyRecordManager, - final DateFormat dateFormat) { + final DateTimeFormatter dateTimeFormatter) { if (!(localItem instanceof PlaylistMetadataEntry)) { return; } @@ -39,6 +39,6 @@ public void updateFromItem(final LocalItem localItem, itemBuilder.displayImage(item.thumbnailUrl, itemThumbnailView, ImageDisplayConstants.DISPLAY_PLAYLIST_OPTIONS); - super.updateFromItem(localItem, historyRecordManager, dateFormat); + super.updateFromItem(localItem, historyRecordManager, dateTimeFormatter); } } diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamItemHolder.java index 331149e17e6..f7cf6970878 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamItemHolder.java @@ -20,7 +20,7 @@ import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.views.AnimatedProgressBar; -import java.text.DateFormat; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.concurrent.TimeUnit; @@ -52,7 +52,7 @@ public LocalPlaylistStreamItemHolder(final LocalItemBuilder infoItemBuilder, @Override public void updateFromItem(final LocalItem localItem, final HistoryRecordManager historyRecordManager, - final DateFormat dateFormat) { + final DateTimeFormatter dateTimeFormatter) { if (!(localItem instanceof PlaylistStreamEntry)) { return; } @@ -104,7 +104,6 @@ public void updateFromItem(final LocalItem localItem, return true; }); - itemThumbnailView.setOnTouchListener(getOnTouchListener(item)); itemHandleView.setOnTouchListener(getOnTouchListener(item)); } diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamItemHolder.java index 8eaef807a8a..f473b027739 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamItemHolder.java @@ -20,7 +20,7 @@ import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.views.AnimatedProgressBar; -import java.text.DateFormat; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.concurrent.TimeUnit; @@ -71,10 +71,10 @@ public LocalStatisticStreamItemHolder(final LocalItemBuilder itemBuilder, } private String getStreamInfoDetailLine(final StreamStatisticsEntry entry, - final DateFormat dateFormat) { + final DateTimeFormatter dateTimeFormatter) { final String watchCount = Localization .shortViewCount(itemBuilder.getContext(), entry.getWatchCount()); - final String uploadDate = dateFormat.format(entry.getLatestAccessDate()); + final String uploadDate = dateTimeFormatter.format(entry.getLatestAccessDate()); final String serviceName = NewPipe.getNameOfService(entry.getStreamEntity().getServiceId()); return Localization.concatenateStrings(watchCount, uploadDate, serviceName); } @@ -82,7 +82,7 @@ private String getStreamInfoDetailLine(final StreamStatisticsEntry entry, @Override public void updateFromItem(final LocalItem localItem, final HistoryRecordManager historyRecordManager, - final DateFormat dateFormat) { + final DateTimeFormatter dateTimeFormatter) { if (!(localItem instanceof StreamStatisticsEntry)) { return; } @@ -116,7 +116,7 @@ public void updateFromItem(final LocalItem localItem, } if (itemAdditionalDetails != null) { - itemAdditionalDetails.setText(getStreamInfoDetailLine(item, dateFormat)); + itemAdditionalDetails.setText(getStreamInfoDetailLine(item, dateTimeFormatter)); } // Default thumbnail is shown on error, while loading and if the url is empty diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/PlaylistItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/PlaylistItemHolder.java index 11e3deb6738..e8c53161e9b 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/PlaylistItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/PlaylistItemHolder.java @@ -9,7 +9,7 @@ import org.schabi.newpipe.local.LocalItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; -import java.text.DateFormat; +import java.time.format.DateTimeFormatter; public abstract class PlaylistItemHolder extends LocalItemHolder { public final ImageView itemThumbnailView; @@ -34,7 +34,7 @@ public PlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final ViewGrou @Override public void updateFromItem(final LocalItem localItem, final HistoryRecordManager historyRecordManager, - final DateFormat dateFormat) { + final DateTimeFormatter dateTimeFormatter) { itemView.setOnClickListener(view -> { if (itemBuilder.getOnItemSelectedListener() != null) { itemBuilder.getOnItemSelectedListener().selected(localItem); diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java index a47d61d2f72..a39e3cecb5b 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java @@ -11,7 +11,7 @@ import org.schabi.newpipe.util.ImageDisplayConstants; import org.schabi.newpipe.util.Localization; -import java.text.DateFormat; +import java.time.format.DateTimeFormatter; public class RemotePlaylistItemHolder extends PlaylistItemHolder { public RemotePlaylistItemHolder(final LocalItemBuilder infoItemBuilder, @@ -27,7 +27,7 @@ public RemotePlaylistItemHolder(final LocalItemBuilder infoItemBuilder, @Override public void updateFromItem(final LocalItem localItem, final HistoryRecordManager historyRecordManager, - final DateFormat dateFormat) { + final DateTimeFormatter dateTimeFormatter) { if (!(localItem instanceof PlaylistRemoteEntity)) { return; } @@ -48,6 +48,6 @@ public void updateFromItem(final LocalItem localItem, itemBuilder.displayImage(item.getThumbnailUrl(), itemThumbnailView, ImageDisplayConstants.DISPLAY_PLAYLIST_OPTIONS); - super.updateFromItem(localItem, historyRecordManager, dateFormat); + super.updateFromItem(localItem, historyRecordManager, dateTimeFormatter); } } diff --git a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java index 3b66fd73f14..71df07a4b50 100644 --- a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java @@ -30,12 +30,14 @@ import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.database.history.model.StreamHistoryEntry; import org.schabi.newpipe.database.playlist.PlaylistStreamEntry; +import org.schabi.newpipe.database.stream.model.StreamEntity; import org.schabi.newpipe.database.stream.model.StreamStateEntity; import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.info_list.InfoItemDialog; import org.schabi.newpipe.local.BaseLocalListFragment; 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.SinglePlayQueue; import org.schabi.newpipe.report.UserAction; @@ -45,6 +47,7 @@ import org.schabi.newpipe.util.StreamDialogEntry; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -176,10 +179,10 @@ protected void initListeners() { @Override public void selected(final LocalItem selectedItem) { if (selectedItem instanceof PlaylistStreamEntry) { - final PlaylistStreamEntry item = (PlaylistStreamEntry) selectedItem; - NavigationHelper.openVideoDetailFragment(getFM(), - item.getStreamEntity().getServiceId(), item.getStreamEntity().getUrl(), - item.getStreamEntity().getTitle()); + final StreamEntity item = + ((PlaylistStreamEntry) selectedItem).getStreamEntity(); + NavigationHelper.openVideoDetailFragment(requireContext(), getFM(), + item.getServiceId(), item.getUrl(), item.getTitle(), null, false); } } @@ -492,7 +495,7 @@ public void handleResult(@NonNull final List result) { setVideoCount(itemListAdapter.getItemsList().size()); headerPlayAllButton.setOnClickListener(view -> - NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), true)); + NavigationHelper.playOnMainPlayer(activity, getPlayQueue())); headerPopupButton.setOnClickListener(view -> NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false)); headerBackgroundButton.setOnClickListener(view -> @@ -756,29 +759,30 @@ protected void showStreamItemDialog(final PlaylistStreamEntry item) { } final StreamInfoItem infoItem = item.toStreamInfoItem(); + final ArrayList entries = new ArrayList<>(); + + if (PlayerHolder.getType() != null) { + entries.add(StreamDialogEntry.enqueue); + } if (infoItem.getStreamType() == StreamType.AUDIO_STREAM) { - StreamDialogEntry.setEnabledEntries( - StreamDialogEntry.enqueue_on_background, + entries.addAll(Arrays.asList( StreamDialogEntry.start_here_on_background, StreamDialogEntry.set_as_playlist_thumbnail, StreamDialogEntry.delete, StreamDialogEntry.append_playlist, - StreamDialogEntry.share); - } else { - StreamDialogEntry.setEnabledEntries( - StreamDialogEntry.enqueue_on_background, - StreamDialogEntry.enqueue_on_popup, + StreamDialogEntry.share + )); + } else { + entries.addAll(Arrays.asList( StreamDialogEntry.start_here_on_background, StreamDialogEntry.start_here_on_popup, StreamDialogEntry.set_as_playlist_thumbnail, StreamDialogEntry.delete, StreamDialogEntry.append_playlist, - StreamDialogEntry.share); - - StreamDialogEntry.start_here_on_popup.setCustomAction( - (fragment, infoItemDuplicate) -> NavigationHelper. - playOnPopupPlayer(context, getPlayQueueStartingAt(item), true)); + StreamDialogEntry.share + )); } + StreamDialogEntry.setEnabledEntries(entries); StreamDialogEntry.start_here_on_background.setCustomAction((fragment, infoItemDuplicate) -> NavigationHelper.playOnBackgroundPlayer(context, diff --git a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistManager.java b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistManager.java index 2dec53fae44..d6101eb1d90 100644 --- a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistManager.java +++ b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistManager.java @@ -126,4 +126,10 @@ private Maybe modifyPlaylist(final long playlistId, }).subscribeOn(Schedulers.io()); } + public Maybe hasPlaylists() { + return playlistTable.getCount() + .firstElement() + .map(count -> count > 0) + .subscribeOn(Schedulers.io()); + } } diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialog.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialog.kt index 5e9b6b6a49d..28ffa626560 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialog.kt +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialog.kt @@ -1,17 +1,19 @@ package org.schabi.newpipe.local.subscription.dialog import android.app.Dialog -import android.content.Context import android.os.Bundle import android.os.Parcelable -import android.text.Editable import android.text.TextUtils -import android.text.TextWatcher import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.inputmethod.InputMethodManager 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.lifecycle.Observer import androidx.lifecycle.ViewModelProvider @@ -191,16 +193,11 @@ class FeedGroupDialog : DialogFragment(), BackPressable { } group_name_input_container.error = null - group_name_input.addTextChangedListener(object : TextWatcher { - override fun afterTextChanged(s: Editable?) {} - override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} - - 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 - } + group_name_input.doOnTextChanged { text, _, _, _ -> + if (group_name_input_container.isErrorEnabled && !text.isNullOrBlank()) { + group_name_input_container.error = null } - }) + } confirm_button.setOnClickListener { handlePositiveButton() } @@ -242,15 +239,11 @@ class FeedGroupDialog : DialogFragment(), BackPressable { } } - toolbar_search_edit_text.addTextChangedListener(object : TextWatcher { - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) = Unit - override fun afterTextChanged(s: Editable) = Unit - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - val newQuery: String = toolbar_search_edit_text.text.toString() - subscriptionsCurrentSearchQuery = newQuery - viewModel.filterSubscriptionsBy(newQuery) - } - }) + toolbar_search_edit_text.doOnTextChanged { _, _, _, _ -> + val newQuery: String = toolbar_search_edit_text.text.toString() + subscriptionsCurrentSearchQuery = newQuery + viewModel.filterSubscriptionsBy(newQuery) + } subscriptionGroupAdapter.setOnItemClickListener(subscriptionPickerItemListener) } @@ -414,21 +407,14 @@ class FeedGroupDialog : DialogFragment(), BackPressable { else -> android.R.string.ok }) - delete_button.visibility = when { - currentScreen != InitialScreen -> View.GONE - groupId == NO_GROUP_SELECTED -> View.GONE - else -> View.VISIBLE - } + delete_button.isGone = currentScreen != InitialScreen || groupId == NO_GROUP_SELECTED hideKeyboard() hideSearch() } private fun View.onlyVisibleIn(vararg screens: ScreenState) { - visibility = when (currentScreen) { - in screens -> View.VISIBLE - else -> View.GONE - } + isVisible = currentScreen in screens } /*/​////////////////////////////////////////////////////////////////////////// @@ -459,7 +445,7 @@ class FeedGroupDialog : DialogFragment(), BackPressable { } private val inputMethodManager by lazy { - requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + requireActivity().getSystemService()!! } private fun showKeyboardSearch() { @@ -501,11 +487,7 @@ class FeedGroupDialog : DialogFragment(), BackPressable { fun newInstance(groupId: Long = NO_GROUP_SELECTED): FeedGroupDialog { val dialog = FeedGroupDialog() - - dialog.arguments = Bundle().apply { - putLong(KEY_GROUP_ID, groupId) - } - + dialog.arguments = bundleOf(KEY_GROUP_ID to groupId) return dialog } } diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialogViewModel.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialogViewModel.kt index e9a7e4eb70a..4b1a4df5e14 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialogViewModel.kt +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialogViewModel.kt @@ -37,8 +37,8 @@ class FeedGroupDialogViewModel( BiFunction { t1: String, t2: Boolean -> Filter(t1, t2) } ) .distinctUntilChanged() - .switchMap { filter -> - subscriptionManager.getSubscriptions(groupId, filter.query, filter.showOnlyUngrouped) + .switchMap { (query, showOnlyUngrouped) -> + subscriptionManager.getSubscriptions(groupId, query, showOnlyUngrouped) }.map { list -> list.map { PickerSubscriptionItem(it) } } private val mutableGroupLiveData = MutableLiveData() diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/item/HeaderWithMenuItem.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/item/HeaderWithMenuItem.kt index 32493225610..142bba433f8 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/item/HeaderWithMenuItem.kt +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/item/HeaderWithMenuItem.kt @@ -1,9 +1,8 @@ package org.schabi.newpipe.local.subscription.item -import android.view.View.GONE import android.view.View.OnClickListener -import android.view.View.VISIBLE import androidx.annotation.DrawableRes +import androidx.core.view.isVisible import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder import com.xwray.groupie.kotlinandroidextensions.Item import kotlinx.android.synthetic.main.header_with_menu_item.header_menu_item @@ -47,6 +46,6 @@ class HeaderWithMenuItem( } private fun updateMenuItemVisibility(viewHolder: GroupieViewHolder) { - viewHolder.header_menu_item.visibility = if (showMenuItem) VISIBLE else GONE + viewHolder.header_menu_item.isVisible = showMenuItem } } diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/item/PickerSubscriptionItem.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/item/PickerSubscriptionItem.kt index 7d33da71f3a..df84c115a78 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/item/PickerSubscriptionItem.kt +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/item/PickerSubscriptionItem.kt @@ -1,6 +1,7 @@ package org.schabi.newpipe.local.subscription.item import android.view.View +import androidx.core.view.isVisible import com.nostra13.universalimageloader.core.ImageLoader import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder import com.xwray.groupie.kotlinandroidextensions.Item @@ -25,7 +26,7 @@ data class PickerSubscriptionItem( viewHolder.thumbnail_view, ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS) 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) { diff --git a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayerActivity.java b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayerActivity.java index 0e5222f5e5f..2fc710fb0b3 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayerActivity.java +++ b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayerActivity.java @@ -2,11 +2,8 @@ import android.content.Intent; import android.view.Menu; -import android.view.MenuItem; import org.schabi.newpipe.R; -import org.schabi.newpipe.util.NavigationHelper; -import org.schabi.newpipe.util.PermissionHelper; public final class BackgroundPlayerActivity extends ServicePlayerActivity { @@ -46,31 +43,6 @@ public int getPlayerOptionMenuResource() { return R.menu.menu_play_queue_bg; } - @Override - public boolean onPlayerOptionSelected(final MenuItem item) { - if (item.getItemId() == R.id.action_switch_popup) { - - if (!PermissionHelper.isPopupEnabled(this)) { - PermissionHelper.showPopupEnablementToast(this); - return true; - } - - this.player.setRecovery(); - NavigationHelper.playOnPopupPlayer( - getApplicationContext(), player.playQueue, this.player.isPlaying()); - return true; - } - - if (item.getItemId() == R.id.action_switch_background) { - this.player.setRecovery(); - NavigationHelper.playOnBackgroundPlayer( - getApplicationContext(), player.playQueue, this.player.isPlaying()); - return true; - } - - return false; - } - @Override public void setupMenu(final Menu menu) { if (player == null) { diff --git a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java index 70f2e158b9f..1355f428561 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java @@ -125,7 +125,7 @@ public abstract class BasePlayer implements @NonNull public static final String RESUME_PLAYBACK = "resume_playback"; @NonNull - public static final String START_PAUSED = "start_paused"; + public static final String PLAY_WHEN_READY = "play_when_ready"; @NonNull public static final String SELECT_ON_APPEND = "select_on_append"; @NonNull @@ -224,7 +224,7 @@ public void onReceive(final Context ctx, final Intent intent) { this.dataSource = new PlayerDataSource(context, userAgent, bandwidthMeter); final TrackSelection.Factory trackSelectionFactory = PlayerHelper - .getQualitySelector(context); + .getQualitySelector(); this.trackSelector = new CustomTrackSelector(context, trackSelectionFactory); this.loadControl = new LoadController(); @@ -302,6 +302,7 @@ public void handleIntent(final Intent intent) { final boolean samePlayQueue = playQueue != null && playQueue.equals(queue); final int repeatMode = intent.getIntExtra(REPEAT_MODE, getRepeatMode()); + final boolean playWhenReady = intent.getBooleanExtra(PLAY_WHEN_READY, true); final boolean isMuted = intent .getBooleanExtra(IS_MUTED, simpleExoPlayer != null && isMuted()); @@ -327,16 +328,20 @@ public void handleIntent(final Intent intent) { simpleExoPlayer.retry(); } simpleExoPlayer.seekTo(playQueue.getIndex(), queue.getItem().getRecoveryPosition()); - return; + simpleExoPlayer.setPlayWhenReady(playWhenReady); - } else if (samePlayQueue && !playQueue.isDisposed() && simpleExoPlayer != null) { + } else if (simpleExoPlayer != null + && samePlayQueue + && playQueue != null + && !playQueue.isDisposed()) { // Do not re-init the same PlayQueue. Save time // Player can have state = IDLE when playback is stopped or failed // and we should retry() in this case if (simpleExoPlayer.getPlaybackState() == Player.STATE_IDLE) { simpleExoPlayer.retry(); } - return; + simpleExoPlayer.setPlayWhenReady(playWhenReady); + } else if (intent.getBooleanExtra(RESUME_PLAYBACK, false) && isPlaybackResumeEnabled() && !samePlayQueue) { @@ -351,7 +356,7 @@ && isPlaybackResumeEnabled() state -> { queue.setRecovery(queue.getIndex(), state.getProgressTime()); initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, - playbackSkipSilence, true, isMuted); + playbackSkipSilence, playWhenReady, isMuted); }, error -> { if (DEBUG) { @@ -359,24 +364,22 @@ && isPlaybackResumeEnabled() } // In case any error we can start playback without history initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, - playbackSkipSilence, true, isMuted); + playbackSkipSilence, playWhenReady, isMuted); }, () -> { // Completed but not found in history initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, - playbackSkipSilence, true, isMuted); + playbackSkipSilence, playWhenReady, isMuted); } ); databaseUpdateReactor.add(stateLoader); - return; } + } else { + // Good to go... + // In a case of equal PlayQueues we can re-init old one but only when it is disposed + initPlayback(samePlayQueue ? playQueue : queue, repeatMode, playbackSpeed, + playbackPitch, playbackSkipSilence, playWhenReady, isMuted); } - // Good to go... - // In a case of equal PlayQueues we can re-init old one but only when it is disposed - initPlayback(samePlayQueue ? playQueue : queue, repeatMode, - playbackSpeed, playbackPitch, playbackSkipSilence, - !intent.getBooleanExtra(START_PAUSED, false), - isMuted); } private PlaybackParameters retrievePlaybackParametersFromPreferences() { diff --git a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java index c7fbb444b40..63f6a400ef7 100644 --- a/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/MainPlayer.java @@ -30,6 +30,9 @@ import android.view.ViewGroup; import android.view.WindowManager; +import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; + import org.schabi.newpipe.R; import org.schabi.newpipe.util.ThemeHelper; @@ -91,7 +94,7 @@ public void onCreate() { Log.d(TAG, "onCreate() called"); } assureCorrectAppLanguage(this); - windowManager = (WindowManager) getSystemService(WINDOW_SERVICE); + windowManager = ContextCompat.getSystemService(this, WindowManager.class); ThemeHelper.setTheme(this); createView(); @@ -223,12 +226,13 @@ boolean isLandscape() { // DisplayMetrics from activity context knows about MultiWindow feature // while DisplayMetrics from app context doesn't final DisplayMetrics metrics = (playerImpl != null - && playerImpl.getParentActivity() != null) - ? playerImpl.getParentActivity().getResources().getDisplayMetrics() - : getResources().getDisplayMetrics(); + && playerImpl.getParentActivity() != null + ? playerImpl.getParentActivity().getResources() + : getResources()).getDisplayMetrics(); return metrics.heightPixels < metrics.widthPixels; } + @Nullable public View getView() { if (playerImpl == null) { return null; @@ -238,7 +242,7 @@ public View getView() { } public void removeViewFromParent() { - if (getView().getParent() != null) { + if (getView() != null && getView().getParent() != null) { if (playerImpl.getParentActivity() != null) { // This means view was added to fragment final ViewGroup parent = (ViewGroup) getView().getParent(); diff --git a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java index 370631116a2..62f1d5dc2ae 100644 --- a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java +++ b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java @@ -116,10 +116,14 @@ private synchronized NotificationCompat.Builder createNotification( .setMediaSession(player.mediaSessionManager.getSessionToken()) .setShowActionsInCompactView(compactSlots)) .setPriority(NotificationCompat.PRIORITY_HIGH) - .setSmallIcon(R.drawable.ic_newpipe_triangle_white) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .setColor(ContextCompat.getColor(player.context, R.color.gray)) .setCategory(NotificationCompat.CATEGORY_TRANSPORT) + .setShowWhen(false) + .setSmallIcon(R.drawable.ic_newpipe_triangle_white) + .setColor(ContextCompat.getColor(player.context, R.color.dark_background_color)) + .setColorized(player.sharedPreferences.getBoolean( + player.context.getString(R.string.notification_colorize_key), + true)) .setDeleteIntent(PendingIntent.getBroadcast(player.context, NOTIFICATION_ID, new Intent(ACTION_CLOSE), FLAG_UPDATE_CURRENT)); @@ -148,7 +152,10 @@ private synchronized void updateNotification(final VideoPlayerImpl player) { @SuppressLint("RestrictedApi") 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 return true; } diff --git a/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java b/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java index 0ffd7f5949d..ad4c603cdbc 100644 --- a/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java +++ b/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java @@ -27,12 +27,11 @@ import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player; -import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.R; -import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; 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.helper.PlaybackParameterDialog; import org.schabi.newpipe.player.playqueue.PlayQueue; @@ -41,9 +40,9 @@ import org.schabi.newpipe.player.playqueue.PlayQueueItemBuilder; import org.schabi.newpipe.player.playqueue.PlayQueueItemHolder; import org.schabi.newpipe.player.playqueue.PlayQueueItemTouchCallback; -import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; +import org.schabi.newpipe.util.PermissionHelper; import org.schabi.newpipe.util.ThemeHelper; import java.util.Collections; @@ -112,9 +111,8 @@ public abstract class ServicePlayerActivity extends AppCompatActivity public abstract int getPlayerOptionMenuResource(); - public abstract boolean onPlayerOptionSelected(MenuItem item); - public abstract void setupMenu(Menu m); + //////////////////////////////////////////////////////////////////////////// // Activity Lifecycle //////////////////////////////////////////////////////////////////////////// @@ -186,12 +184,22 @@ public boolean onOptionsItemSelected(final MenuItem item) { return true; case R.id.action_switch_main: this.player.setRecovery(); - getApplicationContext().startActivity( - getSwitchIntent(MainActivity.class, MainPlayer.PlayerType.VIDEO) - .putExtra(BasePlayer.START_PAUSED, !this.player.isPlaying())); + NavigationHelper.playOnMainPlayer(this, player.getPlayQueue(), true); + return true; + case R.id.action_switch_popup: + if (PermissionHelper.isPopupEnabled(this)) { + this.player.setRecovery(); + NavigationHelper.playOnPopupPlayer(this, player.playQueue, true); + } else { + PermissionHelper.showPopupEnablementToast(this); + } + return true; + case R.id.action_switch_background: + this.player.setRecovery(); + NavigationHelper.playOnBackgroundPlayer(this, player.playQueue, true); return true; } - return onPlayerOptionSelected(item) || super.onOptionsItemSelected(item); + return super.onOptionsItemSelected(item); } @Override @@ -200,24 +208,6 @@ protected void onDestroy() { unbind(); } - protected Intent getSwitchIntent(final Class clazz, final MainPlayer.PlayerType playerType) { - return NavigationHelper.getPlayerIntent(getApplicationContext(), clazz, - this.player.getPlayQueue(), this.player.getRepeatMode(), - this.player.getPlaybackSpeed(), this.player.getPlaybackPitch(), - this.player.getPlaybackSkipSilence(), - null, - true, - !this.player.isPlaying(), - this.player.isMuted()) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - .putExtra(Constants.KEY_LINK_TYPE, StreamingService.LinkType.STREAM) - .putExtra(Constants.KEY_URL, this.player.getVideoUrl()) - .putExtra(Constants.KEY_TITLE, this.player.getVideoTitle()) - .putExtra(Constants.KEY_SERVICE_ID, - this.player.getCurrentMetadata().getMetadata().getServiceId()) - .putExtra(VideoPlayer.PLAYER_TYPE, playerType); - } - //////////////////////////////////////////////////////////////////////////// // Service Connection //////////////////////////////////////////////////////////////////////////// @@ -366,7 +356,9 @@ private void buildItemPopupMenu(final PlayQueueItem item, final View view) { final MenuItem detail = popupMenu.getMenu().add(RECYCLER_ITEM_POPUP_MENU_GROUP_ID, 1, Menu.NONE, R.string.play_queue_stream_detail); detail.setOnMenuItemClickListener(menuItem -> { - onOpenDetail(item.getServiceId(), item.getUrl(), item.getTitle()); + // playQueue is null since we don't want any queue change + NavigationHelper.openVideoDetail(this, item.getServiceId(), item.getUrl(), + item.getTitle(), null, false); return true; }); @@ -453,11 +445,6 @@ public void onStartDrag(final PlayQueueItemHolder viewHolder) { }; } - private void onOpenDetail(final int serviceId, final String videoUrl, - final String videoTitle) { - NavigationHelper.openVideoDetail(this, serviceId, videoUrl, videoTitle); - } - private void scrollToSelected() { if (player == null) { return; @@ -571,8 +558,13 @@ private void appendAllToPlaylist() { } private void openPlaylistAppendDialog(final List playlist) { - PlaylistAppendDialog.fromPlayQueueItems(playlist) - .show(getSupportFragmentManager(), getTag()); + final PlaylistAppendDialog d = PlaylistAppendDialog.fromPlayQueueItems(playlist); + + PlaylistAppendDialog.onPlaylistFound(getApplicationContext(), + () -> d.show(getSupportFragmentManager(), getTag()), + () -> PlaylistCreationDialog.newInstance(d) + .show(getSupportFragmentManager(), getTag() + )); } //////////////////////////////////////////////////////////////////////////// @@ -742,11 +734,10 @@ private void onMaybeMuteChanged() { //2) Icon change accordingly to current App Theme // using rootView.getContext() because getApplicationContext() didn't work - item.setIcon(player.isMuted() - ? ThemeHelper.resolveResourceIdFromAttr(rootView.getContext(), - R.attr.ic_volume_off) - : ThemeHelper.resolveResourceIdFromAttr(rootView.getContext(), - R.attr.ic_volume_up)); + item.setIcon(ThemeHelper.resolveResourceIdFromAttr(rootView.getContext(), + player.isMuted() + ? R.attr.ic_volume_off + : R.attr.ic_volume_up)); } } } diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java index f1bbc731206..5b4b987a4fb 100644 --- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java +++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java @@ -32,8 +32,6 @@ import android.net.Uri; import android.os.Build; import android.os.Handler; -import android.view.DisplayCutout; -import androidx.preference.PreferenceManager; import android.provider.Settings; import android.util.DisplayMetrics; import android.util.Log; @@ -60,8 +58,13 @@ import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; 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.RecyclerView; + import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.SimpleExoPlayer; @@ -72,9 +75,8 @@ import com.google.android.exoplayer2.ui.SubtitleView; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.nostra13.universalimageloader.core.assist.FailReason; -import org.schabi.newpipe.MainActivity; + import org.schabi.newpipe.R; -import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.VideoStream; import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; @@ -92,9 +94,8 @@ import org.schabi.newpipe.player.resolver.AudioPlaybackResolver; import org.schabi.newpipe.player.resolver.MediaSourceTag; import org.schabi.newpipe.player.resolver.VideoPlaybackResolver; -import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.AnimationUtils; -import org.schabi.newpipe.util.Constants; +import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.KoreUtil; import org.schabi.newpipe.util.ListHelper; import org.schabi.newpipe.util.NavigationHelper; @@ -102,7 +103,6 @@ import java.util.List; -import static android.content.Context.WINDOW_SERVICE; 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_REWIND; @@ -257,7 +257,12 @@ public void handleIntent(final Intent intent) { onQueueClosed(); // Android TV: without it focus will frame the whole player playPauseButton.requestFocus(); - onPlay(); + + if (simpleExoPlayer.getPlayWhenReady()) { + onPlay(); + } else { + onPause(); + } } NavigationHelper.sendPlayerStartedEvent(service); } @@ -266,7 +271,7 @@ public void handleIntent(final Intent intent) { super("MainPlayer" + TAG, service); this.service = service; this.shouldUpdateOnProgress = true; - this.windowManager = (WindowManager) service.getSystemService(WINDOW_SERVICE); + this.windowManager = ContextCompat.getSystemService(service, WindowManager.class); this.defaultPreferences = PreferenceManager.getDefaultSharedPreferences(service); this.resolver = new AudioPlaybackResolver(context, dataSource); } @@ -475,16 +480,14 @@ public void onChange(final boolean selfChange) { settingsContentObserver); getRootView().addOnLayoutChangeListener(this); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - queueLayout.setOnApplyWindowInsetsListener((view, windowInsets) -> { - final DisplayCutout cutout = windowInsets.getDisplayCutout(); - if (cutout != null) { - view.setPadding(cutout.getSafeInsetLeft(), cutout.getSafeInsetTop(), - cutout.getSafeInsetRight(), cutout.getSafeInsetBottom()); - } - return windowInsets; - }); - } + ViewCompat.setOnApplyWindowInsetsListener(queueLayout, (view, windowInsets) -> { + final DisplayCutoutCompat cutout = windowInsets.getDisplayCutout(); + if (cutout != null) { + view.setPadding(cutout.getSafeInsetLeft(), cutout.getSafeInsetTop(), + cutout.getSafeInsetRight(), cutout.getSafeInsetBottom()); + } + return windowInsets; + }); // PlaybackControlRoot already consumed window insets but we should pass them to // player_overlays too. Without it they will be off-centered @@ -755,40 +758,6 @@ public void toggleFullscreen() { setupScreenRotationButton(); } - public void switchFromPopupToMain() { - if (DEBUG) { - Log.d(TAG, "switchFromPopupToMain() called"); - } - if (!popupPlayerSelected() || simpleExoPlayer == null || getCurrentMetadata() == null) { - return; - } - - setRecovery(); - service.removeViewFromParent(); - final Intent intent = NavigationHelper.getPlayerIntent( - service, - MainActivity.class, - this.getPlayQueue(), - this.getRepeatMode(), - this.getPlaybackSpeed(), - this.getPlaybackPitch(), - this.getPlaybackSkipSilence(), - null, - true, - !isPlaying(), - isMuted() - ); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra(Constants.KEY_SERVICE_ID, - getCurrentMetadata().getMetadata().getServiceId()); - intent.putExtra(Constants.KEY_LINK_TYPE, StreamingService.LinkType.STREAM); - intent.putExtra(Constants.KEY_URL, getVideoUrl()); - intent.putExtra(Constants.KEY_TITLE, getVideoTitle()); - intent.putExtra(VideoDetailFragment.AUTO_PLAY, true); - service.onDestroy(); - context.startActivity(intent); - } - @Override public void onClick(final View v) { super.onClick(v); @@ -816,7 +785,9 @@ public void onClick(final View v) { } else if (v.getId() == openInBrowser.getId()) { onOpenInBrowserClicked(); } else if (v.getId() == fullscreenButton.getId()) { - switchFromPopupToMain(); + setRecovery(); + NavigationHelper.playOnMainPlayer(context, getPlayQueue(), true); + return; } else if (v.getId() == screenRotationButton.getId()) { // Only if it's not a vertical video or vertical video but in landscape with locked // orientation a screen orientation can be changed automatically @@ -2093,6 +2064,10 @@ public WindowManager.LayoutParams getPopupLayoutParams() { return popupLayoutParams; } + public MainPlayer.PlayerType getPlayerType() { + return playerType; + } + public float getScreenWidth() { return screenWidth; } diff --git a/app/src/main/java/org/schabi/newpipe/player/event/BasePlayerGestureListener.kt b/app/src/main/java/org/schabi/newpipe/player/event/BasePlayerGestureListener.kt new file mode 100644 index 00000000000..681c1b9af46 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/player/event/BasePlayerGestureListener.kt @@ -0,0 +1,500 @@ +package org.schabi.newpipe.player.event + +import android.content.Context +import android.os.Handler +import android.util.Log +import android.view.GestureDetector +import android.view.MotionEvent +import android.view.View +import android.view.ViewConfiguration +import org.schabi.newpipe.player.BasePlayer +import org.schabi.newpipe.player.MainPlayer +import org.schabi.newpipe.player.VideoPlayerImpl +import org.schabi.newpipe.player.helper.PlayerHelper +import org.schabi.newpipe.util.AnimationUtils +import kotlin.math.abs +import kotlin.math.hypot +import kotlin.math.max + +/** + * Base gesture handling for [VideoPlayerImpl] + * + * This class contains the logic for the player gestures like View preparations + * and provides some abstract methods to make it easier separating the logic from the UI. + */ +abstract class BasePlayerGestureListener( + @JvmField + protected val playerImpl: VideoPlayerImpl, + @JvmField + protected val service: MainPlayer +) : GestureDetector.SimpleOnGestureListener(), View.OnTouchListener { + + // /////////////////////////////////////////////////////////////////// + // Abstract methods for VIDEO and POPUP + // /////////////////////////////////////////////////////////////////// + + abstract fun onDoubleTap(event: MotionEvent, portion: DisplayPortion) + + abstract fun onSingleTap(playerType: MainPlayer.PlayerType) + + abstract fun onScroll( + playerType: MainPlayer.PlayerType, + portion: DisplayPortion, + initialEvent: MotionEvent, + movingEvent: MotionEvent, + distanceX: Float, + distanceY: Float + ) + + abstract fun onScrollEnd(playerType: MainPlayer.PlayerType, event: MotionEvent) + + // /////////////////////////////////////////////////////////////////// + // Abstract methods for POPUP (exclusive) + // /////////////////////////////////////////////////////////////////// + + abstract fun onPopupResizingStart() + + abstract fun onPopupResizingEnd() + + private var initialPopupX: Int = -1 + private var initialPopupY: Int = -1 + + private var isMovingInMain = false + private var isMovingInPopup = false + private var isResizing = false + + private val tossFlingVelocity = PlayerHelper.getTossFlingVelocity() + + // [popup] initial coordinates and distance between fingers + private var initPointerDistance = -1.0 + private var initFirstPointerX = -1f + private var initFirstPointerY = -1f + private var initSecPointerX = -1f + private var initSecPointerY = -1f + + // /////////////////////////////////////////////////////////////////// + // onTouch implementation + // /////////////////////////////////////////////////////////////////// + + override fun onTouch(v: View, event: MotionEvent): Boolean { + return if (playerImpl.popupPlayerSelected()) { + onTouchInPopup(v, event) + } else { + onTouchInMain(v, event) + } + } + + private fun onTouchInMain(v: View, event: MotionEvent): Boolean { + playerImpl.gestureDetector.onTouchEvent(event) + if (event.action == MotionEvent.ACTION_UP && isMovingInMain) { + isMovingInMain = false + onScrollEnd(MainPlayer.PlayerType.VIDEO, event) + } + return when (event.action) { + MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> { + v.parent.requestDisallowInterceptTouchEvent(playerImpl.isFullscreen) + true + } + MotionEvent.ACTION_UP -> { + v.parent.requestDisallowInterceptTouchEvent(false) + false + } + else -> true + } + } + + private fun onTouchInPopup(v: View, event: MotionEvent): Boolean { + playerImpl.gestureDetector.onTouchEvent(event) + if (event.pointerCount == 2 && !isMovingInPopup && !isResizing) { + if (DEBUG) { + Log.d(TAG, "onTouch() 2 finger pointer detected, enabling resizing.") + } + onPopupResizingStart() + + // record coordinates of fingers + initFirstPointerX = event.getX(0) + initFirstPointerY = event.getY(0) + initSecPointerX = event.getX(1) + initSecPointerY = event.getY(1) + // record distance between fingers + initPointerDistance = Math.hypot(initFirstPointerX - initSecPointerX.toDouble(), + initFirstPointerY - initSecPointerY.toDouble()) + + isResizing = true + } + if (event.action == MotionEvent.ACTION_MOVE && !isMovingInPopup && isResizing) { + if (DEBUG) { + Log.d(TAG, "onTouch() ACTION_MOVE > v = [$v], e1.getRaw = [${event.rawX}" + + ", ${event.rawY}]") + } + return handleMultiDrag(event) + } + if (event.action == MotionEvent.ACTION_UP) { + if (DEBUG) { + Log.d(TAG, "onTouch() ACTION_UP > v = [$v], e1.getRaw = [${event.rawX}" + + ", ${event.rawY}]") + } + if (isMovingInPopup) { + isMovingInPopup = false + onScrollEnd(MainPlayer.PlayerType.POPUP, event) + } + if (isResizing) { + isResizing = false + + initPointerDistance = (-1).toDouble() + initFirstPointerX = (-1).toFloat() + initFirstPointerY = (-1).toFloat() + initSecPointerX = (-1).toFloat() + initSecPointerY = (-1).toFloat() + + onPopupResizingEnd() + playerImpl.changeState(playerImpl.currentState) + } + if (!playerImpl.isPopupClosing) { + playerImpl.savePositionAndSize() + } + } + + v.performClick() + return true + } + + private fun handleMultiDrag(event: MotionEvent): Boolean { + if (initPointerDistance != -1.0 && event.pointerCount == 2) { + // get the movements of the fingers + val firstPointerMove = hypot(event.getX(0) - initFirstPointerX.toDouble(), + event.getY(0) - initFirstPointerY.toDouble()) + val secPointerMove = hypot(event.getX(1) - initSecPointerX.toDouble(), + event.getY(1) - initSecPointerY.toDouble()) + + // minimum threshold beyond which pinch gesture will work + val minimumMove = ViewConfiguration.get(service).scaledTouchSlop + + if (max(firstPointerMove, secPointerMove) > minimumMove) { + // calculate current distance between the pointers + val currentPointerDistance = hypot(event.getX(0) - event.getX(1).toDouble(), + event.getY(0) - event.getY(1).toDouble()) + + val popupWidth = playerImpl.popupWidth.toDouble() + // change co-ordinates of popup so the center stays at the same position + val newWidth = popupWidth * currentPointerDistance / initPointerDistance + initPointerDistance = currentPointerDistance + playerImpl.popupLayoutParams.x += ((popupWidth - newWidth) / 2.0).toInt() + + playerImpl.checkPopupPositionBounds() + playerImpl.updateScreenSize() + + playerImpl.updatePopupSize( + Math.min(playerImpl.screenWidth.toDouble(), newWidth).toInt(), + -1) + return true + } + } + return false + } + + // /////////////////////////////////////////////////////////////////// + // Simple gestures + // /////////////////////////////////////////////////////////////////// + + override fun onDown(e: MotionEvent): Boolean { + if (DEBUG) + Log.d(TAG, "onDown called with e = [$e]") + + if (isDoubleTapping && isDoubleTapEnabled) { + doubleTapControls?.onDoubleTapProgressDown(getDisplayPortion(e)) + return true + } + + return if (playerImpl.popupPlayerSelected()) + onDownInPopup(e) + else + true + } + + private fun onDownInPopup(e: MotionEvent): Boolean { + // Fix popup position when the user touch it, it may have the wrong one + // because the soft input is visible (the draggable area is currently resized). + playerImpl.updateScreenSize() + playerImpl.checkPopupPositionBounds() + initialPopupX = playerImpl.popupLayoutParams.x + initialPopupY = playerImpl.popupLayoutParams.y + playerImpl.popupWidth = playerImpl.popupLayoutParams.width.toFloat() + playerImpl.popupHeight = playerImpl.popupLayoutParams.height.toFloat() + return super.onDown(e) + } + + override fun onDoubleTap(e: MotionEvent): Boolean { + if (DEBUG) + Log.d(TAG, "onDoubleTap called with e = [$e]") + + onDoubleTap(e, getDisplayPortion(e)) + return true + } + + override fun onSingleTapConfirmed(e: MotionEvent): Boolean { + if (DEBUG) + Log.d(TAG, "onSingleTapConfirmed() called with: e = [$e]") + + if (isDoubleTapping) + return true + + if (playerImpl.popupPlayerSelected()) { + if (playerImpl.player == null) + return false + + onSingleTap(MainPlayer.PlayerType.POPUP) + return true + } else { + super.onSingleTapConfirmed(e) + if (playerImpl.currentState == BasePlayer.STATE_BLOCKED) + return true + + onSingleTap(MainPlayer.PlayerType.VIDEO) + } + return true + } + + override fun onLongPress(e: MotionEvent?) { + if (playerImpl.popupPlayerSelected()) { + playerImpl.updateScreenSize() + playerImpl.checkPopupPositionBounds() + playerImpl.updatePopupSize(playerImpl.screenWidth.toInt(), -1) + } + } + + override fun onScroll( + initialEvent: MotionEvent, + movingEvent: MotionEvent, + distanceX: Float, + distanceY: Float + ): Boolean { + return if (playerImpl.popupPlayerSelected()) { + onScrollInPopup(initialEvent, movingEvent, distanceX, distanceY) + } else { + onScrollInMain(initialEvent, movingEvent, distanceX, distanceY) + } + } + + override fun onFling( + e1: MotionEvent?, + e2: MotionEvent?, + velocityX: Float, + velocityY: Float + ): Boolean { + return if (playerImpl.popupPlayerSelected()) { + val absVelocityX = abs(velocityX) + val absVelocityY = abs(velocityY) + if (absVelocityX.coerceAtLeast(absVelocityY) > tossFlingVelocity) { + if (absVelocityX > tossFlingVelocity) { + playerImpl.popupLayoutParams.x = velocityX.toInt() + } + if (absVelocityY > tossFlingVelocity) { + playerImpl.popupLayoutParams.y = velocityY.toInt() + } + playerImpl.checkPopupPositionBounds() + playerImpl.windowManager + .updateViewLayout(playerImpl.rootView, playerImpl.popupLayoutParams) + return true + } + return false + } else { + true + } + } + + private fun onScrollInMain( + initialEvent: MotionEvent, + movingEvent: MotionEvent, + distanceX: Float, + distanceY: Float + ): Boolean { + + if (!playerImpl.isFullscreen) { + return false + } + + val isTouchingStatusBar: Boolean = initialEvent.y < getStatusBarHeight(service) + val isTouchingNavigationBar: Boolean = (initialEvent.y + > playerImpl.rootView.height - getNavigationBarHeight(service)) + if (isTouchingStatusBar || isTouchingNavigationBar) { + return false + } + + val insideThreshold = abs(movingEvent.y - initialEvent.y) <= MOVEMENT_THRESHOLD + if (!isMovingInMain && (insideThreshold || abs(distanceX) > abs(distanceY)) || + playerImpl.currentState == BasePlayer.STATE_COMPLETED) { + return false + } + + isMovingInMain = true + + onScroll(MainPlayer.PlayerType.VIDEO, getDisplayHalfPortion(initialEvent), + initialEvent, movingEvent, distanceX, distanceY) + + return true + } + + private fun onScrollInPopup( + initialEvent: MotionEvent, + movingEvent: MotionEvent, + distanceX: Float, + distanceY: Float + ): Boolean { + + if (isResizing) { + return super.onScroll(initialEvent, movingEvent, distanceX, distanceY) + } + + if (!isMovingInPopup) { + AnimationUtils.animateView(playerImpl.closeOverlayButton, true, 200) + } + + isMovingInPopup = true + + val diffX: Float = (movingEvent.rawX - initialEvent.rawX) + var posX: Float = (initialPopupX + diffX) + val diffY: Float = (movingEvent.rawY - initialEvent.rawY) + var posY: Float = (initialPopupY + diffY) + + if (posX > playerImpl.screenWidth - playerImpl.popupWidth) { + posX = (playerImpl.screenWidth - playerImpl.popupWidth) + } else if (posX < 0) { + posX = 0f + } + + if (posY > playerImpl.screenHeight - playerImpl.popupHeight) { + posY = (playerImpl.screenHeight - playerImpl.popupHeight) + } else if (posY < 0) { + posY = 0f + } + + playerImpl.popupLayoutParams.x = posX.toInt() + playerImpl.popupLayoutParams.y = posY.toInt() + + onScroll(MainPlayer.PlayerType.POPUP, getDisplayHalfPortion(initialEvent), + initialEvent, movingEvent, distanceX, distanceY) + + playerImpl.windowManager + .updateViewLayout(playerImpl.rootView, playerImpl.popupLayoutParams) + return true + } + + // /////////////////////////////////////////////////////////////////// + // Multi double tapping + // /////////////////////////////////////////////////////////////////// + + var doubleTapControls: DoubleTapListener? = null + private set + + val isDoubleTapEnabled: Boolean + get() = doubleTapDelay > 0 + + var isDoubleTapping = false + private set + + fun doubleTapControls(listener: DoubleTapListener) = apply { + doubleTapControls = listener + } + + private var doubleTapDelay = DOUBLE_TAP_DELAY + private val doubleTapHandler: Handler = Handler() + private val doubleTapRunnable = Runnable { + if (DEBUG) + Log.d(TAG, "doubleTapRunnable called") + + isDoubleTapping = false + doubleTapControls?.onDoubleTapFinished() + } + + fun startMultiDoubleTap(e: MotionEvent) { + if (!isDoubleTapping) { + if (DEBUG) + Log.d(TAG, "startMultiDoubleTap called with e = [$e]") + + keepInDoubleTapMode() + doubleTapControls?.onDoubleTapStarted(getDisplayPortion(e)) + } + } + + fun keepInDoubleTapMode() { + if (DEBUG) + Log.d(TAG, "keepInDoubleTapMode called") + + isDoubleTapping = true + doubleTapHandler.removeCallbacks(doubleTapRunnable) + doubleTapHandler.postDelayed(doubleTapRunnable, doubleTapDelay) + } + + fun endMultiDoubleTap() { + if (DEBUG) + Log.d(TAG, "endMultiDoubleTap called") + + isDoubleTapping = false + doubleTapHandler.removeCallbacks(doubleTapRunnable) + doubleTapControls?.onDoubleTapFinished() + } + + fun enableMultiDoubleTap(enable: Boolean) = apply { + doubleTapDelay = if (enable) DOUBLE_TAP_DELAY else 0 + } + + // /////////////////////////////////////////////////////////////////// + // Utils + // /////////////////////////////////////////////////////////////////// + + private fun getDisplayPortion(e: MotionEvent): DisplayPortion { + return if (playerImpl.playerType == MainPlayer.PlayerType.POPUP) { + when { + e.x < playerImpl.popupWidth / 3.0 -> DisplayPortion.LEFT + e.x > playerImpl.popupWidth * 2.0 / 3.0 -> DisplayPortion.RIGHT + else -> DisplayPortion.MIDDLE + } + } else /* MainPlayer.PlayerType.VIDEO */ { + when { + e.x < playerImpl.rootView.width / 3.0 -> DisplayPortion.LEFT + e.x > playerImpl.rootView.width * 2.0 / 3.0 -> DisplayPortion.RIGHT + else -> DisplayPortion.MIDDLE + } + } + } + + // Currently needed for scrolling since there is no action more the middle portion + private fun getDisplayHalfPortion(e: MotionEvent): DisplayPortion { + return if (playerImpl.playerType == MainPlayer.PlayerType.POPUP) { + when { + e.x < playerImpl.popupWidth / 2.0 -> DisplayPortion.LEFT_HALF + else -> DisplayPortion.RIGHT_HALF + } + } else /* MainPlayer.PlayerType.VIDEO */ { + when { + e.x < playerImpl.rootView.width / 2.0 -> DisplayPortion.LEFT_HALF + else -> DisplayPortion.RIGHT_HALF + } + } + } + + private fun getNavigationBarHeight(context: Context): Int { + val resId = context.resources + .getIdentifier("navigation_bar_height", "dimen", "android") + return if (resId > 0) { + context.resources.getDimensionPixelSize(resId) + } else 0 + } + + private fun getStatusBarHeight(context: Context): Int { + val resId = context.resources + .getIdentifier("status_bar_height", "dimen", "android") + return if (resId > 0) { + context.resources.getDimensionPixelSize(resId) + } else 0 + } + + companion object { + private const val TAG = "BasePlayerGestListener" + private val DEBUG = BasePlayer.DEBUG + + private const val DOUBLE_TAP_DELAY = 550L + private const val MOVEMENT_THRESHOLD = 40 + } +} diff --git a/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java b/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java index 5405d01c174..26ecb187105 100644 --- a/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java +++ b/app/src/main/java/org/schabi/newpipe/player/event/CustomBottomSheetBehavior.java @@ -42,6 +42,14 @@ public boolean onInterceptTouchEvent(@NonNull final CoordinatorLayout parent, 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 if (getState() == BottomSheetBehavior.STATE_EXPANDED && event.getAction() == MotionEvent.ACTION_DOWN) { diff --git a/app/src/main/java/org/schabi/newpipe/player/event/DisplayPortion.kt b/app/src/main/java/org/schabi/newpipe/player/event/DisplayPortion.kt new file mode 100644 index 00000000000..f15e42897ca --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/player/event/DisplayPortion.kt @@ -0,0 +1,5 @@ +package org.schabi.newpipe.player.event + +enum class DisplayPortion { + LEFT, MIDDLE, RIGHT, LEFT_HALF, RIGHT_HALF +} diff --git a/app/src/main/java/org/schabi/newpipe/player/event/DoubleTapListener.kt b/app/src/main/java/org/schabi/newpipe/player/event/DoubleTapListener.kt new file mode 100644 index 00000000000..84cfb9b8d5a --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/player/event/DoubleTapListener.kt @@ -0,0 +1,7 @@ +package org.schabi.newpipe.player.event + +interface DoubleTapListener { + fun onDoubleTapStarted(portion: DisplayPortion) {} + fun onDoubleTapProgressDown(portion: DisplayPortion) {} + fun onDoubleTapFinished() {} +} diff --git a/app/src/main/java/org/schabi/newpipe/player/event/PlayerGestureListener.java b/app/src/main/java/org/schabi/newpipe/player/event/PlayerGestureListener.java index a2def2a64a0..fdea20775c2 100644 --- a/app/src/main/java/org/schabi/newpipe/player/event/PlayerGestureListener.java +++ b/app/src/main/java/org/schabi/newpipe/player/event/PlayerGestureListener.java @@ -1,16 +1,16 @@ package org.schabi.newpipe.player.event; import android.app.Activity; -import android.content.Context; import android.util.Log; -import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; -import android.view.ViewConfiguration; import android.view.Window; import android.view.WindowManager; import android.widget.ProgressBar; + import androidx.appcompat.content.res.AppCompatResources; + +import org.jetbrains.annotations.NotNull; import org.schabi.newpipe.R; import org.schabi.newpipe.player.BasePlayer; import org.schabi.newpipe.player.MainPlayer; @@ -23,217 +23,116 @@ import static org.schabi.newpipe.util.AnimationUtils.Type.SCALE_AND_ALPHA; import static org.schabi.newpipe.util.AnimationUtils.animateView; +/** + * GestureListener for the player + * + * While {@link BasePlayerGestureListener} contains the logic behind the single gestures + * this class focuses on the visual aspect like hiding and showing the controls or changing + * volume/brightness during scrolling for specific events. + */ public class PlayerGestureListener - extends GestureDetector.SimpleOnGestureListener + extends BasePlayerGestureListener implements View.OnTouchListener { private static final String TAG = ".PlayerGestureListener"; private static final boolean DEBUG = BasePlayer.DEBUG; - private final VideoPlayerImpl playerImpl; - private final MainPlayer service; - - private int initialPopupX; - private int initialPopupY; - - private boolean isMovingInMain; - private boolean isMovingInPopup; - - private boolean isResizing; - - private final int tossFlingVelocity; - private final boolean isVolumeGestureEnabled; private final boolean isBrightnessGestureEnabled; private final int maxVolume; - private static final int MOVEMENT_THRESHOLD = 40; - - // [popup] initial coordinates and distance between fingers - private double initPointerDistance = -1; - private float initFirstPointerX = -1; - private float initFirstPointerY = -1; - private float initSecPointerX = -1; - private float initSecPointerY = -1; - public PlayerGestureListener(final VideoPlayerImpl playerImpl, final MainPlayer service) { - this.playerImpl = playerImpl; - this.service = service; - this.tossFlingVelocity = PlayerHelper.getTossFlingVelocity(service); + super(playerImpl, service); isVolumeGestureEnabled = PlayerHelper.isVolumeGestureEnabled(service); isBrightnessGestureEnabled = PlayerHelper.isBrightnessGestureEnabled(service); maxVolume = playerImpl.getAudioReactor().getMaxVolume(); } - /*////////////////////////////////////////////////////////////////////////// - // Helpers - //////////////////////////////////////////////////////////////////////////*/ - - /* - * Main and popup players' gesture listeners is too different. - * So it will be better to have different implementations of them - * */ - @Override - public boolean onDoubleTap(final MotionEvent e) { - if (DEBUG) { - Log.d(TAG, "onDoubleTap() called with: e = [" + e + "]" + "rawXy = " - + e.getRawX() + ", " + e.getRawY() + ", xy = " + e.getX() + ", " + e.getY()); - } - - if (playerImpl.popupPlayerSelected()) { - return onDoubleTapInPopup(e); - } else { - return onDoubleTapInMain(e); - } - } - @Override - public boolean onSingleTapConfirmed(final MotionEvent e) { + public void onDoubleTap(@NotNull final MotionEvent event, + @NotNull final DisplayPortion portion) { if (DEBUG) { - Log.d(TAG, "onSingleTapConfirmed() called with: e = [" + e + "]"); + Log.d(TAG, "onDoubleTap called with playerType = [" + + playerImpl.getPlayerType() + "], portion = [" + + portion + "]"); } - - if (playerImpl.popupPlayerSelected()) { - return onSingleTapConfirmedInPopup(e); - } else { - return onSingleTapConfirmedInMain(e); - } - } - - @Override - public boolean onDown(final MotionEvent e) { - if (DEBUG) { - Log.d(TAG, "onDown() called with: e = [" + e + "]"); + if (playerImpl.isSomePopupMenuVisible()) { + playerImpl.hideControls(0, 0); } - if (playerImpl.popupPlayerSelected()) { - return onDownInPopup(e); - } else { - return true; + if (portion == DisplayPortion.LEFT) { + playerImpl.onFastRewind(); + } else if (portion == DisplayPortion.MIDDLE) { + playerImpl.onPlayPause(); + } else if (portion == DisplayPortion.RIGHT) { + playerImpl.onFastForward(); } } @Override - public void onLongPress(final MotionEvent e) { + public void onSingleTap(@NotNull final MainPlayer.PlayerType playerType) { if (DEBUG) { - Log.d(TAG, "onLongPress() called with: e = [" + e + "]"); + Log.d(TAG, "onSingleTap called with playerType = [" + + playerImpl.getPlayerType() + "]"); } + if (playerType == MainPlayer.PlayerType.POPUP) { - if (playerImpl.popupPlayerSelected()) { - onLongPressInPopup(e); - } - } - - @Override - public boolean onScroll(final MotionEvent initialEvent, final MotionEvent movingEvent, - final float distanceX, final float distanceY) { - if (playerImpl.popupPlayerSelected()) { - return onScrollInPopup(initialEvent, movingEvent, distanceX, distanceY); - } else { - return onScrollInMain(initialEvent, movingEvent, distanceX, distanceY); - } - } + if (playerImpl.isControlsVisible()) { + playerImpl.hideControls(100, 100); + } else { + playerImpl.getPlayPauseButton().requestFocus(); + playerImpl.showControlsThenHide(); + } - @Override - public boolean onFling(final MotionEvent e1, final MotionEvent e2, - final float velocityX, final float velocityY) { - if (DEBUG) { - Log.d(TAG, "onFling() called with velocity: dX=[" - + velocityX + "], dY=[" + velocityY + "]"); - } + } else /* playerType == MainPlayer.PlayerType.VIDEO */ { - if (playerImpl.popupPlayerSelected()) { - return onFlingInPopup(e1, e2, velocityX, velocityY); - } else { - return true; + if (playerImpl.isControlsVisible()) { + playerImpl.hideControls(150, 0); + } else { + if (playerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED) { + playerImpl.showControls(0); + } else { + playerImpl.showControlsThenHide(); + } + } } } @Override - public boolean onTouch(final View v, final MotionEvent event) { - /*if (DEBUG && false) { - Log.d(TAG, "onTouch() called with: v = [" + v + "], event = [" + event + "]"); - }*/ - - if (playerImpl.popupPlayerSelected()) { - return onTouchInPopup(v, event); - } else { - return onTouchInMain(v, event); - } - } - - - /*////////////////////////////////////////////////////////////////////////// - // Main player listener - //////////////////////////////////////////////////////////////////////////*/ - - private boolean onDoubleTapInMain(final MotionEvent e) { - if (e.getX() > playerImpl.getRootView().getWidth() * 2.0 / 3.0) { - playerImpl.onFastForward(); - } else if (e.getX() < playerImpl.getRootView().getWidth() / 3.0) { - playerImpl.onFastRewind(); - } else { - playerImpl.getPlayPauseButton().performClick(); - } - - return true; - } - - - private boolean onSingleTapConfirmedInMain(final MotionEvent e) { + public void onScroll(@NotNull final MainPlayer.PlayerType playerType, + @NotNull final DisplayPortion portion, + @NotNull final MotionEvent initialEvent, + @NotNull final MotionEvent movingEvent, + final float distanceX, final float distanceY) { if (DEBUG) { - Log.d(TAG, "onSingleTapConfirmed() called with: e = [" + e + "]"); + Log.d(TAG, "onScroll called with playerType = [" + + playerImpl.getPlayerType() + "], portion = [" + + portion + "]"); } + if (playerType == MainPlayer.PlayerType.VIDEO) { + if (portion == DisplayPortion.LEFT_HALF) { + onScrollMainBrightness(distanceX, distanceY); - if (playerImpl.getCurrentState() == BasePlayer.STATE_BLOCKED) { - return true; - } + } else /* DisplayPortion.RIGHT_HALF */ { + onScrollMainVolume(distanceX, distanceY); + } - if (playerImpl.isControlsVisible()) { - playerImpl.hideControls(150, 0); - } else { - if (playerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED) { - playerImpl.showControls(0); + } else /* MainPlayer.PlayerType.POPUP */ { + final View closingOverlayView = playerImpl.getClosingOverlayView(); + if (playerImpl.isInsideClosingRadius(movingEvent)) { + if (closingOverlayView.getVisibility() == View.GONE) { + animateView(closingOverlayView, true, 250); + } } else { - playerImpl.showControlsThenHide(); + if (closingOverlayView.getVisibility() == View.VISIBLE) { + animateView(closingOverlayView, false, 0); + } } } - return true; } - private boolean onScrollInMain(final MotionEvent initialEvent, final MotionEvent movingEvent, - final float distanceX, final float distanceY) { - if ((!isVolumeGestureEnabled && !isBrightnessGestureEnabled) - || !playerImpl.isFullscreen()) { - return false; - } - - final boolean isTouchingStatusBar = initialEvent.getY() < getStatusBarHeight(service); - final boolean isTouchingNavigationBar = initialEvent.getY() - > playerImpl.getRootView().getHeight() - getNavigationBarHeight(service); - if (isTouchingStatusBar || isTouchingNavigationBar) { - return false; - } - - /*if (DEBUG && false) Log.d(TAG, "onScrollInMain = " + - ", e1.getRaw = [" + initialEvent.getRawX() + ", " + initialEvent.getRawY() + "]" + - ", e2.getRaw = [" + movingEvent.getRawX() + ", " + movingEvent.getRawY() + "]" + - ", distanceXy = [" + distanceX + ", " + distanceY + "]");*/ - - final boolean insideThreshold = - Math.abs(movingEvent.getY() - initialEvent.getY()) <= MOVEMENT_THRESHOLD; - if (!isMovingInMain && (insideThreshold || Math.abs(distanceX) > Math.abs(distanceY)) - || playerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED) { - return false; - } - - isMovingInMain = true; - - final boolean acceptAnyArea = isVolumeGestureEnabled != isBrightnessGestureEnabled; - final boolean acceptVolumeArea = acceptAnyArea - || initialEvent.getX() > playerImpl.getRootView().getWidth() / 2.0; - - if (isVolumeGestureEnabled && acceptVolumeArea) { + private void onScrollMainVolume(final float distanceX, final float distanceY) { + if (isVolumeGestureEnabled) { playerImpl.getVolumeProgressBar().incrementProgressBy((int) distanceY); final float currentProgressPercent = (float) playerImpl .getVolumeProgressBar().getProgress() / playerImpl.getMaxGestureLength(); @@ -258,10 +157,14 @@ private boolean onScrollInMain(final MotionEvent initialEvent, final MotionEvent if (playerImpl.getBrightnessRelativeLayout().getVisibility() == View.VISIBLE) { playerImpl.getBrightnessRelativeLayout().setVisibility(View.GONE); } - } else { + } + } + + private void onScrollMainBrightness(final float distanceX, final float distanceY) { + if (isBrightnessGestureEnabled) { final Activity parent = playerImpl.getParentActivity(); if (parent == null) { - return true; + return; } final Window window = parent.getWindow(); @@ -299,330 +202,71 @@ private boolean onScrollInMain(final MotionEvent initialEvent, final MotionEvent playerImpl.getVolumeRelativeLayout().setVisibility(View.GONE); } } - return true; } - private void onScrollEndInMain() { + @Override + public void onScrollEnd(@NotNull final MainPlayer.PlayerType playerType, + @NotNull final MotionEvent event) { if (DEBUG) { - Log.d(TAG, "onScrollEnd() called"); - } - - if (playerImpl.getVolumeRelativeLayout().getVisibility() == View.VISIBLE) { - animateView(playerImpl.getVolumeRelativeLayout(), SCALE_AND_ALPHA, false, 200, 200); - } - if (playerImpl.getBrightnessRelativeLayout().getVisibility() == View.VISIBLE) { - animateView(playerImpl.getBrightnessRelativeLayout(), SCALE_AND_ALPHA, false, 200, 200); - } - - if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == STATE_PLAYING) { - playerImpl.hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME); - } - } - - private boolean onTouchInMain(final View v, final MotionEvent event) { - playerImpl.getGestureDetector().onTouchEvent(event); - if (event.getAction() == MotionEvent.ACTION_UP && isMovingInMain) { - isMovingInMain = false; - onScrollEndInMain(); - } - // This hack allows to stop receiving touch events on appbar - // while touching video player's view - switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_MOVE: - v.getParent().requestDisallowInterceptTouchEvent(playerImpl.isFullscreen()); - return true; - case MotionEvent.ACTION_UP: - v.getParent().requestDisallowInterceptTouchEvent(false); - return false; - default: - return true; - } - } - - /*////////////////////////////////////////////////////////////////////////// - // Popup player listener - //////////////////////////////////////////////////////////////////////////*/ - - private boolean onDoubleTapInPopup(final MotionEvent e) { - if (playerImpl == null || !playerImpl.isPlaying()) { - return false; - } - - playerImpl.hideControls(0, 0); - - if (e.getX() > playerImpl.getPopupWidth() / 2) { - playerImpl.onFastForward(); - } else { - playerImpl.onFastRewind(); - } - - return true; - } - - private boolean onSingleTapConfirmedInPopup(final MotionEvent e) { - if (playerImpl == null || playerImpl.getPlayer() == null) { - return false; + Log.d(TAG, "onScrollEnd called with playerType = [" + + playerImpl.getPlayerType() + "]"); } - if (playerImpl.isControlsVisible()) { - playerImpl.hideControls(100, 100); - } else { - playerImpl.getPlayPauseButton().requestFocus(); - playerImpl.showControlsThenHide(); - } - return true; - } - - private boolean onDownInPopup(final MotionEvent e) { - // Fix popup position when the user touch it, it may have the wrong one - // because the soft input is visible (the draggable area is currently resized). - playerImpl.updateScreenSize(); - playerImpl.checkPopupPositionBounds(); - - initialPopupX = playerImpl.getPopupLayoutParams().x; - initialPopupY = playerImpl.getPopupLayoutParams().y; - playerImpl.setPopupWidth(playerImpl.getPopupLayoutParams().width); - playerImpl.setPopupHeight(playerImpl.getPopupLayoutParams().height); - return super.onDown(e); - } - - private void onLongPressInPopup(final MotionEvent e) { - playerImpl.updateScreenSize(); - playerImpl.checkPopupPositionBounds(); - playerImpl.updatePopupSize((int) playerImpl.getScreenWidth(), -1); - } - - private boolean onScrollInPopup(final MotionEvent initialEvent, - final MotionEvent movingEvent, - final float distanceX, - final float distanceY) { - if (isResizing || playerImpl == null) { - return super.onScroll(initialEvent, movingEvent, distanceX, distanceY); - } - - if (!isMovingInPopup) { - animateView(playerImpl.getCloseOverlayButton(), true, 200); - } - - isMovingInPopup = true; - - final float diffX = (int) (movingEvent.getRawX() - initialEvent.getRawX()); - float posX = (int) (initialPopupX + diffX); - final float diffY = (int) (movingEvent.getRawY() - initialEvent.getRawY()); - float posY = (int) (initialPopupY + diffY); - - if (posX > (playerImpl.getScreenWidth() - playerImpl.getPopupWidth())) { - posX = (int) (playerImpl.getScreenWidth() - playerImpl.getPopupWidth()); - } else if (posX < 0) { - posX = 0; - } - - if (posY > (playerImpl.getScreenHeight() - playerImpl.getPopupHeight())) { - posY = (int) (playerImpl.getScreenHeight() - playerImpl.getPopupHeight()); - } else if (posY < 0) { - posY = 0; - } - - playerImpl.getPopupLayoutParams().x = (int) posX; - playerImpl.getPopupLayoutParams().y = (int) posY; - - final View closingOverlayView = playerImpl.getClosingOverlayView(); - if (playerImpl.isInsideClosingRadius(movingEvent)) { - if (closingOverlayView.getVisibility() == View.GONE) { - animateView(closingOverlayView, true, 250); - } - } else { - if (closingOverlayView.getVisibility() == View.VISIBLE) { - animateView(closingOverlayView, false, 0); - } - } - -// if (DEBUG) { -// Log.d(TAG, "onScrollInPopup = " -// + "e1.getRaw = [" + initialEvent.getRawX() + ", " -// + initialEvent.getRawY() + "], " -// + "e1.getX,Y = [" + initialEvent.getX() + ", " -// + initialEvent.getY() + "], " -// + "e2.getRaw = [" + movingEvent.getRawX() + ", " -// + movingEvent.getRawY() + "], " -// + "e2.getX,Y = [" + movingEvent.getX() + ", " + movingEvent.getY() + "], " -// + "distanceX,Y = [" + distanceX + ", " + distanceY + "], " -// + "posX,Y = [" + posX + ", " + posY + "], " -// + "popupW,H = [" + popupWidth + " x " + popupHeight + "]"); -// } - playerImpl.windowManager - .updateViewLayout(playerImpl.getRootView(), playerImpl.getPopupLayoutParams()); - return true; - } - - private void onScrollEndInPopup(final MotionEvent event) { - if (playerImpl == null) { - return; - } - if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == STATE_PLAYING) { - playerImpl.hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME); - } - - if (playerImpl.isInsideClosingRadius(event)) { - playerImpl.closePopup(); - } else { - animateView(playerImpl.getClosingOverlayView(), false, 0); - - if (!playerImpl.isPopupClosing) { - animateView(playerImpl.getCloseOverlayButton(), false, 200); + if (playerType == MainPlayer.PlayerType.VIDEO) { + if (DEBUG) { + Log.d(TAG, "onScrollEnd() called"); } - } - } - private boolean onFlingInPopup(final MotionEvent e1, - final MotionEvent e2, - final float velocityX, - final float velocityY) { - if (playerImpl == null) { - return false; - } - - final float absVelocityX = Math.abs(velocityX); - final float absVelocityY = Math.abs(velocityY); - if (Math.max(absVelocityX, absVelocityY) > tossFlingVelocity) { - if (absVelocityX > tossFlingVelocity) { - playerImpl.getPopupLayoutParams().x = (int) velocityX; - } - if (absVelocityY > tossFlingVelocity) { - playerImpl.getPopupLayoutParams().y = (int) velocityY; + if (playerImpl.getVolumeRelativeLayout().getVisibility() == View.VISIBLE) { + animateView(playerImpl.getVolumeRelativeLayout(), SCALE_AND_ALPHA, + false, 200, 200); } - playerImpl.checkPopupPositionBounds(); - playerImpl.windowManager - .updateViewLayout(playerImpl.getRootView(), playerImpl.getPopupLayoutParams()); - return true; - } - return false; - } - - private boolean onTouchInPopup(final View v, final MotionEvent event) { - if (playerImpl == null) { - return false; - } - playerImpl.getGestureDetector().onTouchEvent(event); - - if (event.getPointerCount() == 2 && !isMovingInPopup && !isResizing) { - if (DEBUG) { - Log.d(TAG, "onTouch() 2 finger pointer detected, enabling resizing."); + if (playerImpl.getBrightnessRelativeLayout().getVisibility() == View.VISIBLE) { + animateView(playerImpl.getBrightnessRelativeLayout(), SCALE_AND_ALPHA, + false, 200, 200); } - playerImpl.showAndAnimateControl(-1, true); - playerImpl.getLoadingPanel().setVisibility(View.GONE); - - playerImpl.hideControls(0, 0); - animateView(playerImpl.getCurrentDisplaySeek(), false, 0, 0); - animateView(playerImpl.getResizingIndicator(), true, 200, 0); - //record coordinates of fingers - initFirstPointerX = event.getX(0); - initFirstPointerY = event.getY(0); - initSecPointerX = event.getX(1); - initSecPointerY = event.getY(1); - //record distance between fingers - initPointerDistance = Math.hypot(initFirstPointerX - initSecPointerX, - initFirstPointerY - initSecPointerY); - - isResizing = true; - } - if (event.getAction() == MotionEvent.ACTION_MOVE && !isMovingInPopup && isResizing) { - if (DEBUG) { - Log.d(TAG, "onTouch() ACTION_MOVE > v = [" + v + "], " - + "e1.getRaw = [" + event.getRawX() + ", " + event.getRawY() + "]"); + if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == STATE_PLAYING) { + playerImpl.hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME); } - return handleMultiDrag(event); - } - - if (event.getAction() == MotionEvent.ACTION_UP) { - if (DEBUG) { - Log.d(TAG, "onTouch() ACTION_UP > v = [" + v + "], " - + "e1.getRaw = [" + event.getRawX() + ", " + event.getRawY() + "]"); + } else { + if (playerImpl == null) { + return; } - if (isMovingInPopup) { - isMovingInPopup = false; - onScrollEndInPopup(event); + if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == STATE_PLAYING) { + playerImpl.hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME); } - if (isResizing) { - isResizing = false; - - initPointerDistance = -1; - initFirstPointerX = -1; - initFirstPointerY = -1; - initSecPointerX = -1; - initSecPointerY = -1; - - animateView(playerImpl.getResizingIndicator(), false, 100, 0); - playerImpl.changeState(playerImpl.getCurrentState()); - } + if (playerImpl.isInsideClosingRadius(event)) { + playerImpl.closePopup(); + } else { + animateView(playerImpl.getClosingOverlayView(), false, 0); - if (!playerImpl.isPopupClosing) { - playerImpl.savePositionAndSize(); + if (!playerImpl.isPopupClosing) { + animateView(playerImpl.getCloseOverlayButton(), false, 200); + } } } - - v.performClick(); - return true; } - private boolean handleMultiDrag(final MotionEvent event) { - if (initPointerDistance != -1 && event.getPointerCount() == 2) { - // get the movements of the fingers - final double firstPointerMove = Math.hypot(event.getX(0) - initFirstPointerX, - event.getY(0) - initFirstPointerY); - final double secPointerMove = Math.hypot(event.getX(1) - initSecPointerX, - event.getY(1) - initSecPointerY); - - // minimum threshold beyond which pinch gesture will work - final int minimumMove = ViewConfiguration.get(service).getScaledTouchSlop(); - - if (Math.max(firstPointerMove, secPointerMove) > minimumMove) { - // calculate current distance between the pointers - final double currentPointerDistance = - Math.hypot(event.getX(0) - event.getX(1), - event.getY(0) - event.getY(1)); - - final double popupWidth = playerImpl.getPopupWidth(); - // change co-ordinates of popup so the center stays at the same position - final double newWidth = (popupWidth * currentPointerDistance / initPointerDistance); - initPointerDistance = currentPointerDistance; - playerImpl.getPopupLayoutParams().x += (popupWidth - newWidth) / 2; - - playerImpl.checkPopupPositionBounds(); - playerImpl.updateScreenSize(); - - playerImpl.updatePopupSize( - (int) Math.min(playerImpl.getScreenWidth(), newWidth), - -1); - return true; - } + @Override + public void onPopupResizingStart() { + if (DEBUG) { + Log.d(TAG, "onPopupResizingStart called"); } - return false; - } - + playerImpl.showAndAnimateControl(-1, true); + playerImpl.getLoadingPanel().setVisibility(View.GONE); - /* - * Utils - * */ - - private int getNavigationBarHeight(final Context context) { - final int resId = context.getResources() - .getIdentifier("navigation_bar_height", "dimen", "android"); - if (resId > 0) { - return context.getResources().getDimensionPixelSize(resId); - } - return 0; + playerImpl.hideControls(0, 0); + animateView(playerImpl.getCurrentDisplaySeek(), false, 0, 0); + animateView(playerImpl.getResizingIndicator(), true, 200, 0); } - private int getStatusBarHeight(final Context context) { - final int resId = context.getResources() - .getIdentifier("status_bar_height", "dimen", "android"); - if (resId > 0) { - return context.getResources().getDimensionPixelSize(resId); + @Override + public void onPopupResizingEnd() { + if (DEBUG) { + Log.d(TAG, "onPopupResizingEnd called"); } - return 0; + animateView(playerImpl.getResizingIndicator(), false, 100, 0); } } diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/AudioReactor.java b/app/src/main/java/org/schabi/newpipe/player/helper/AudioReactor.java index f434b062120..ffe19599d20 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/AudioReactor.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/AudioReactor.java @@ -12,6 +12,7 @@ import android.util.Log; import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.analytics.AnalyticsListener; @@ -39,7 +40,7 @@ public AudioReactor(@NonNull final Context context, @NonNull final SimpleExoPlayer player) { this.player = player; this.context = context; - this.audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + this.audioManager = ContextCompat.getSystemService(context, AudioManager.class); player.addAnalyticsListener(this); if (SHOULD_BUILD_FOCUS_REQUEST) { @@ -163,7 +164,7 @@ public void onAnimationEnd(final Animator animation) { @Override public void onAudioSessionId(final EventTime eventTime, final int audioSessionId) { - if (!PlayerHelper.isUsingDSP(context)) { + if (!PlayerHelper.isUsingDSP()) { return; } diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/LockManager.java b/app/src/main/java/org/schabi/newpipe/player/helper/LockManager.java index 6d0cf8e85fd..270156fe977 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/LockManager.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/LockManager.java @@ -5,8 +5,7 @@ import android.os.PowerManager; import android.util.Log; -import static android.content.Context.POWER_SERVICE; -import static android.content.Context.WIFI_SERVICE; +import androidx.core.content.ContextCompat; public class LockManager { private final String TAG = "LockManager@" + hashCode(); @@ -18,10 +17,9 @@ public class LockManager { private WifiManager.WifiLock wifiLock; public LockManager(final Context context) { - powerManager = ((PowerManager) context.getApplicationContext() - .getSystemService(POWER_SERVICE)); - wifiManager = ((WifiManager) context.getApplicationContext() - .getSystemService(WIFI_SERVICE)); + powerManager = ContextCompat.getSystemService(context.getApplicationContext(), + PowerManager.class); + wifiManager = ContextCompat.getSystemService(context, WifiManager.class); } public void acquireWifiAndCpu() { diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java index 6efe7510c95..d89b5dd1930 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java @@ -8,6 +8,7 @@ import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; import androidx.preference.PreferenceManager; import com.google.android.exoplayer2.SeekParameters; @@ -83,12 +84,12 @@ public static String getTimeString(final int milliSeconds) { final int days = (milliSeconds % (86400000 * 7)) / 86400000; STRING_BUILDER.setLength(0); - return days > 0 + return (days > 0 ? STRING_FORMATTER.format("%d:%02d:%02d:%02d", days, hours, minutes, seconds) - .toString() : hours > 0 - ? STRING_FORMATTER.format("%d:%02d:%02d", hours, minutes, seconds).toString() - : STRING_FORMATTER.format("%02d:%02d", minutes, seconds).toString(); + ? STRING_FORMATTER.format("%d:%02d:%02d", hours, minutes, seconds) + : STRING_FORMATTER.format("%02d:%02d", minutes, seconds) + ).toString(); } public static String formatSpeed(final double speed) { @@ -298,7 +299,7 @@ public static int getPlaybackOptimalBufferMs() { return 60000; } - public static TrackSelection.Factory getQualitySelector(@NonNull final Context context) { + public static TrackSelection.Factory getQualitySelector() { return new AdaptiveTrackSelection.Factory( 1000, AdaptiveTrackSelection.DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS, @@ -306,18 +307,18 @@ public static TrackSelection.Factory getQualitySelector(@NonNull final Context c AdaptiveTrackSelection.DEFAULT_BANDWIDTH_FRACTION); } - public static boolean isUsingDSP(@NonNull final Context context) { + public static boolean isUsingDSP() { return true; } - public static int getTossFlingVelocity(@NonNull final Context context) { + public static int getTossFlingVelocity() { return 2500; } @NonNull public static CaptionStyleCompat getCaptionStyle(@NonNull final Context context) { - final CaptioningManager captioningManager = (CaptioningManager) - context.getSystemService(Context.CAPTIONING_SERVICE); + final CaptioningManager captioningManager = ContextCompat.getSystemService(context, + CaptioningManager.class); if (captioningManager == null || !captioningManager.isEnabled()) { return CaptionStyleCompat.DEFAULT; } @@ -340,8 +341,8 @@ public static CaptionStyleCompat getCaptionStyle(@NonNull final Context context) * @return caption scaling */ public static float getCaptionScale(@NonNull final Context context) { - final CaptioningManager captioningManager - = (CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE); + final CaptioningManager captioningManager = ContextCompat.getSystemService(context, + CaptioningManager.class); if (captioningManager == null || !captioningManager.isEnabled()) { return 1.0f; } diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHolder.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHolder.java index a5760eddc86..854e3eb2b26 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHolder.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHolder.java @@ -6,8 +6,12 @@ import android.content.ServiceConnection; import android.os.IBinder; import android.util.Log; + +import androidx.annotation.Nullable; + import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.PlaybackParameters; + import org.schabi.newpipe.App; import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.extractor.stream.StreamInfo; @@ -31,6 +35,31 @@ private PlayerHolder() { private static MainPlayer playerService; 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 boolean isPlaying() { + if (player == null) { + return false; + } + return player.isPlaying(); + } + + public static boolean isPlayerOpen() { + return player != null; + } + public static void setListener(final PlayerServiceExtendedEventListener newListener) { listener = newListener; // Force reload data from service diff --git a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java index 8bef0b2e023..b8bb677e033 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java +++ b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java @@ -1,12 +1,8 @@ package org.schabi.newpipe.player.playqueue; -import android.util.Log; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.player.playqueue.events.AppendEvent; import org.schabi.newpipe.player.playqueue.events.ErrorEvent; @@ -43,7 +39,6 @@ *

*/ public abstract class PlayQueue implements Serializable { - private final String TAG = "PlayQueue@" + Integer.toHexString(hashCode()); public static final boolean DEBUG = MainActivity.DEBUG; private ArrayList backup; @@ -55,7 +50,6 @@ public abstract class PlayQueue implements Serializable { private transient BehaviorSubject eventBroadcast; private transient Flowable broadcastReceiver; - private transient Subscription reportingReactor; private transient boolean disposed; @@ -87,10 +81,6 @@ public void init() { broadcastReceiver = eventBroadcast.toFlowable(BackpressureStrategy.BUFFER) .observeOn(AndroidSchedulers.mainThread()) .startWith(new InitEvent()); - - if (DEBUG) { - broadcastReceiver.subscribe(getSelfReporter()); - } } /** @@ -100,13 +90,9 @@ public void dispose() { if (eventBroadcast != null) { eventBroadcast.onComplete(); } - if (reportingReactor != null) { - reportingReactor.cancel(); - } eventBroadcast = null; broadcastReceiver = null; - reportingReactor = null; disposed = true; } @@ -167,19 +153,20 @@ public synchronized void setIndex(final int index) { } /** - * @return the current item that should be played + * @return the current item that should be played, or null if the queue is empty */ + @Nullable public PlayQueueItem getItem() { return getItem(getIndex()); } /** * @param index the index of the item to return - * @return the item at the given index - * @throws IndexOutOfBoundsException + * @return the item at the given index, or null if the index is out of bounds */ + @Nullable public PlayQueueItem getItem(final int index) { - if (index < 0 || index >= streams.size() || streams.get(index) == null) { + if (index < 0 || index >= streams.size()) { return null; } return streams.get(index); @@ -543,35 +530,5 @@ private void broadcast(@NonNull final PlayQueueEvent event) { eventBroadcast.onNext(event); } } - - private Subscriber getSelfReporter() { - return new Subscriber() { - @Override - public void onSubscribe(final Subscription s) { - if (reportingReactor != null) { - reportingReactor.cancel(); - } - reportingReactor = s; - reportingReactor.request(1); - } - - @Override - public void onNext(final PlayQueueEvent event) { - Log.d(TAG, "Received broadcast: " + event.type().name() + ". " - + "Current index: " + getIndex() + ", play queue length: " + size() + "."); - reportingReactor.request(1); - } - - @Override - public void onError(final Throwable t) { - Log.e(TAG, "Received broadcast error", t); - } - - @Override - public void onComplete() { - Log.d(TAG, "Broadcast is shutting down."); - } - }; - } } diff --git a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueItem.java b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueItem.java index 74aef07fadb..79efc03aeea 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueItem.java +++ b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueItem.java @@ -64,6 +64,20 @@ private PlayQueueItem(@Nullable final String name, @Nullable final String url, this.recoveryPosition = RECOVERY_UNSET; } + @Override + public boolean equals(final Object o) { + if (o instanceof PlayQueueItem) { + return url.equals(((PlayQueueItem) o).url); + } else { + return false; + } + } + + @Override + public int hashCode() { + return url.hashCode(); + } + @NonNull public String getTitle() { return title; diff --git a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueItemBuilder.java b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueItemBuilder.java index 1c50dc6b410..3e0865a3e97 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueItemBuilder.java +++ b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueItemBuilder.java @@ -52,7 +52,6 @@ public void buildStreamInfoItem(final PlayQueueItemHolder holder, final PlayQueu return false; }); - holder.itemThumbnailView.setOnTouchListener(getOnTouchListener(holder)); holder.itemHandle.setOnTouchListener(getOnTouchListener(holder)); } diff --git a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java index df30a21f3ba..37431fa582f 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java @@ -7,13 +7,13 @@ import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; -import androidx.preference.PreferenceManager; import android.util.Log; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.preference.Preference; +import androidx.preference.PreferenceManager; import com.nononsenseapps.filepicker.Utils; import com.nostra13.universalimageloader.core.ImageLoader; @@ -21,6 +21,7 @@ import org.schabi.newpipe.DownloaderImpl; import org.schabi.newpipe.NewPipeDatabase; import org.schabi.newpipe.R; +import org.schabi.newpipe.ReCaptchaActivity; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.localization.ContentCountry; import org.schabi.newpipe.extractor.localization.Localization; @@ -75,6 +76,22 @@ public void onCreate(@Nullable final Bundle savedInstanceState) { .getPreferredContentCountry(requireContext()); initialLanguage = PreferenceManager .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 diff --git a/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java deleted file mode 100644 index af3e3f5a905..00000000000 --- a/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java +++ /dev/null @@ -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); - } -} diff --git a/app/src/main/java/org/schabi/newpipe/settings/HistorySettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/HistorySettingsFragment.java index d9b404204ad..893e7c740e0 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/HistorySettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/HistorySettingsFragment.java @@ -55,7 +55,7 @@ public boolean onPreferenceTreeClick(final Preference preference) { .setNegativeButton(R.string.cancel, ((dialog, which) -> dialog.dismiss())) .setPositiveButton(R.string.delete, ((dialog, which) -> { final Disposable onDeletePlaybackStates - = recordManager.deleteCompelteStreamStateHistory() + = recordManager.deleteCompleteStreamStateHistory() .observeOn(AndroidSchedulers.mainThread()) .subscribe( howManyDeleted -> Toast.makeText(getActivity(), @@ -113,7 +113,7 @@ public boolean onPreferenceTreeClick(final Preference preference) { .setPositiveButton(R.string.delete, ((dialog, which) -> { final Disposable onDeletePlaybackStates - = recordManager.deleteCompelteStreamStateHistory() + = recordManager.deleteCompleteStreamStateHistory() .observeOn(AndroidSchedulers.mainThread()) .subscribe( howManyDeleted -> Toast.makeText(getActivity(), diff --git a/app/src/main/java/org/schabi/newpipe/settings/MainSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/MainSettingsFragment.java index 5502a06ebb4..2f65af4d6b0 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/MainSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/MainSettingsFragment.java @@ -4,7 +4,8 @@ import androidx.preference.Preference; -import org.schabi.newpipe.CheckForNewAppVersionTask; +import org.schabi.newpipe.App; +import org.schabi.newpipe.CheckForNewAppVersion; import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.R; @@ -15,16 +16,11 @@ public class MainSettingsFragment extends BasePreferenceFragment { public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) { addPreferencesFromResource(R.xml.main_settings); - if (!CheckForNewAppVersionTask.isGithubApk()) { + if (!CheckForNewAppVersion.isGithubApk(App.getApp())) { final Preference update = findPreference(getString(R.string.update_pref_screen_key)); getPreferenceScreen().removePreference(update); 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); - } } } diff --git a/app/src/main/java/org/schabi/newpipe/settings/NotificationSettingsFragment.kt b/app/src/main/java/org/schabi/newpipe/settings/NotificationSettingsFragment.kt new file mode 100644 index 00000000000..c68b699d35b --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/settings/NotificationSettingsFragment.kt @@ -0,0 +1,19 @@ +package org.schabi.newpipe.settings + +import android.os.Build +import android.os.Bundle +import androidx.preference.Preference +import org.schabi.newpipe.R + +class NotificationSettingsFragment : BasePreferenceFragment() { + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + addPreferencesFromResource(R.xml.notification_settings) + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + val colorizePref: Preference? = findPreference(getString(R.string.notification_colorize_key)) + colorizePref?.let { + preferenceScreen.removePreference(it) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java index c858c7f7719..153adf4c047 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java @@ -1,7 +1,6 @@ package org.schabi.newpipe.settings; import android.app.Activity; -import android.content.DialogInterface; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -34,6 +33,7 @@ import java.util.Vector; import io.reactivex.Flowable; +import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; public class SelectPlaylistFragment extends DialogFragment { @@ -46,12 +46,11 @@ public class SelectPlaylistFragment extends DialogFragment { private final ImageLoader imageLoader = ImageLoader.getInstance(); private OnSelectedListener onSelectedListener = null; - private OnCancelListener onCancelListener = null; private ProgressBar progressBar; private TextView emptyView; private RecyclerView recyclerView; - private Disposable playlistsSubscriber; + private Disposable disposable = null; private List playlists = new Vector<>(); @@ -59,10 +58,6 @@ public void setOnSelectedListener(final OnSelectedListener listener) { onSelectedListener = listener; } - public void setOnCancelListener(final OnCancelListener listener) { - onCancelListener = listener; - } - /*////////////////////////////////////////////////////////////////////////// // Fragment's Lifecycle //////////////////////////////////////////////////////////////////////////*/ @@ -70,15 +65,32 @@ public void setOnCancelListener(final OnCancelListener listener) { @Override public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) { - final View v = - inflater.inflate(R.layout.select_playlist_fragment, container, false); + final View v = inflater.inflate(R.layout.select_playlist_fragment, container, false); + progressBar = v.findViewById(R.id.progressBar); recyclerView = v.findViewById(R.id.items_list); + emptyView = v.findViewById(R.id.empty_state_view); + recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); final SelectPlaylistAdapter playlistAdapter = new SelectPlaylistAdapter(); recyclerView.setAdapter(playlistAdapter); - progressBar = v.findViewById(R.id.progressBar); - emptyView = v.findViewById(R.id.empty_state_view); + loadPlaylists(); + return v; + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (disposable != null) { + disposable.dispose(); + } + } + + /*////////////////////////////////////////////////////////////////////////// + // Load and display playlists + //////////////////////////////////////////////////////////////////////////*/ + + private void loadPlaylists() { progressBar.setVisibility(View.VISIBLE); recyclerView.setVisibility(View.GONE); emptyView.setVisibility(View.GONE); @@ -87,43 +99,36 @@ public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup final LocalPlaylistManager localPlaylistManager = new LocalPlaylistManager(database); final RemotePlaylistManager remotePlaylistManager = new RemotePlaylistManager(database); - playlistsSubscriber = Flowable.combineLatest(localPlaylistManager.getPlaylists(), + disposable = Flowable.combineLatest(localPlaylistManager.getPlaylists(), remotePlaylistManager.getPlaylists(), PlaylistLocalItem::merge) + .observeOn(AndroidSchedulers.mainThread()) .subscribe(this::displayPlaylists, this::onError); - - return v; } - @Override - public void onDestroy() { - super.onDestroy(); + private void displayPlaylists(final List newPlaylists) { + playlists = newPlaylists; + progressBar.setVisibility(View.GONE); + emptyView.setVisibility(newPlaylists.isEmpty() ? View.VISIBLE : View.GONE); + recyclerView.setVisibility(newPlaylists.isEmpty() ? View.GONE : View.VISIBLE); + } - if (playlistsSubscriber != null) { - playlistsSubscriber.dispose(); - playlistsSubscriber = null; - } + protected void onError(final Throwable e) { + final Activity activity = requireActivity(); + ErrorActivity.reportError(activity, e, activity.getClass(), null, ErrorActivity.ErrorInfo + .make(UserAction.UI_ERROR, "none", "load_playlists", R.string.app_ui_crash)); } /*////////////////////////////////////////////////////////////////////////// // Handle actions //////////////////////////////////////////////////////////////////////////*/ - @Override - public void onCancel(final DialogInterface dialogInterface) { - super.onCancel(dialogInterface); - if (onCancelListener != null) { - onCancelListener.onCancel(); - } - } - private void clickedItem(final int position) { if (onSelectedListener != null) { final LocalItem selectedItem = playlists.get(position); if (selectedItem instanceof PlaylistMetadataEntry) { final PlaylistMetadataEntry entry = ((PlaylistMetadataEntry) selectedItem); - onSelectedListener - .onLocalPlaylistSelected(entry.uid, entry.name); + onSelectedListener.onLocalPlaylistSelected(entry.uid, entry.name); } else if (selectedItem instanceof PlaylistRemoteEntity) { final PlaylistRemoteEntity entry = ((PlaylistRemoteEntity) selectedItem); @@ -134,31 +139,6 @@ private void clickedItem(final int position) { dismiss(); } - /*////////////////////////////////////////////////////////////////////////// - // Item handling - //////////////////////////////////////////////////////////////////////////*/ - - private void displayPlaylists(final List newPlaylists) { - this.playlists = newPlaylists; - progressBar.setVisibility(View.GONE); - if (newPlaylists.isEmpty()) { - emptyView.setVisibility(View.VISIBLE); - return; - } - recyclerView.setVisibility(View.VISIBLE); - - } - - /*////////////////////////////////////////////////////////////////////////// - // Error - //////////////////////////////////////////////////////////////////////////*/ - - protected void onError(final Throwable e) { - final Activity activity = getActivity(); - ErrorActivity.reportError(activity, e, activity.getClass(), null, ErrorActivity.ErrorInfo - .make(UserAction.UI_ERROR, "none", "", R.string.app_ui_crash)); - } - /*////////////////////////////////////////////////////////////////////////// // Interfaces //////////////////////////////////////////////////////////////////////////*/ @@ -168,22 +148,20 @@ public interface OnSelectedListener { void onRemotePlaylistSelected(int serviceId, String url, String name); } - public interface OnCancelListener { - void onCancel(); - } - private class SelectPlaylistAdapter extends RecyclerView.Adapter { + @NonNull @Override public SelectPlaylistItemHolder onCreateViewHolder(final ViewGroup parent, - final int viewType) { + final int viewType) { final View item = LayoutInflater.from(parent.getContext()) .inflate(R.layout.list_playlist_mini_item, parent, false); return new SelectPlaylistItemHolder(item); } @Override - public void onBindViewHolder(final SelectPlaylistItemHolder holder, final int position) { + public void onBindViewHolder(@NonNull final SelectPlaylistItemHolder holder, + final int position) { final PlaylistLocalItem selectedItem = playlists.get(position); if (selectedItem instanceof PlaylistMetadataEntry) { diff --git a/app/src/main/java/org/schabi/newpipe/settings/NotificationSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/custom/NotificationActionsPreference.java similarity index 70% rename from app/src/main/java/org/schabi/newpipe/settings/NotificationSettingsFragment.java rename to app/src/main/java/org/schabi/newpipe/settings/custom/NotificationActionsPreference.java index ce1e9e5a534..1fe405552db 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/NotificationSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/custom/NotificationActionsPreference.java @@ -1,12 +1,10 @@ -package org.schabi.newpipe.settings; +package org.schabi.newpipe.settings.custom; +import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; -import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; -import android.os.Build; -import android.os.Bundle; -import android.preference.PreferenceManager; +import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -15,16 +13,16 @@ import android.widget.LinearLayout; import android.widget.RadioButton; import android.widget.RadioGroup; -import android.widget.Switch; import android.widget.TextView; import android.widget.Toast; - import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.content.res.AppCompatResources; -import androidx.fragment.app.Fragment; - +import androidx.core.graphics.drawable.DrawableCompat; +import androidx.core.widget.TextViewCompat; +import androidx.preference.Preference; +import androidx.preference.PreferenceViewHolder; +import java.util.List; import org.schabi.newpipe.R; import org.schabi.newpipe.player.MainPlayer; import org.schabi.newpipe.player.NotificationConstants; @@ -32,56 +30,35 @@ import org.schabi.newpipe.util.ThemeHelper; import org.schabi.newpipe.views.FocusOverlayView; -import java.util.List; +public class NotificationActionsPreference extends Preference { + + public NotificationActionsPreference(final Context context, final AttributeSet attrs) { + super(context, attrs); + setLayoutResource(R.layout.settings_notification); + } -public class NotificationSettingsFragment extends Fragment { - private Switch scaleSwitch; private NotificationSlot[] notificationSlots; - private SharedPreferences pref; private List compactSlots; - private String scaleKey; //////////////////////////////////////////////////////////////////////////// // Lifecycle //////////////////////////////////////////////////////////////////////////// @Override - public void onCreate(@Nullable final Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - pref = PreferenceManager.getDefaultSharedPreferences(requireContext()); - scaleKey = getString(R.string.scale_to_square_image_in_notifications_key); - } - - @Override - public View onCreateView(@NonNull final LayoutInflater inflater, - final ViewGroup container, - @Nullable final Bundle savedInstanceState) { - return inflater.inflate(R.layout.settings_notification, container, false); - } - - @Override - public void onViewCreated(@NonNull final View rootView, - @Nullable final Bundle savedInstanceState) { - super.onViewCreated(rootView, savedInstanceState); + public void onBindViewHolder(final PreferenceViewHolder holder) { + super.onBindViewHolder(holder); - setupScaleSwitch(rootView); - setupActions(rootView); - } - - @Override - public void onResume() { - super.onResume(); - ThemeHelper.setTitleToAppCompatActivity(getActivity(), - getString(R.string.settings_category_notification_title)); + holder.itemView.setClickable(false); + setupActions(holder.itemView); } @Override - public void onPause() { - super.onPause(); + public void onDetached() { + super.onDetached(); saveChanges(); - requireContext().sendBroadcast(new Intent(MainPlayer.ACTION_RECREATE_NOTIFICATION)); + getContext().sendBroadcast(new Intent(MainPlayer.ACTION_RECREATE_NOTIFICATION)); } @@ -89,17 +66,10 @@ public void onPause() { // Setup //////////////////////////////////////////////////////////////////////////// - private void setupScaleSwitch(@NonNull final View view) { - scaleSwitch = view.findViewById(R.id.notificationScaleSwitch); - scaleSwitch.setChecked(pref.getBoolean(scaleKey, false)); - - view.findViewById(R.id.notificationScaleSwitchClickableArea) - .setOnClickListener(v -> scaleSwitch.toggle()); - } - private void setupActions(@NonNull final View view) { compactSlots = - NotificationConstants.getCompactSlotsFromPreferences(requireContext(), pref, 5); + NotificationConstants.getCompactSlotsFromPreferences( + getContext(), getSharedPreferences(), 5); notificationSlots = new NotificationSlot[5]; for (int i = 0; i < 5; i++) { notificationSlots[i] = new NotificationSlot(i, view); @@ -112,16 +82,15 @@ private void setupActions(@NonNull final View view) { //////////////////////////////////////////////////////////////////////////// private void saveChanges() { - final SharedPreferences.Editor editor = pref.edit(); - editor.putBoolean(scaleKey, scaleSwitch.isChecked()); + final SharedPreferences.Editor editor = getSharedPreferences().edit(); for (int i = 0; i < 3; i++) { - editor.putInt(getString(NotificationConstants.SLOT_COMPACT_PREF_KEYS[i]), + editor.putInt(getContext().getString(NotificationConstants.SLOT_COMPACT_PREF_KEYS[i]), (i < compactSlots.size() ? compactSlots.get(i) : -1)); } for (int i = 0; i < 5; i++) { - editor.putInt(getString(NotificationConstants.SLOT_PREF_KEYS[i]), + editor.putInt(getContext().getString(NotificationConstants.SLOT_PREF_KEYS[i]), notificationSlots[i].selectedAction); } @@ -183,7 +152,7 @@ void setupCheckbox(final View view) { } else if (compactSlots.size() < 3) { compactSlots.add(i); } else { - Toast.makeText(requireContext(), + Toast.makeText(getContext(), R.string.notification_actions_at_most_three, Toast.LENGTH_SHORT).show(); return; @@ -196,7 +165,8 @@ void setupCheckbox(final View view) { void setupSelectedAction(final View view) { icon = view.findViewById(R.id.notificationActionIcon); summary = view.findViewById(R.id.notificationActionSummary); - selectedAction = pref.getInt(getString(NotificationConstants.SLOT_PREF_KEYS[i]), + selectedAction = getSharedPreferences().getInt( + getContext().getString(NotificationConstants.SLOT_PREF_KEYS[i]), NotificationConstants.SLOT_DEFAULTS[i]); updateInfo(); } @@ -205,20 +175,20 @@ void updateInfo() { if (NotificationConstants.ACTION_ICONS[selectedAction] == 0) { icon.setImageDrawable(null); } else { - icon.setImageDrawable(AppCompatResources.getDrawable(requireContext(), + icon.setImageDrawable(AppCompatResources.getDrawable(getContext(), NotificationConstants.ACTION_ICONS[selectedAction])); } - summary.setText(NotificationConstants.getActionName(requireContext(), selectedAction)); + summary.setText(NotificationConstants.getActionName(getContext(), selectedAction)); } void openActionChooserDialog() { - final LayoutInflater inflater = LayoutInflater.from(requireContext()); + final LayoutInflater inflater = LayoutInflater.from(getContext()); final LinearLayout rootLayout = (LinearLayout) inflater.inflate( R.layout.single_choice_dialog_view, null, false); final RadioGroup radioGroup = rootLayout.findViewById(android.R.id.list); - final AlertDialog alertDialog = new AlertDialog.Builder(requireContext()) + final AlertDialog alertDialog = new AlertDialog.Builder(getContext()) .setTitle(SLOT_TITLES[i]) .setView(radioGroup) .setCancelable(true) @@ -237,22 +207,19 @@ void openActionChooserDialog() { // if present set action icon with correct color if (NotificationConstants.ACTION_ICONS[action] != 0) { - final Drawable drawable = AppCompatResources.getDrawable(requireContext(), + Drawable drawable = AppCompatResources.getDrawable(getContext(), NotificationConstants.ACTION_ICONS[action]); if (drawable != null) { - final int color = ThemeHelper.resolveColorFromAttr(requireContext(), + final int color = ThemeHelper.resolveColorFromAttr(getContext(), android.R.attr.textColorPrimary); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - drawable.setTint(color); - } else { - drawable.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN); - } - radioButton.setCompoundDrawablesWithIntrinsicBounds( + drawable = DrawableCompat.wrap(drawable).mutate(); + DrawableCompat.setTint(drawable, color); + TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(radioButton, null, null, drawable, null); } } - radioButton.setText(NotificationConstants.getActionName(requireContext(), action)); + radioButton.setText(NotificationConstants.getActionName(getContext(), action)); radioButton.setChecked(action == selectedAction); radioButton.setId(id); radioButton.setLayoutParams(new RadioGroup.LayoutParams( @@ -262,7 +229,7 @@ void openActionChooserDialog() { } alertDialog.show(); - if (DeviceUtils.isTv(requireContext())) { + if (DeviceUtils.isTv(getContext())) { FocusOverlayView.setupFocusObserver(alertDialog); } } diff --git a/app/src/main/java/org/schabi/newpipe/util/DeviceUtils.java b/app/src/main/java/org/schabi/newpipe/util/DeviceUtils.java index d852c2296fe..1afedcaef31 100644 --- a/app/src/main/java/org/schabi/newpipe/util/DeviceUtils.java +++ b/app/src/main/java/org/schabi/newpipe/util/DeviceUtils.java @@ -9,10 +9,9 @@ import android.view.KeyEvent; import androidx.annotation.NonNull; -import org.schabi.newpipe.App; +import androidx.core.content.ContextCompat; -import static android.content.Context.BATTERY_SERVICE; -import static android.content.Context.UI_MODE_SERVICE; +import org.schabi.newpipe.App; public final class DeviceUtils { @@ -30,15 +29,14 @@ public static boolean isTv(final Context context) { final PackageManager pm = App.getApp().getPackageManager(); // 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 || pm.hasSystemFeature(AMAZON_FEATURE_FIRE_TV) || pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION); // from https://stackoverflow.com/a/58932366 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - final boolean isBatteryAbsent - = ((BatteryManager) context.getSystemService(BATTERY_SERVICE)) + final boolean isBatteryAbsent = context.getSystemService(BatteryManager.class) .getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY) == 0; isTv = isTv || (isBatteryAbsent && !pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN) diff --git a/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java b/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java index a1a73d7acbf..e6dffaec42e 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java @@ -293,8 +293,8 @@ public static void handleGeneralException(final Context context, final int servi } else if (exception instanceof ContentNotSupportedException) { Toast.makeText(context, R.string.content_not_supported, Toast.LENGTH_LONG).show(); } else { - final int errorId = exception instanceof YoutubeStreamExtractor.DecryptException - ? R.string.youtube_signature_decryption_error + final int errorId = exception instanceof YoutubeStreamExtractor.DeobfuscateException + ? R.string.youtube_signature_deobfuscation_error : exception instanceof ParsingException ? R.string.parsing_error : R.string.general_error; ErrorActivity.reportError(handler, context, exception, MainActivity.class, null, diff --git a/app/src/main/java/org/schabi/newpipe/util/ListHelper.java b/app/src/main/java/org/schabi/newpipe/util/ListHelper.java index 7a428a05d1f..0c840f8c331 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ListHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/ListHelper.java @@ -3,6 +3,8 @@ import android.content.Context; import android.content.SharedPreferences; import android.net.ConnectivityManager; + +import androidx.core.content.ContextCompat; import androidx.preference.PreferenceManager; import androidx.annotation.Nullable; @@ -16,6 +18,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.List; @@ -263,10 +266,8 @@ static List getSortedStreamVideosList(final MediaFormat defaultForm */ private static void sortStreamList(final List videoStreams, final boolean ascendingOrder) { - Collections.sort(videoStreams, (o1, o2) -> { - final int result = compareVideoStreamResolution(o1, o2); - return result == 0 ? 0 : (ascendingOrder ? result : -result); - }); + final Comparator comparator = ListHelper::compareVideoStreamResolution; + Collections.sort(videoStreams, ascendingOrder ? comparator : comparator.reversed()); } /** @@ -543,7 +544,7 @@ private static String getResolutionLimit(final Context context) { */ public static boolean isMeteredNetwork(final Context context) { final ConnectivityManager manager - = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + = ContextCompat.getSystemService(context, ConnectivityManager.class); if (manager == null || manager.getActiveNetworkInfo() == null) { return false; } diff --git a/app/src/main/java/org/schabi/newpipe/util/Localization.java b/app/src/main/java/org/schabi/newpipe/util/Localization.java index 838e4e986bc..9cebfa8635b 100644 --- a/app/src/main/java/org/schabi/newpipe/util/Localization.java +++ b/app/src/main/java/org/schabi/newpipe/util/Localization.java @@ -5,13 +5,15 @@ import android.content.SharedPreferences; import android.content.res.Configuration; import android.content.res.Resources; -import androidx.preference.PreferenceManager; +import android.icu.text.CompactDecimalFormat; +import android.os.Build; import android.text.TextUtils; import android.util.DisplayMetrics; import androidx.annotation.NonNull; import androidx.annotation.PluralsRes; import androidx.annotation.StringRes; +import androidx.preference.PreferenceManager; import org.ocpsoft.prettytime.PrettyTime; import org.ocpsoft.prettytime.units.Decade; @@ -21,11 +23,13 @@ import java.math.BigDecimal; import java.math.RoundingMode; -import java.text.DateFormat; import java.text.NumberFormat; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; import java.util.Arrays; import java.util.Calendar; -import java.util.Date; import java.util.List; import java.util.Locale; @@ -137,13 +141,16 @@ public static String localizeNumber(final Context context, final double number) return nf.format(number); } - public static String formatDate(final Date date, final Context context) { - return DateFormat.getDateInstance(DateFormat.MEDIUM, getAppLocale(context)).format(date); + public static String formatDate(final OffsetDateTime offsetDateTime, final Context context) { + return DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM) + .withLocale(getAppLocale(context)).format(offsetDateTime + .atZoneSameInstant(ZoneId.systemDefault())); } @SuppressLint("StringFormatInvalid") - public static String localizeUploadDate(final Context context, final Date date) { - return context.getString(R.string.upload_date_text, formatDate(date, context)); + public static String localizeUploadDate(final Context context, + final OffsetDateTime offsetDateTime) { + return context.getString(R.string.upload_date_text, formatDate(offsetDateTime, context)); } public static String localizeViewCount(final Context context, final long viewCount) { @@ -184,6 +191,11 @@ public static String localizeWatchingCount(final Context context, final long wat } public static String shortCount(final Context context, final long count) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + return CompactDecimalFormat.getInstance(getAppLocale(context), + CompactDecimalFormat.CompactStyle.SHORT).format(count); + } + final double value = (double) count; if (count >= 1000000000) { return localizeNumber(context, round(value / 1000000000, 1)) diff --git a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java index eef70c1e5fb..b45a1e7b9bc 100644 --- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java @@ -7,14 +7,14 @@ import android.content.Intent; import android.net.Uri; import android.os.Build; -import androidx.preference.PreferenceManager; import android.util.Log; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; @@ -37,7 +37,6 @@ import org.schabi.newpipe.fragments.MainFragment; import org.schabi.newpipe.fragments.detail.VideoDetailFragment; import org.schabi.newpipe.fragments.list.channel.ChannelFragment; -import org.schabi.newpipe.fragments.list.comments.CommentsFragment; import org.schabi.newpipe.fragments.list.kiosk.KioskFragment; import org.schabi.newpipe.fragments.list.playlist.PlaylistFragment; import org.schabi.newpipe.fragments.list.search.SearchFragment; @@ -51,13 +50,14 @@ import org.schabi.newpipe.player.BasePlayer; import org.schabi.newpipe.player.MainPlayer; import org.schabi.newpipe.player.VideoPlayer; +import org.schabi.newpipe.player.helper.PlayerHelper; +import org.schabi.newpipe.player.helper.PlayerHolder; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlayQueueItem; import org.schabi.newpipe.settings.SettingsActivity; import java.util.ArrayList; -@SuppressWarnings({"unused"}) public final class NavigationHelper { public static final String MAIN_FRAGMENT_TAG = "main_fragment_tag"; public static final String SEARCH_FRAGMENT_TAG = "search_fragment_tag"; @@ -72,7 +72,6 @@ private NavigationHelper() { } public static Intent getPlayerIntent(@NonNull final Context context, @NonNull final Class targetClazz, @Nullable final PlayQueue playQueue, - @Nullable final String quality, final boolean resumePlayback) { final Intent intent = new Intent(context, targetClazz); @@ -82,9 +81,6 @@ public static Intent getPlayerIntent(@NonNull final Context context, intent.putExtra(VideoPlayer.PLAY_QUEUE_KEY, cacheKey); } } - if (quality != null) { - intent.putExtra(VideoPlayer.PLAYBACK_QUALITY, quality); - } intent.putExtra(VideoPlayer.RESUME_PLAYBACK, resumePlayback); intent.putExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_VIDEO); @@ -95,8 +91,10 @@ public static Intent getPlayerIntent(@NonNull final Context context, public static Intent getPlayerIntent(@NonNull final Context context, @NonNull final Class targetClazz, @Nullable final PlayQueue playQueue, - final boolean resumePlayback) { - return getPlayerIntent(context, targetClazz, playQueue, null, resumePlayback); + final boolean resumePlayback, + final boolean playWhenReady) { + return getPlayerIntent(context, targetClazz, playQueue, resumePlayback) + .putExtra(BasePlayer.PLAY_WHEN_READY, playWhenReady); } @NonNull @@ -110,61 +108,25 @@ public static Intent getPlayerEnqueueIntent(@NonNull final Context context, .putExtra(BasePlayer.SELECT_ON_APPEND, selectOnAppend); } - @NonNull - public static Intent getPlayerIntent(@NonNull final Context context, - @NonNull final Class targetClazz, - @Nullable final PlayQueue playQueue, - final int repeatMode, - final float playbackSpeed, - final float playbackPitch, - final boolean playbackSkipSilence, - @Nullable final String playbackQuality, - final boolean resumePlayback, - final boolean startPaused, - final boolean isMuted) { - return getPlayerIntent(context, targetClazz, playQueue, playbackQuality, resumePlayback) - .putExtra(BasePlayer.REPEAT_MODE, repeatMode) - .putExtra(BasePlayer.START_PAUSED, startPaused) - .putExtra(BasePlayer.IS_MUTED, isMuted); + public static void playOnMainPlayer(final AppCompatActivity activity, + @NonNull final PlayQueue playQueue) { + final PlayQueueItem item = playQueue.getItem(); + assert item != null; + openVideoDetailFragment(activity, activity.getSupportFragmentManager(), + item.getServiceId(), item.getUrl(), item.getTitle(), playQueue, false); } - public static void playOnMainPlayer(final AppCompatActivity activity, - final PlayQueue queue, - final boolean autoPlay) { - playOnMainPlayer(activity.getSupportFragmentManager(), queue, autoPlay); - } - - public static void playOnMainPlayer(final FragmentManager fragmentManager, - final PlayQueue queue, - final boolean autoPlay) { - final PlayQueueItem currentStream = queue.getItem(); - openVideoDetailFragment( - fragmentManager, - currentStream.getServiceId(), - currentStream.getUrl(), - currentStream.getTitle(), - autoPlay, - queue); - } - - public static void playOnMainPlayer(@NonNull final Context context, - @Nullable final PlayQueue queue, - @NonNull final StreamingService.LinkType linkType, - @NonNull final String url, - @NonNull final String title, - final boolean autoPlay, - final boolean resumePlayback) { - - final Intent intent = getPlayerIntent(context, MainActivity.class, queue, resumePlayback); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra(Constants.KEY_LINK_TYPE, linkType); - intent.putExtra(Constants.KEY_URL, url); - intent.putExtra(Constants.KEY_TITLE, title); - intent.putExtra(VideoDetailFragment.AUTO_PLAY, autoPlay); - context.startActivity(intent); + public static void playOnMainPlayer(final Context context, + @NonNull final PlayQueue playQueue, + final boolean switchingPlayers) { + final PlayQueueItem item = playQueue.getItem(); + assert item != null; + openVideoDetail(context, + item.getServiceId(), item.getUrl(), item.getTitle(), playQueue, switchingPlayers); } - public static void playOnPopupPlayer(final Context context, final PlayQueue queue, + public static void playOnPopupPlayer(final Context context, + final PlayQueue queue, final boolean resumePlayback) { if (!PermissionHelper.isPopupEnabled(context)) { PermissionHelper.showPopupEnablementToast(context); @@ -174,7 +136,7 @@ public static void playOnPopupPlayer(final Context context, final PlayQueue queu Toast.makeText(context, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show(); final Intent intent = getPlayerIntent(context, MainPlayer.class, queue, resumePlayback); intent.putExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_POPUP); - startService(context, intent); + ContextCompat.startForegroundService(context, intent); } public static void playOnBackgroundPlayer(final Context context, @@ -184,7 +146,24 @@ public static void playOnBackgroundPlayer(final Context context, .show(); final Intent intent = getPlayerIntent(context, MainPlayer.class, queue, resumePlayback); 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, @@ -200,11 +179,11 @@ public static void enqueueOnPopupPlayer(final Context context, final PlayQueue q 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( context, MainPlayer.class, queue, selectOnAppend, resumePlayback); 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, @@ -216,19 +195,11 @@ public static void enqueueOnBackgroundPlayer(final Context context, final PlayQueue queue, final boolean selectOnAppend, 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( context, MainPlayer.class, queue, selectOnAppend, resumePlayback); intent.putExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_AUDIO); - startService(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); - } + ContextCompat.startForegroundService(context, intent); } /*////////////////////////////////////////////////////////////////////////// @@ -290,9 +261,6 @@ public static void resolveActivityOrAskToInstall(final Context context, final In .setNegativeButton(R.string.cancel, (dialog, which) -> Log.i("NavigationHelper", "You unlocked a secret unicorn.")) .show(); -// Log.e("NavigationHelper", -// "Either no Streaming player for audio was installed, " -// + "or something important crashed:"); } else { Toast.makeText(context, R.string.no_player_found_toast, Toast.LENGTH_LONG).show(); } @@ -348,41 +316,6 @@ public static void openSearchFragment(final FragmentManager fragmentManager, .commit(); } - public static void openVideoDetailFragment(final FragmentManager fragmentManager, - final int serviceId, final String url, - final String title) { - openVideoDetailFragment(fragmentManager, serviceId, url, title, true, null); - } - - public static void openVideoDetailFragment( - final FragmentManager fragmentManager, - final int serviceId, - final String url, - final String title, - final boolean autoPlay, - final PlayQueue playQueue) { - final Fragment fragment = fragmentManager.findFragmentById(R.id.fragment_player_holder); - - if (fragment instanceof VideoDetailFragment && fragment.isVisible()) { - expandMainPlayer(fragment.requireActivity()); - final VideoDetailFragment detailFragment = (VideoDetailFragment) fragment; - detailFragment.setAutoplay(autoPlay); - detailFragment - .selectAndLoadVideo(serviceId, url, title == null ? "" : title, playQueue); - detailFragment.scrollToTop(); - return; - } - - final VideoDetailFragment instance = VideoDetailFragment - .getInstance(serviceId, url, title == null ? "" : title, playQueue); - instance.setAutoplay(autoPlay); - - defaultTransaction(fragmentManager) - .replace(R.id.fragment_player_holder, instance) - .runOnCommit(() -> expandMainPlayer(instance.requireActivity())) - .commit(); - } - public static void expandMainPlayer(final Context context) { context.sendBroadcast(new Intent(VideoDetailFragment.ACTION_SHOW_MAIN_PLAYER)); } @@ -396,36 +329,79 @@ public static void showMiniPlayer(final FragmentManager fragmentManager) { defaultTransaction(fragmentManager) .replace(R.id.fragment_player_holder, instance) .runOnCommit(() -> sendPlayerStartedEvent(instance.requireActivity())) - .commit(); + .commitAllowingStateLoss(); + } + + private interface RunnableWithVideoDetailFragment { + void run(VideoDetailFragment detailFragment); + } + + public static void openVideoDetailFragment(@NonNull final Context context, + @NonNull final FragmentManager fragmentManager, + final int serviceId, + @Nullable final String url, + @NonNull final String title, + @Nullable final PlayQueue playQueue, + final boolean switchingPlayers) { + + final boolean autoPlay; + @Nullable final MainPlayer.PlayerType playerType = PlayerHolder.getType(); + if (playerType == null) { + // no player open + autoPlay = PlayerHelper.isAutoplayAllowedByUser(context); + } else if (switchingPlayers) { + // switching player to main player + autoPlay = PlayerHolder.isPlaying(); // keep play/pause state + } else if (playerType == MainPlayer.PlayerType.VIDEO) { + // opening new stream while already playing in main player + autoPlay = PlayerHelper.isAutoplayAllowedByUser(context); + } else { + // opening new stream while already playing in another player + autoPlay = false; + } + + final RunnableWithVideoDetailFragment onVideoDetailFragmentReady = (detailFragment) -> { + expandMainPlayer(detailFragment.requireActivity()); + detailFragment.setAutoPlay(autoPlay); + if (switchingPlayers) { + // Situation when user switches from players to main player. All needed data is + // here, we can start watching (assuming newQueue equals playQueue). + detailFragment.openVideoPlayer(); + } else { + detailFragment.selectAndLoadVideo(serviceId, url, title, playQueue); + } + detailFragment.scrollToTop(); + }; + + final Fragment fragment = fragmentManager.findFragmentById(R.id.fragment_player_holder); + if (fragment instanceof VideoDetailFragment && fragment.isVisible()) { + onVideoDetailFragmentReady.run((VideoDetailFragment) fragment); + } else { + final VideoDetailFragment instance = VideoDetailFragment + .getInstance(serviceId, url, title, playQueue); + instance.setAutoPlay(autoPlay); + + defaultTransaction(fragmentManager) + .replace(R.id.fragment_player_holder, instance) + .runOnCommit(() -> onVideoDetailFragmentReady.run(instance)) + .commit(); + } } public static void openChannelFragment(final FragmentManager fragmentManager, final int serviceId, final String url, - final String name) { + @NonNull final String name) { defaultTransaction(fragmentManager) - .replace(R.id.fragment_holder, ChannelFragment.getInstance(serviceId, url, - name == null ? "" : name)) - .addToBackStack(null) - .commit(); - } - - public static void openCommentsFragment(final FragmentManager fragmentManager, - final int serviceId, final String url, - final String name) { - fragmentManager.beginTransaction() - .setCustomAnimations(R.anim.switch_service_in, R.anim.switch_service_out) - .replace(R.id.fragment_holder, CommentsFragment.getInstance(serviceId, url, - name == null ? "" : name)) + .replace(R.id.fragment_holder, ChannelFragment.getInstance(serviceId, url, name)) .addToBackStack(null) .commit(); } public static void openPlaylistFragment(final FragmentManager fragmentManager, final int serviceId, final String url, - final String name) { + @NonNull final String name) { defaultTransaction(fragmentManager) - .replace(R.id.fragment_holder, PlaylistFragment.getInstance(serviceId, url, - name == null ? "" : name)) + .replace(R.id.fragment_holder, PlaylistFragment.getInstance(serviceId, url, name)) .addToBackStack(null) .commit(); } @@ -501,33 +477,26 @@ public static void openSearch(final Context context, final int serviceId, context.startActivity(mIntent); } - public static void openChannel(final Context context, final int serviceId, final String url) { - openChannel(context, serviceId, url, null); - } + public static void openVideoDetail(final Context context, + final int serviceId, + final String url, + @NonNull final String title, + @Nullable final PlayQueue playQueue, + final boolean switchingPlayers) { - public static void openChannel(final Context context, final int serviceId, - final String url, final String name) { - final Intent openIntent = getOpenIntent(context, url, serviceId, - StreamingService.LinkType.CHANNEL); - if (name != null && !name.isEmpty()) { - openIntent.putExtra(Constants.KEY_TITLE, name); - } - context.startActivity(openIntent); - } - - public static void openVideoDetail(final Context context, final int serviceId, - final String url) { - openVideoDetail(context, serviceId, url, null); - } - - public static void openVideoDetail(final Context context, final int serviceId, - final String url, final String title) { - final Intent openIntent = getOpenIntent(context, url, serviceId, + final Intent intent = getOpenIntent(context, url, serviceId, StreamingService.LinkType.STREAM); - if (title != null && !title.isEmpty()) { - openIntent.putExtra(Constants.KEY_TITLE, title); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(Constants.KEY_TITLE, title); + intent.putExtra(VideoDetailFragment.KEY_SWITCHING_PLAYERS, switchingPlayers); + + if (playQueue != null) { + final String cacheKey = SerializedCache.getInstance().put(playQueue, PlayQueue.class); + if (cacheKey != null) { + intent.putExtra(VideoPlayer.PLAY_QUEUE_KEY, cacheKey); + } } - context.startActivity(openIntent); + context.startActivity(intent); } public static void openMainActivity(final Context context) { @@ -540,7 +509,6 @@ public static void openMainActivity(final Context context) { public static void openRouterActivity(final Context context, final String url) { final Intent mIntent = new Intent(context, RouterActivity.class); mIntent.setData(Uri.parse(url)); - mIntent.putExtra(RouterActivity.INTERNAL_ROUTE_KEY, true); context.startActivity(mIntent); } @@ -554,14 +522,12 @@ public static void openSettings(final Context context) { context.startActivity(intent); } - public static boolean openDownloads(final Activity activity) { - if (!PermissionHelper.checkStoragePermissions( + public static void openDownloads(final Activity activity) { + if (PermissionHelper.checkStoragePermissions( activity, PermissionHelper.DOWNLOADS_REQUEST_CODE)) { - return false; + final Intent intent = new Intent(activity, DownloadActivity.class); + activity.startActivity(intent); } - final Intent intent = new Intent(activity, DownloadActivity.class); - activity.startActivity(intent); - return true; } public static Intent getPlayQueueActivityIntent(final Context context) { @@ -590,7 +556,8 @@ public static Intent getIntentByLink(final Context context, final String url) return getIntentByLink(context, NewPipe.getServiceByUrl(url), url); } - public static Intent getIntentByLink(final Context context, final StreamingService service, + public static Intent getIntentByLink(final Context context, + final StreamingService service, final String url) throws ExtractionException { final StreamingService.LinkType linkType = service.getLinkTypeByUrl(url); @@ -599,15 +566,7 @@ public static Intent getIntentByLink(final Context context, final StreamingServi + " url=" + url); } - final Intent rIntent = getOpenIntent(context, url, service.getServiceId(), linkType); - - if (linkType == StreamingService.LinkType.STREAM) { - rIntent.putExtra(VideoDetailFragment.AUTO_PLAY, - PreferenceManager.getDefaultSharedPreferences(context).getBoolean( - context.getString(R.string.autoplay_through_intent_key), false)); - } - - return rIntent; + return getOpenIntent(context, url, service.getServiceId(), linkType); } private static Uri openMarketUrl(final String packageName) { diff --git a/app/src/main/java/org/schabi/newpipe/util/ShareUtils.java b/app/src/main/java/org/schabi/newpipe/util/ShareUtils.java index 1283f67f519..b631f19da53 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ShareUtils.java +++ b/app/src/main/java/org/schabi/newpipe/util/ShareUtils.java @@ -9,6 +9,8 @@ import android.net.Uri; import android.widget.Toast; +import androidx.core.content.ContextCompat; + import org.schabi.newpipe.R; public final class ShareUtils { @@ -95,7 +97,7 @@ public static void shareUrl(final Context context, final String subject, final S */ public static void copyToClipboard(final Context context, final String text) { final ClipboardManager clipboardManager = - (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); + ContextCompat.getSystemService(context, ClipboardManager.class); if (clipboardManager == null) { Toast.makeText(context, diff --git a/app/src/main/java/org/schabi/newpipe/util/StreamDialogEntry.java b/app/src/main/java/org/schabi/newpipe/util/StreamDialogEntry.java index a1e2e6eb9d2..34ff637ad61 100644 --- a/app/src/main/java/org/schabi/newpipe/util/StreamDialogEntry.java +++ b/app/src/main/java/org/schabi/newpipe/util/StreamDialogEntry.java @@ -7,22 +7,41 @@ import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.stream.StreamInfoItem; 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 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 { ////////////////////////////////////// // enum values with DEFAULT actions // ////////////////////////////////////// - enqueue_on_background(R.string.enqueue_on_background, (fragment, item) -> - NavigationHelper.enqueueOnBackgroundPlayer(fragment.getContext(), - new SinglePlayQueue(item), false)), + /** + * Enqueues the stream automatically to the current PlayerType.
+ *
+ * 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(), - 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) -> NavigationHelper.playOnBackgroundPlayer(fragment.getContext(), @@ -40,8 +59,14 @@ public enum StreamDialogEntry { append_playlist(R.string.append_playlist, (fragment, item) -> { if (fragment.getFragmentManager() != null) { - PlaylistAppendDialog.fromStreamInfoItems(Collections.singletonList(item)) - .show(fragment.getFragmentManager(), "StreamDialogEntry@append_playlist"); + final PlaylistAppendDialog d = PlaylistAppendDialog + .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 // /////////////////////////////////////////////////////// + public static void setEnabledEntries(final List entries) { + setEnabledEntries(entries.toArray(new StreamDialogEntry[0])); + } + /** * To be called before using {@link #setCustomAction(StreamDialogEntryAction)}. * diff --git a/app/src/main/java/org/schabi/newpipe/views/ExpandableSurfaceView.java b/app/src/main/java/org/schabi/newpipe/views/ExpandableSurfaceView.java index a23172bd318..e7a028d508d 100644 --- a/app/src/main/java/org/schabi/newpipe/views/ExpandableSurfaceView.java +++ b/app/src/main/java/org/schabi/newpipe/views/ExpandableSurfaceView.java @@ -1,8 +1,7 @@ package org.schabi.newpipe.views; import android.content.Context; -import android.os.Build.VERSION; -import android.os.Build.VERSION_CODES; +import android.os.Build; import android.util.AttributeSet; import android.view.SurfaceView; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; @@ -47,7 +46,8 @@ protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec if (resizeMode == RESIZE_MODE_FIT // KitKat doesn't work well when a view has a scale like needed for ZOOM - || (resizeMode == RESIZE_MODE_ZOOM && VERSION.SDK_INT < VERSION_CODES.LOLLIPOP)) { + || (resizeMode == RESIZE_MODE_ZOOM + && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)) { if (aspectDeformation > 0) { height = (int) (width / videoAspectRatio); } else { diff --git a/app/src/main/java/org/schabi/newpipe/views/FocusOverlayView.java b/app/src/main/java/org/schabi/newpipe/views/FocusOverlayView.java index dc5bf7133b3..29c38511c2e 100644 --- a/app/src/main/java/org/schabi/newpipe/views/FocusOverlayView.java +++ b/app/src/main/java/org/schabi/newpipe/views/FocusOverlayView.java @@ -270,7 +270,7 @@ private static void fixFocusHierarchy(final View decor) { clearFocusObstacles((ViewGroup) decor); } - @RequiresApi(api = 26) + @RequiresApi(api = Build.VERSION_CODES.O) private static void clearFocusObstacles(final ViewGroup viewGroup) { viewGroup.setTouchscreenBlocksFocus(false); diff --git a/app/src/main/java/us/shandian/giga/get/Mission.java b/app/src/main/java/us/shandian/giga/get/Mission.java index 8e814a2aff5..ff131988428 100644 --- a/app/src/main/java/us/shandian/giga/get/Mission.java +++ b/app/src/main/java/us/shandian/giga/get/Mission.java @@ -35,6 +35,10 @@ public abstract class Mission implements Serializable { */ public StoredFileHelper storage; + public long getTimestamp() { + return timestamp; + } + /** * Delete the downloaded file * diff --git a/app/src/main/java/us/shandian/giga/io/StoredDirectoryHelper.java b/app/src/main/java/us/shandian/giga/io/StoredDirectoryHelper.java index 8f6070ff4d3..8f7e18a3173 100644 --- a/app/src/main/java/us/shandian/giga/io/StoredDirectoryHelper.java +++ b/app/src/main/java/us/shandian/giga/io/StoredDirectoryHelper.java @@ -212,7 +212,7 @@ public boolean canWrite() { @NonNull @Override public String toString() { - return docTree == null ? Uri.fromFile(ioTree).toString() : docTree.getUri().toString(); + return (docTree == null ? Uri.fromFile(ioTree) : docTree.getUri()).toString(); } diff --git a/app/src/main/java/us/shandian/giga/service/DownloadManager.java b/app/src/main/java/us/shandian/giga/service/DownloadManager.java index 994c6ee63ea..dc4d5701bc9 100644 --- a/app/src/main/java/us/shandian/giga/service/DownloadManager.java +++ b/app/src/main/java/us/shandian/giga/service/DownloadManager.java @@ -12,7 +12,8 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collections; -import java.util.Iterator; +import java.util.Comparator; +import java.util.List; import us.shandian.giga.get.DownloadMission; import us.shandian.giga.get.FinishedMission; @@ -198,7 +199,7 @@ private void loadPendingMissions(Context ctx) { } if (mMissionsPending.size() > 1) - Collections.sort(mMissionsPending, (mission1, mission2) -> Long.compare(mission1.timestamp, mission2.timestamp)); + Collections.sort(mMissionsPending, Comparator.comparingLong(Mission::getTimestamp)); } /** @@ -563,14 +564,10 @@ private ArrayList getSpecialItems() { synchronized (DownloadManager.this) { ArrayList pending = new ArrayList<>(mMissionsPending); ArrayList finished = new ArrayList<>(mMissionsFinished); - ArrayList remove = new ArrayList<>(hidden); + List remove = new ArrayList<>(hidden); // hide missions (if required) - Iterator iterator = remove.iterator(); - while (iterator.hasNext()) { - Mission mission = iterator.next(); - if (pending.remove(mission) || finished.remove(mission)) iterator.remove(); - } + remove.removeIf(mission -> pending.remove(mission) || finished.remove(mission)); int fakeTotal = pending.size(); if (fakeTotal > 0) fakeTotal++; diff --git a/app/src/main/java/us/shandian/giga/service/DownloadManagerService.java b/app/src/main/java/us/shandian/giga/service/DownloadManagerService.java index 828f1adaf91..0392e723538 100755 --- a/app/src/main/java/us/shandian/giga/service/DownloadManagerService.java +++ b/app/src/main/java/us/shandian/giga/service/DownloadManagerService.java @@ -24,6 +24,8 @@ import android.os.IBinder; import android.os.Message; import android.os.Parcelable; + +import androidx.core.content.ContextCompat; import androidx.preference.PreferenceManager; import android.util.Log; import android.util.SparseArray; @@ -157,8 +159,10 @@ public void onCreate() { mNotification = builder.build(); - mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - mConnectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); + mNotificationManager = ContextCompat.getSystemService(this, + NotificationManager.class); + mConnectivityManager = ContextCompat.getSystemService(this, + ConnectivityManager.class); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { mNetworkStateListenerL = new ConnectivityManager.NetworkCallback() { diff --git a/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java b/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java index c9518e477fc..80c238d9109 100644 --- a/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java +++ b/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java @@ -1,13 +1,11 @@ package us.shandian.giga.ui.adapter; import android.annotation.SuppressLint; -import android.app.Activity; import android.app.ProgressDialog; import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.net.Uri; -import android.os.AsyncTask; import android.os.Build; import android.os.Handler; import android.os.Message; @@ -26,7 +24,6 @@ import android.widget.Toast; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.appcompat.app.AlertDialog; import androidx.core.content.FileProvider; @@ -46,12 +43,15 @@ import org.schabi.newpipe.util.NavigationHelper; import java.io.File; -import java.lang.ref.WeakReference; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.schedulers.Schedulers; import us.shandian.giga.get.DownloadMission; import us.shandian.giga.get.FinishedMission; import us.shandian.giga.get.Mission; @@ -116,11 +116,13 @@ public class MissionAdapter extends Adapter implements Handler.Callb private final Runnable rUpdater = this::updater; private final Runnable rDelete = this::deleteFinishedDownloads; + private final CompositeDisposable compositeDisposable = new CompositeDisposable(); + public MissionAdapter(Context context, @NonNull DownloadManager downloadManager, View emptyMessage, View root) { mContext = context; mDownloadManager = downloadManager; - mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mInflater = LayoutInflater.from(mContext); mLayout = R.layout.mission_item; mHandler = new Handler(context.getMainLooper()); @@ -675,7 +677,30 @@ private boolean handlePopupItem(@NonNull ViewHolderItem h, @NonNull MenuItem opt return true; case R.id.md5: case R.id.sha1: - new ChecksumTask(mContext).execute(h.item.mission.storage, ALGORITHMS.get(id)); + ProgressDialog progressDialog = null; + if (mContext != null) { + // Create dialog + progressDialog = new ProgressDialog(mContext); + progressDialog.setCancelable(false); + progressDialog.setMessage(mContext.getString(R.string.msg_wait)); + progressDialog.show(); + } + final ProgressDialog finalProgressDialog = progressDialog; + final StoredFileHelper storage = h.item.mission.storage; + compositeDisposable.add( + Observable.fromCallable(() -> Utility.checksum(storage, ALGORITHMS.get(id))) + .subscribeOn(Schedulers.computation()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(result -> { + if (finalProgressDialog != null) { + Utility.copyToClipboard(finalProgressDialog.getContext(), + result); + if (mContext != null) { + finalProgressDialog.dismiss(); + } + } + }) + ); return true; case R.id.source: /*Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(h.item.mission.source)); @@ -758,8 +783,8 @@ public void refreshMissionItems() { } } - public void onDestroy() { + compositeDisposable.dispose(); mDeleter.dispose(); } @@ -960,60 +985,7 @@ class ViewHolderHeader extends RecyclerView.ViewHolder { } } - - static class ChecksumTask extends AsyncTask { - ProgressDialog progressDialog; - WeakReference weakReference; - - ChecksumTask(@NonNull Context context) { - weakReference = new WeakReference<>((Activity) context); - } - - @Override - protected void onPreExecute() { - super.onPreExecute(); - - Activity activity = getActivity(); - if (activity != null) { - // Create dialog - progressDialog = new ProgressDialog(activity); - progressDialog.setCancelable(false); - progressDialog.setMessage(activity.getString(R.string.msg_wait)); - progressDialog.show(); - } - } - - @Override - protected String doInBackground(Object... params) { - return Utility.checksum((StoredFileHelper) params[0], (String) params[1]); - } - - @Override - protected void onPostExecute(String result) { - super.onPostExecute(result); - - if (progressDialog != null) { - Utility.copyToClipboard(progressDialog.getContext(), result); - if (getActivity() != null) { - progressDialog.dismiss(); - } - } - } - - @Nullable - private Activity getActivity() { - Activity activity = weakReference.get(); - - if (activity != null && activity.isFinishing()) { - return null; - } else { - return activity; - } - } - } - public interface RecoverHelper { void tryRecover(DownloadMission mission); } - } diff --git a/app/src/main/java/us/shandian/giga/ui/common/ProgressDrawable.java b/app/src/main/java/us/shandian/giga/ui/common/ProgressDrawable.java index bec947540d2..2a8077d51fa 100644 --- a/app/src/main/java/us/shandian/giga/ui/common/ProgressDrawable.java +++ b/app/src/main/java/us/shandian/giga/ui/common/ProgressDrawable.java @@ -83,8 +83,8 @@ public void draw(@NonNull Canvas canvas) { // render marquee width += size * 2; Path marquee = new Path(); - for (float i = -size; i < width; i += size) { - marquee.addPath(mMarqueeLine, i + mMarqueeProgress, 0); + for (int i = -size; i < width; i += size) { + marquee.addPath(mMarqueeLine, ((float)i + mMarqueeProgress), 0); } marquee.close(); diff --git a/app/src/main/java/us/shandian/giga/ui/fragment/MissionsFragment.java b/app/src/main/java/us/shandian/giga/ui/fragment/MissionsFragment.java index 35f40aa825b..265491b8a74 100644 --- a/app/src/main/java/us/shandian/giga/ui/fragment/MissionsFragment.java +++ b/app/src/main/java/us/shandian/giga/ui/fragment/MissionsFragment.java @@ -224,9 +224,10 @@ private void updateList() { mList.setAdapter(mAdapter); if (mSwitch != null) { - mSwitch.setIcon(mLinear - ? ThemeHelper.resolveResourceIdFromAttr(requireContext(), R.attr.ic_grid) - : ThemeHelper.resolveResourceIdFromAttr(requireContext(), R.attr.ic_list)); + mSwitch.setIcon(ThemeHelper.resolveResourceIdFromAttr( + requireContext(), mLinear + ? R.attr.ic_grid + : R.attr.ic_list)); mSwitch.setTitle(mLinear ? R.string.grid : R.string.list); mPrefs.edit().putBoolean("linear", mLinear).apply(); } diff --git a/app/src/main/java/us/shandian/giga/util/Utility.java b/app/src/main/java/us/shandian/giga/util/Utility.java index 551e80a3e65..79924435ff2 100644 --- a/app/src/main/java/us/shandian/giga/util/Utility.java +++ b/app/src/main/java/us/shandian/giga/util/Utility.java @@ -201,7 +201,7 @@ public static int getIconForFileType(FileType type) { } 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) { Toast.makeText(context, R.string.permission_denied, Toast.LENGTH_LONG).show(); diff --git a/app/src/main/res/anim/switch_service_in.xml b/app/src/main/res/anim/switch_service_in.xml index a49d1dabab6..b15e579995a 100644 --- a/app/src/main/res/anim/switch_service_in.xml +++ b/app/src/main/res/anim/switch_service_in.xml @@ -1,9 +1,9 @@ + android:interpolator="@android:interpolator/decelerate_quint"> + android:toAlpha="1.0" /> diff --git a/app/src/main/res/anim/switch_service_out.xml b/app/src/main/res/anim/switch_service_out.xml index 635d1630ee7..824efc9a8fd 100644 --- a/app/src/main/res/anim/switch_service_out.xml +++ b/app/src/main/res/anim/switch_service_out.xml @@ -1,9 +1,9 @@ + android:interpolator="@android:interpolator/accelerate_quint"> + android:toAlpha="0.00" /> diff --git a/app/src/main/res/animator/custom_fade_in.xml b/app/src/main/res/animator/custom_fade_in.xml index fa7f516c24f..f8df118cc18 100644 --- a/app/src/main/res/animator/custom_fade_in.xml +++ b/app/src/main/res/animator/custom_fade_in.xml @@ -5,5 +5,5 @@ android:interpolator="@android:interpolator/accelerate_decelerate" android:propertyName="alpha" android:valueFrom="0.0f" - android:valueTo="1.0f"/> - \ No newline at end of file + android:valueTo="1.0f" /> + diff --git a/app/src/main/res/animator/custom_fade_out.xml b/app/src/main/res/animator/custom_fade_out.xml index db3662647e1..3f71e5c5820 100644 --- a/app/src/main/res/animator/custom_fade_out.xml +++ b/app/src/main/res/animator/custom_fade_out.xml @@ -5,5 +5,5 @@ android:interpolator="@android:interpolator/accelerate_decelerate" android:propertyName="alpha" android:valueFrom="1.0f" - android:valueTo="0.0f"/> - \ No newline at end of file + android:valueTo="0.0f" /> + diff --git a/app/src/main/res/drawable-hdpi/ic_close_white_24dp_png.png b/app/src/main/res/drawable-hdpi/ic_close_white_24dp_png.png index 2f73a04b16d..9af50602df3 100644 Binary files a/app/src/main/res/drawable-hdpi/ic_close_white_24dp_png.png and b/app/src/main/res/drawable-hdpi/ic_close_white_24dp_png.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_hourglass_top_white_24dp_png.png b/app/src/main/res/drawable-hdpi/ic_hourglass_top_white_24dp_png.png index 13050da089b..dc2f5122a91 100644 Binary files a/app/src/main/res/drawable-hdpi/ic_hourglass_top_white_24dp_png.png and b/app/src/main/res/drawable-hdpi/ic_hourglass_top_white_24dp_png.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_newpipe_triangle_white.png b/app/src/main/res/drawable-hdpi/ic_newpipe_triangle_white.png index cb26a5f65ea..6c05313dd12 100644 Binary files a/app/src/main/res/drawable-hdpi/ic_newpipe_triangle_white.png and b/app/src/main/res/drawable-hdpi/ic_newpipe_triangle_white.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_newpipe_update.png b/app/src/main/res/drawable-hdpi/ic_newpipe_update.png index cbf336a1f17..f8e0fc5970a 100755 Binary files a/app/src/main/res/drawable-hdpi/ic_newpipe_update.png and b/app/src/main/res/drawable-hdpi/ic_newpipe_update.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_replay_white_24dp_png.png b/app/src/main/res/drawable-hdpi/ic_replay_white_24dp_png.png index 8c5afb38015..01b248180c0 100644 Binary files a/app/src/main/res/drawable-hdpi/ic_replay_white_24dp_png.png and b/app/src/main/res/drawable-hdpi/ic_replay_white_24dp_png.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_close_white_24dp_png.png b/app/src/main/res/drawable-mdpi/ic_close_white_24dp_png.png index d8aa2f7c472..199af130323 100644 Binary files a/app/src/main/res/drawable-mdpi/ic_close_white_24dp_png.png and b/app/src/main/res/drawable-mdpi/ic_close_white_24dp_png.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_hourglass_top_white_24dp_png.png b/app/src/main/res/drawable-mdpi/ic_hourglass_top_white_24dp_png.png index 2343e8cb9e2..8df1a61ec82 100644 Binary files a/app/src/main/res/drawable-mdpi/ic_hourglass_top_white_24dp_png.png and b/app/src/main/res/drawable-mdpi/ic_hourglass_top_white_24dp_png.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_newpipe_triangle_white.png b/app/src/main/res/drawable-mdpi/ic_newpipe_triangle_white.png index fc86823ac08..97c60c91c5f 100644 Binary files a/app/src/main/res/drawable-mdpi/ic_newpipe_triangle_white.png and b/app/src/main/res/drawable-mdpi/ic_newpipe_triangle_white.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_newpipe_update.png b/app/src/main/res/drawable-mdpi/ic_newpipe_update.png index 8ab23eb6a23..23b1dbfa377 100755 Binary files a/app/src/main/res/drawable-mdpi/ic_newpipe_update.png and b/app/src/main/res/drawable-mdpi/ic_newpipe_update.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_replay_white_24dp_png.png b/app/src/main/res/drawable-mdpi/ic_replay_white_24dp_png.png index 038804d1133..f351cf709a8 100644 Binary files a/app/src/main/res/drawable-mdpi/ic_replay_white_24dp_png.png and b/app/src/main/res/drawable-mdpi/ic_replay_white_24dp_png.png differ diff --git a/app/src/main/res/drawable-nodpi/background_header.png b/app/src/main/res/drawable-nodpi/background_header.png index b417038f6b9..04032b55d3f 100644 Binary files a/app/src/main/res/drawable-nodpi/background_header.png and b/app/src/main/res/drawable-nodpi/background_header.png differ diff --git a/app/src/main/res/drawable-nodpi/buddy.png b/app/src/main/res/drawable-nodpi/buddy.png index 1b414ffa7e5..878c5dff3ee 100644 Binary files a/app/src/main/res/drawable-nodpi/buddy.png and b/app/src/main/res/drawable-nodpi/buddy.png differ diff --git a/app/src/main/res/drawable-nodpi/buddy_channel_item.png b/app/src/main/res/drawable-nodpi/buddy_channel_item.png index d43c2cbd996..3c5f8f994b7 100644 Binary files a/app/src/main/res/drawable-nodpi/buddy_channel_item.png and b/app/src/main/res/drawable-nodpi/buddy_channel_item.png differ diff --git a/app/src/main/res/drawable-nodpi/channel_banner.png b/app/src/main/res/drawable-nodpi/channel_banner.png index 94b25e98b6d..7532bd3a2f8 100644 Binary files a/app/src/main/res/drawable-nodpi/channel_banner.png and b/app/src/main/res/drawable-nodpi/channel_banner.png differ diff --git a/app/src/main/res/drawable-nodpi/dummy_thumbnail.png b/app/src/main/res/drawable-nodpi/dummy_thumbnail.png index 24230b261e2..49d6e511060 100644 Binary files a/app/src/main/res/drawable-nodpi/dummy_thumbnail.png and b/app/src/main/res/drawable-nodpi/dummy_thumbnail.png differ diff --git a/app/src/main/res/drawable-nodpi/dummy_thumbnail_dark.png b/app/src/main/res/drawable-nodpi/dummy_thumbnail_dark.png index 7123a6f01be..d6ab854c33b 100644 Binary files a/app/src/main/res/drawable-nodpi/dummy_thumbnail_dark.png and b/app/src/main/res/drawable-nodpi/dummy_thumbnail_dark.png differ diff --git a/app/src/main/res/drawable-nodpi/dummy_thumbnail_playlist.png b/app/src/main/res/drawable-nodpi/dummy_thumbnail_playlist.png index c70e4bf14d0..3873b83cce7 100644 Binary files a/app/src/main/res/drawable-nodpi/dummy_thumbnail_playlist.png and b/app/src/main/res/drawable-nodpi/dummy_thumbnail_playlist.png differ diff --git a/app/src/main/res/drawable-nodpi/newpipe_logo_nude_shadow.png b/app/src/main/res/drawable-nodpi/newpipe_logo_nude_shadow.png index 99b91d3745e..55c5c105da7 100644 Binary files a/app/src/main/res/drawable-nodpi/newpipe_logo_nude_shadow.png and b/app/src/main/res/drawable-nodpi/newpipe_logo_nude_shadow.png differ diff --git a/app/src/main/res/drawable-nodpi/not_available_monkey.png b/app/src/main/res/drawable-nodpi/not_available_monkey.png index ef0068bed43..babd536024a 100644 Binary files a/app/src/main/res/drawable-nodpi/not_available_monkey.png and b/app/src/main/res/drawable-nodpi/not_available_monkey.png differ diff --git a/app/src/main/res/drawable-nodpi/place_holder_circle.png b/app/src/main/res/drawable-nodpi/place_holder_circle.png index 704729e8fc8..630d0454e53 100644 Binary files a/app/src/main/res/drawable-nodpi/place_holder_circle.png and b/app/src/main/res/drawable-nodpi/place_holder_circle.png differ diff --git a/app/src/main/res/drawable-nodpi/place_holder_cloud.png b/app/src/main/res/drawable-nodpi/place_holder_cloud.png index f78e846e155..0f9bd26c282 100644 Binary files a/app/src/main/res/drawable-nodpi/place_holder_cloud.png and b/app/src/main/res/drawable-nodpi/place_holder_cloud.png differ diff --git a/app/src/main/res/drawable-nodpi/place_holder_gadse.png b/app/src/main/res/drawable-nodpi/place_holder_gadse.png index f11dd57e536..7e3d22e8105 100644 Binary files a/app/src/main/res/drawable-nodpi/place_holder_gadse.png and b/app/src/main/res/drawable-nodpi/place_holder_gadse.png differ diff --git a/app/src/main/res/drawable-nodpi/place_holder_peertube.png b/app/src/main/res/drawable-nodpi/place_holder_peertube.png index 68850054d27..331bf94f68d 100644 Binary files a/app/src/main/res/drawable-nodpi/place_holder_peertube.png and b/app/src/main/res/drawable-nodpi/place_holder_peertube.png differ diff --git a/app/src/main/res/drawable-nodpi/place_holder_youtube.png b/app/src/main/res/drawable-nodpi/place_holder_youtube.png index c4113e005cb..d147c6643a2 100644 Binary files a/app/src/main/res/drawable-nodpi/place_holder_youtube.png and b/app/src/main/res/drawable-nodpi/place_holder_youtube.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_close_white_24dp_png.png b/app/src/main/res/drawable-xhdpi/ic_close_white_24dp_png.png index 40782d05716..fc69b5bb517 100644 Binary files a/app/src/main/res/drawable-xhdpi/ic_close_white_24dp_png.png and b/app/src/main/res/drawable-xhdpi/ic_close_white_24dp_png.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_hourglass_top_white_24dp_png.png b/app/src/main/res/drawable-xhdpi/ic_hourglass_top_white_24dp_png.png index bdf88fb3b62..29a36f5431d 100644 Binary files a/app/src/main/res/drawable-xhdpi/ic_hourglass_top_white_24dp_png.png and b/app/src/main/res/drawable-xhdpi/ic_hourglass_top_white_24dp_png.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_newpipe_triangle_white.png b/app/src/main/res/drawable-xhdpi/ic_newpipe_triangle_white.png index b90c550500a..d4e94d0d1b3 100644 Binary files a/app/src/main/res/drawable-xhdpi/ic_newpipe_triangle_white.png and b/app/src/main/res/drawable-xhdpi/ic_newpipe_triangle_white.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_newpipe_update.png b/app/src/main/res/drawable-xhdpi/ic_newpipe_update.png index 5ee02aaa935..b9a296064c5 100755 Binary files a/app/src/main/res/drawable-xhdpi/ic_newpipe_update.png and b/app/src/main/res/drawable-xhdpi/ic_newpipe_update.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_replay_white_24dp_png.png b/app/src/main/res/drawable-xhdpi/ic_replay_white_24dp_png.png index 4456670babc..153e3dbf31a 100644 Binary files a/app/src/main/res/drawable-xhdpi/ic_replay_white_24dp_png.png and b/app/src/main/res/drawable-xhdpi/ic_replay_white_24dp_png.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_close_white_24dp_png.png b/app/src/main/res/drawable-xxhdpi/ic_close_white_24dp_png.png index 2cd1a88659f..9ec308cef34 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_close_white_24dp_png.png and b/app/src/main/res/drawable-xxhdpi/ic_close_white_24dp_png.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_hourglass_top_white_24dp_png.png b/app/src/main/res/drawable-xxhdpi/ic_hourglass_top_white_24dp_png.png index f9a097f4de3..9d214c497cd 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_hourglass_top_white_24dp_png.png and b/app/src/main/res/drawable-xxhdpi/ic_hourglass_top_white_24dp_png.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_newpipe_triangle_white.png b/app/src/main/res/drawable-xxhdpi/ic_newpipe_triangle_white.png index acde4439ee5..fa554585f59 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_newpipe_triangle_white.png and b/app/src/main/res/drawable-xxhdpi/ic_newpipe_triangle_white.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_newpipe_update.png b/app/src/main/res/drawable-xxhdpi/ic_newpipe_update.png index 22f0e99d1b7..5d348e6e326 100755 Binary files a/app/src/main/res/drawable-xxhdpi/ic_newpipe_update.png and b/app/src/main/res/drawable-xxhdpi/ic_newpipe_update.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_replay_white_24dp_png.png b/app/src/main/res/drawable-xxhdpi/ic_replay_white_24dp_png.png index e456ce595db..dc60f4ecd20 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_replay_white_24dp_png.png and b/app/src/main/res/drawable-xxhdpi/ic_replay_white_24dp_png.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_close_white_24dp_png.png b/app/src/main/res/drawable-xxxhdpi/ic_close_white_24dp_png.png index 4d278c5bfba..535d1df0c9f 100644 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_close_white_24dp_png.png and b/app/src/main/res/drawable-xxxhdpi/ic_close_white_24dp_png.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_hourglass_top_white_24dp_png.png b/app/src/main/res/drawable-xxxhdpi/ic_hourglass_top_white_24dp_png.png index 7a099dabfe7..a0d5ba81e29 100644 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_hourglass_top_white_24dp_png.png and b/app/src/main/res/drawable-xxxhdpi/ic_hourglass_top_white_24dp_png.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_newpipe_triangle_white.png b/app/src/main/res/drawable-xxxhdpi/ic_newpipe_triangle_white.png index 93cfda12d84..26e134fac58 100644 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_newpipe_triangle_white.png and b/app/src/main/res/drawable-xxxhdpi/ic_newpipe_triangle_white.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_newpipe_update.png b/app/src/main/res/drawable-xxxhdpi/ic_newpipe_update.png index 1f44c1aaf28..bc06d395323 100755 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_newpipe_update.png and b/app/src/main/res/drawable-xxxhdpi/ic_newpipe_update.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_replay_white_24dp_png.png b/app/src/main/res/drawable-xxxhdpi/ic_replay_white_24dp_png.png index c2e9eb27bb5..372bc8bd150 100644 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_replay_white_24dp_png.png and b/app/src/main/res/drawable-xxxhdpi/ic_replay_white_24dp_png.png differ diff --git a/app/src/main/res/drawable/background_oval_black_transparent.xml b/app/src/main/res/drawable/background_oval_black_transparent.xml index 5db5969c654..e665f529960 100644 --- a/app/src/main/res/drawable/background_oval_black_transparent.xml +++ b/app/src/main/res/drawable/background_oval_black_transparent.xml @@ -2,4 +2,4 @@ - \ No newline at end of file + diff --git a/app/src/main/res/drawable/custom_progress_bar.xml b/app/src/main/res/drawable/custom_progress_bar.xml index 251cde1718d..0ead1c4be97 100644 --- a/app/src/main/res/drawable/custom_progress_bar.xml +++ b/app/src/main/res/drawable/custom_progress_bar.xml @@ -2,16 +2,16 @@ - + - + - \ No newline at end of file + diff --git a/app/src/main/res/drawable/dashed_border_black.xml b/app/src/main/res/drawable/dashed_border_black.xml index b6bac6252c5..137184b86a2 100644 --- a/app/src/main/res/drawable/dashed_border_black.xml +++ b/app/src/main/res/drawable/dashed_border_black.xml @@ -3,6 +3,6 @@ - \ No newline at end of file + android:dashWidth="4dp" + android:dashGap="4dp" /> + diff --git a/app/src/main/res/drawable/dashed_border_dark.xml b/app/src/main/res/drawable/dashed_border_dark.xml index 5af152ecc17..ff714a4489e 100644 --- a/app/src/main/res/drawable/dashed_border_dark.xml +++ b/app/src/main/res/drawable/dashed_border_dark.xml @@ -3,6 +3,6 @@ - \ No newline at end of file + android:dashWidth="4dp" + android:dashGap="4dp" /> + diff --git a/app/src/main/res/drawable/dashed_border_light.xml b/app/src/main/res/drawable/dashed_border_light.xml index 5d29112bdc7..cc71acb7244 100644 --- a/app/src/main/res/drawable/dashed_border_light.xml +++ b/app/src/main/res/drawable/dashed_border_light.xml @@ -3,6 +3,6 @@ - \ No newline at end of file + android:dashWidth="4dp" + android:dashGap="4dp" /> + diff --git a/app/src/main/res/drawable/dot_default.xml b/app/src/main/res/drawable/dot_default.xml index 3380dca3b40..fac1a2e8145 100644 --- a/app/src/main/res/drawable/dot_default.xml +++ b/app/src/main/res/drawable/dot_default.xml @@ -6,7 +6,7 @@ android:shape="ring" android:thickness="4dp" android:useLevel="false"> - + - \ No newline at end of file + diff --git a/app/src/main/res/drawable/dot_selected.xml b/app/src/main/res/drawable/dot_selected.xml index 017e99d43d2..77992e4a63e 100644 --- a/app/src/main/res/drawable/dot_selected.xml +++ b/app/src/main/res/drawable/dot_selected.xml @@ -6,7 +6,7 @@ android:shape="ring" android:thickness="6dp" android:useLevel="false"> - + - \ No newline at end of file + diff --git a/app/src/main/res/drawable/drawer_header_bottom_background.xml b/app/src/main/res/drawable/drawer_header_bottom_background.xml index 9135222749a..9f9792340e8 100644 --- a/app/src/main/res/drawable/drawer_header_bottom_background.xml +++ b/app/src/main/res/drawable/drawer_header_bottom_background.xml @@ -3,5 +3,5 @@ - \ No newline at end of file + android:startColor="#4D000000" /> + diff --git a/app/src/main/res/drawable/ic_add_black_24dp.xml b/app/src/main/res/drawable/ic_add_black_24dp.xml index 0258249cc48..fedd077d841 100644 --- a/app/src/main/res/drawable/ic_add_black_24dp.xml +++ b/app/src/main/res/drawable/ic_add_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" /> diff --git a/app/src/main/res/drawable/ic_add_circle_outline_black_24dp.xml b/app/src/main/res/drawable/ic_add_circle_outline_black_24dp.xml index 900f2275e48..1596099f3b1 100644 --- a/app/src/main/res/drawable/ic_add_circle_outline_black_24dp.xml +++ b/app/src/main/res/drawable/ic_add_circle_outline_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M13,7h-2v4L7,11v2h4v4h2v-4h4v-2h-4L13,7zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z" /> diff --git a/app/src/main/res/drawable/ic_add_circle_outline_white_24dp.xml b/app/src/main/res/drawable/ic_add_circle_outline_white_24dp.xml index 66d3247ae7c..2f2cfe3e3d9 100644 --- a/app/src/main/res/drawable/ic_add_circle_outline_white_24dp.xml +++ b/app/src/main/res/drawable/ic_add_circle_outline_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_add_white_24dp.xml b/app/src/main/res/drawable/ic_add_white_24dp.xml index e3979cd7f25..bbda803b053 100644 --- a/app/src/main/res/drawable/ic_add_white_24dp.xml +++ b/app/src/main/res/drawable/ic_add_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_apps_black_24dp.xml b/app/src/main/res/drawable/ic_apps_black_24dp.xml index ff485cf1a32..b8c4ab12e23 100644 --- a/app/src/main/res/drawable/ic_apps_black_24dp.xml +++ b/app/src/main/res/drawable/ic_apps_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M4,8h4L8,4L4,4v4zM10,20h4v-4h-4v4zM4,20h4v-4L4,16v4zM4,14h4v-4L4,10v4zM10,14h4v-4h-4v4zM16,4v4h4L20,4h-4zM10,8h4L14,4h-4v4zM16,14h4v-4h-4v4zM16,20h4v-4h-4v4z" /> diff --git a/app/src/main/res/drawable/ic_apps_white_24dp.xml b/app/src/main/res/drawable/ic_apps_white_24dp.xml index 373f7752b8b..2d7d796f7cf 100644 --- a/app/src/main/res/drawable/ic_apps_white_24dp.xml +++ b/app/src/main/res/drawable/ic_apps_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_arrow_back_black_24dp.xml b/app/src/main/res/drawable/ic_arrow_back_black_24dp.xml index beafea3959e..2d68f797b50 100644 --- a/app/src/main/res/drawable/ic_arrow_back_black_24dp.xml +++ b/app/src/main/res/drawable/ic_arrow_back_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z" /> diff --git a/app/src/main/res/drawable/ic_arrow_back_white_24dp.xml b/app/src/main/res/drawable/ic_arrow_back_white_24dp.xml index 71d5bbd2926..b7c7287839b 100644 --- a/app/src/main/res/drawable/ic_arrow_back_white_24dp.xml +++ b/app/src/main/res/drawable/ic_arrow_back_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_arrow_drop_down_white_24dp.xml b/app/src/main/res/drawable/ic_arrow_drop_down_white_24dp.xml index 65e1e422817..270637216c1 100644 --- a/app/src/main/res/drawable/ic_arrow_drop_down_white_24dp.xml +++ b/app/src/main/res/drawable/ic_arrow_drop_down_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_arrow_drop_up_white_24dp.xml b/app/src/main/res/drawable/ic_arrow_drop_up_white_24dp.xml index 1d266cecc8f..fdc9dcf8de5 100644 --- a/app/src/main/res/drawable/ic_arrow_drop_up_white_24dp.xml +++ b/app/src/main/res/drawable/ic_arrow_drop_up_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_asterisk_black_24dp.xml b/app/src/main/res/drawable/ic_asterisk_black_24dp.xml index fa16cd5e8e7..840682feeb5 100644 --- a/app/src/main/res/drawable/ic_asterisk_black_24dp.xml +++ b/app/src/main/res/drawable/ic_asterisk_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> - \ No newline at end of file + android:pathData="M10,2H14L13.21,9.91L19.66,5.27L21.66,8.73L14.42,12L21.66,15.27L19.66,18.73L13.21,14.09L14,22H10L10.79,14.09L4.34,18.73L2.34,15.27L9.58,12L2.34,8.73L4.34,5.27L10.79,9.91L10,2Z" /> + diff --git a/app/src/main/res/drawable/ic_asterisk_white_24dp.xml b/app/src/main/res/drawable/ic_asterisk_white_24dp.xml index bd487cb5579..c66bb405121 100644 --- a/app/src/main/res/drawable/ic_asterisk_white_24dp.xml +++ b/app/src/main/res/drawable/ic_asterisk_white_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> - \ No newline at end of file + android:pathData="M10,2H14L13.21,9.91L19.66,5.27L21.66,8.73L14.42,12L21.66,15.27L19.66,18.73L13.21,14.09L14,22H10L10.79,14.09L4.34,18.73L2.34,15.27L9.58,12L2.34,8.73L4.34,5.27L10.79,9.91L10,2Z" /> + diff --git a/app/src/main/res/drawable/ic_attach_money_black_24dp.xml b/app/src/main/res/drawable/ic_attach_money_black_24dp.xml index b520fc98d09..dd93a7599c9 100644 --- a/app/src/main/res/drawable/ic_attach_money_black_24dp.xml +++ b/app/src/main/res/drawable/ic_attach_money_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M11.8,10.9c-2.27,-0.59 -3,-1.2 -3,-2.15 0,-1.09 1.01,-1.85 2.7,-1.85 1.78,0 2.44,0.85 2.5,2.1h2.21c-0.07,-1.72 -1.12,-3.3 -3.21,-3.81V3h-3v2.16c-1.94,0.42 -3.5,1.68 -3.5,3.61 0,2.31 1.91,3.46 4.7,4.13 2.5,0.6 3,1.48 3,2.41 0,0.69 -0.49,1.79 -2.7,1.79 -2.06,0 -2.87,-0.92 -2.98,-2.1h-2.2c0.12,2.19 1.76,3.42 3.68,3.83V21h3v-2.15c1.95,-0.37 3.5,-1.5 3.5,-3.55 0,-2.84 -2.43,-3.81 -4.7,-4.4z" /> diff --git a/app/src/main/res/drawable/ic_attach_money_white_24dp.xml b/app/src/main/res/drawable/ic_attach_money_white_24dp.xml index d198dd14d41..fcc1ab160c0 100644 --- a/app/src/main/res/drawable/ic_attach_money_white_24dp.xml +++ b/app/src/main/res/drawable/ic_attach_money_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_backup_black_24dp.xml b/app/src/main/res/drawable/ic_backup_black_24dp.xml index 086281669fa..200bb70814a 100644 --- a/app/src/main/res/drawable/ic_backup_black_24dp.xml +++ b/app/src/main/res/drawable/ic_backup_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM14,13v4h-4v-4H7l5,-5 5,5h-3z" /> diff --git a/app/src/main/res/drawable/ic_backup_white_24dp.xml b/app/src/main/res/drawable/ic_backup_white_24dp.xml index 55dbbae85cc..29259b0e03a 100644 --- a/app/src/main/res/drawable/ic_backup_white_24dp.xml +++ b/app/src/main/res/drawable/ic_backup_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_bookmark_black_24dp.xml b/app/src/main/res/drawable/ic_bookmark_black_24dp.xml index 6a6a1b39d7c..5bf2e951cbd 100644 --- a/app/src/main/res/drawable/ic_bookmark_black_24dp.xml +++ b/app/src/main/res/drawable/ic_bookmark_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M17,3H7c-1.1,0 -1.99,0.9 -1.99,2L5,21l7,-3 7,3V5c0,-1.1 -0.9,-2 -2,-2z" /> diff --git a/app/src/main/res/drawable/ic_bookmark_white_24dp.xml b/app/src/main/res/drawable/ic_bookmark_white_24dp.xml index feb16ed6325..2e919f18d06 100644 --- a/app/src/main/res/drawable/ic_bookmark_white_24dp.xml +++ b/app/src/main/res/drawable/ic_bookmark_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_brightness_high_white_24dp.xml b/app/src/main/res/drawable/ic_brightness_high_white_24dp.xml index 9ed0b086ca7..1ff2d2e26bf 100644 --- a/app/src/main/res/drawable/ic_brightness_high_white_24dp.xml +++ b/app/src/main/res/drawable/ic_brightness_high_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_brightness_low_white_24dp.xml b/app/src/main/res/drawable/ic_brightness_low_white_24dp.xml index da4e0ca3074..1a00ce2dd44 100644 --- a/app/src/main/res/drawable/ic_brightness_low_white_24dp.xml +++ b/app/src/main/res/drawable/ic_brightness_low_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_brightness_medium_white_24dp.xml b/app/src/main/res/drawable/ic_brightness_medium_white_24dp.xml index c522453f124..853e219bd3c 100644 --- a/app/src/main/res/drawable/ic_brightness_medium_white_24dp.xml +++ b/app/src/main/res/drawable/ic_brightness_medium_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_bug_report_black_24dp.xml b/app/src/main/res/drawable/ic_bug_report_black_24dp.xml index 4d83902b899..206702ff247 100644 --- a/app/src/main/res/drawable/ic_bug_report_black_24dp.xml +++ b/app/src/main/res/drawable/ic_bug_report_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M20,8h-2.81c-0.45,-0.78 -1.07,-1.45 -1.82,-1.96L17,4.41 15.59,3l-2.17,2.17C12.96,5.06 12.49,5 12,5c-0.49,0 -0.96,0.06 -1.41,0.17L8.41,3 7,4.41l1.62,1.63C7.88,6.55 7.26,7.22 6.81,8L4,8v2h2.09c-0.05,0.33 -0.09,0.66 -0.09,1v1L4,12v2h2v1c0,0.34 0.04,0.67 0.09,1L4,16v2h2.81c1.04,1.79 2.97,3 5.19,3s4.15,-1.21 5.19,-3L20,18v-2h-2.09c0.05,-0.33 0.09,-0.66 0.09,-1v-1h2v-2h-2v-1c0,-0.34 -0.04,-0.67 -0.09,-1L20,10L20,8zM14,16h-4v-2h4v2zM14,12h-4v-2h4v2z" /> diff --git a/app/src/main/res/drawable/ic_bug_report_white_24dp.xml b/app/src/main/res/drawable/ic_bug_report_white_24dp.xml index 5c8f5bc16b7..e1a204a29c6 100644 --- a/app/src/main/res/drawable/ic_bug_report_white_24dp.xml +++ b/app/src/main/res/drawable/ic_bug_report_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_cast_black_24dp.xml b/app/src/main/res/drawable/ic_cast_black_24dp.xml index 7b143de9f88..4ffbdc42122 100644 --- a/app/src/main/res/drawable/ic_cast_black_24dp.xml +++ b/app/src/main/res/drawable/ic_cast_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M21,3L3,3c-1.1,0 -2,0.9 -2,2v3h2L3,5h18v14h-7v2h7c1.1,0 2,-0.9 2,-2L23,5c0,-1.1 -0.9,-2 -2,-2zM1,18v3h3c0,-1.66 -1.34,-3 -3,-3zM1,14v2c2.76,0 5,2.24 5,5h2c0,-3.87 -3.13,-7 -7,-7zM1,10v2c4.97,0 9,4.03 9,9h2c0,-6.08 -4.93,-11 -11,-11z" /> diff --git a/app/src/main/res/drawable/ic_cast_white_24dp.xml b/app/src/main/res/drawable/ic_cast_white_24dp.xml index 434c6441679..61a1f61fe6f 100644 --- a/app/src/main/res/drawable/ic_cast_white_24dp.xml +++ b/app/src/main/res/drawable/ic_cast_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_child_care_black_24dp.xml b/app/src/main/res/drawable/ic_child_care_black_24dp.xml index 5af39255e97..25a51bb23ff 100644 --- a/app/src/main/res/drawable/ic_child_care_black_24dp.xml +++ b/app/src/main/res/drawable/ic_child_care_black_24dp.xml @@ -1,15 +1,15 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M14.5,10.5m-1.25,0a1.25,1.25 0,1 1,2.5 0a1.25,1.25 0,1 1,-2.5 0" /> + android:pathData="M9.5,10.5m-1.25,0a1.25,1.25 0,1 1,2.5 0a1.25,1.25 0,1 1,-2.5 0" /> + android:pathData="M22.94,12.66c0.04,-0.21 0.06,-0.43 0.06,-0.66s-0.02,-0.45 -0.06,-0.66c-0.25,-1.51 -1.36,-2.74 -2.81,-3.17 -0.53,-1.12 -1.28,-2.1 -2.19,-2.91C16.36,3.85 14.28,3 12,3s-4.36,0.85 -5.94,2.26c-0.92,0.81 -1.67,1.8 -2.19,2.91 -1.45,0.43 -2.56,1.65 -2.81,3.17 -0.04,0.21 -0.06,0.43 -0.06,0.66s0.02,0.45 0.06,0.66c0.25,1.51 1.36,2.74 2.81,3.17 0.52,1.11 1.27,2.09 2.17,2.89C7.62,20.14 9.71,21 12,21s4.38,-0.86 5.97,-2.28c0.9,-0.8 1.65,-1.79 2.17,-2.89 1.44,-0.43 2.55,-1.65 2.8,-3.17zM19,14c-0.1,0 -0.19,-0.02 -0.29,-0.03 -0.2,0.67 -0.49,1.29 -0.86,1.86C16.6,17.74 14.45,19 12,19s-4.6,-1.26 -5.85,-3.17c-0.37,-0.57 -0.66,-1.19 -0.86,-1.86 -0.1,0.01 -0.19,0.03 -0.29,0.03 -1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2c0.1,0 0.19,0.02 0.29,0.03 0.2,-0.67 0.49,-1.29 0.86,-1.86C7.4,6.26 9.55,5 12,5s4.6,1.26 5.85,3.17c0.37,0.57 0.66,1.19 0.86,1.86 0.1,-0.01 0.19,-0.03 0.29,-0.03 1.1,0 2,0.9 2,2s-0.9,2 -2,2zM7.5,14c0.76,1.77 2.49,3 4.5,3s3.74,-1.23 4.5,-3h-9z" /> diff --git a/app/src/main/res/drawable/ic_child_care_white_24dp.xml b/app/src/main/res/drawable/ic_child_care_white_24dp.xml index 81fa2ddc12b..9375e3116ea 100644 --- a/app/src/main/res/drawable/ic_child_care_white_24dp.xml +++ b/app/src/main/res/drawable/ic_child_care_white_24dp.xml @@ -1,7 +1,16 @@ - - - - + + + + diff --git a/app/src/main/res/drawable/ic_close_black_24dp.xml b/app/src/main/res/drawable/ic_close_black_24dp.xml index ede4b7108d5..f50fd991b75 100644 --- a/app/src/main/res/drawable/ic_close_black_24dp.xml +++ b/app/src/main/res/drawable/ic_close_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z" /> diff --git a/app/src/main/res/drawable/ic_close_white_24dp.xml b/app/src/main/res/drawable/ic_close_white_24dp.xml index d11cc5c9c40..c63eeb59765 100644 --- a/app/src/main/res/drawable/ic_close_white_24dp.xml +++ b/app/src/main/res/drawable/ic_close_white_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z" /> diff --git a/app/src/main/res/drawable/ic_cloud_download_black_24dp.xml b/app/src/main/res/drawable/ic_cloud_download_black_24dp.xml index 261c312175b..aa051b25da5 100644 --- a/app/src/main/res/drawable/ic_cloud_download_black_24dp.xml +++ b/app/src/main/res/drawable/ic_cloud_download_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM17,13l-5,5 -5,-5h3V9h4v4h3z" /> diff --git a/app/src/main/res/drawable/ic_cloud_download_white_24dp.xml b/app/src/main/res/drawable/ic_cloud_download_white_24dp.xml index 0feb270af4d..67e87045662 100644 --- a/app/src/main/res/drawable/ic_cloud_download_white_24dp.xml +++ b/app/src/main/res/drawable/ic_cloud_download_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_computer_black_24dp.xml b/app/src/main/res/drawable/ic_computer_black_24dp.xml index 4599f98cd35..04eb86a51e8 100644 --- a/app/src/main/res/drawable/ic_computer_black_24dp.xml +++ b/app/src/main/res/drawable/ic_computer_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M20,18c1.1,0 1.99,-0.9 1.99,-2L22,6c0,-1.1 -0.9,-2 -2,-2H4c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2H0v2h24v-2h-4zM4,6h16v10H4V6z" /> diff --git a/app/src/main/res/drawable/ic_computer_white_24dp.xml b/app/src/main/res/drawable/ic_computer_white_24dp.xml index 9569b7747ad..68f85594d75 100644 --- a/app/src/main/res/drawable/ic_computer_white_24dp.xml +++ b/app/src/main/res/drawable/ic_computer_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_crop_portrait_black_24dp.xml b/app/src/main/res/drawable/ic_crop_portrait_black_24dp.xml index e8c60a1a298..d906df150cf 100644 --- a/app/src/main/res/drawable/ic_crop_portrait_black_24dp.xml +++ b/app/src/main/res/drawable/ic_crop_portrait_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M17,3L7,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2L19,5c0,-1.1 -0.9,-2 -2,-2zM17,19L7,19L7,5h10v14z" /> diff --git a/app/src/main/res/drawable/ic_crop_portrait_white_24dp.xml b/app/src/main/res/drawable/ic_crop_portrait_white_24dp.xml index caba925a146..fc11eba5725 100644 --- a/app/src/main/res/drawable/ic_crop_portrait_white_24dp.xml +++ b/app/src/main/res/drawable/ic_crop_portrait_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_delete_black_24dp.xml b/app/src/main/res/drawable/ic_delete_black_24dp.xml index 39e64d6980a..962e033743f 100644 --- a/app/src/main/res/drawable/ic_delete_black_24dp.xml +++ b/app/src/main/res/drawable/ic_delete_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z" /> diff --git a/app/src/main/res/drawable/ic_delete_white_24dp.xml b/app/src/main/res/drawable/ic_delete_white_24dp.xml index 8bed121aa60..3760de238c8 100644 --- a/app/src/main/res/drawable/ic_delete_white_24dp.xml +++ b/app/src/main/res/drawable/ic_delete_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_directions_bike_black_24dp.xml b/app/src/main/res/drawable/ic_directions_bike_black_24dp.xml index ded5e335981..328fbe39331 100644 --- a/app/src/main/res/drawable/ic_directions_bike_black_24dp.xml +++ b/app/src/main/res/drawable/ic_directions_bike_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M15.5,5.5c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM5,12c-2.8,0 -5,2.2 -5,5s2.2,5 5,5 5,-2.2 5,-5 -2.2,-5 -5,-5zM5,20.5c-1.9,0 -3.5,-1.6 -3.5,-3.5s1.6,-3.5 3.5,-3.5 3.5,1.6 3.5,3.5 -1.6,3.5 -3.5,3.5zM10.8,10.5l2.4,-2.4 0.8,0.8c1.3,1.3 3,2.1 5.1,2.1L19.1,9c-1.5,0 -2.7,-0.6 -3.6,-1.5l-1.9,-1.9c-0.5,-0.4 -1,-0.6 -1.6,-0.6s-1.1,0.2 -1.4,0.6L7.8,8.4c-0.4,0.4 -0.6,0.9 -0.6,1.4 0,0.6 0.2,1.1 0.6,1.4L11,14v5h2v-6.2l-2.2,-2.3zM19,12c-2.8,0 -5,2.2 -5,5s2.2,5 5,5 5,-2.2 5,-5 -2.2,-5 -5,-5zM19,20.5c-1.9,0 -3.5,-1.6 -3.5,-3.5s1.6,-3.5 3.5,-3.5 3.5,1.6 3.5,3.5 -1.6,3.5 -3.5,3.5z" /> diff --git a/app/src/main/res/drawable/ic_directions_bike_white_24dp.xml b/app/src/main/res/drawable/ic_directions_bike_white_24dp.xml index f165cea9c83..90c7f7a775e 100644 --- a/app/src/main/res/drawable/ic_directions_bike_white_24dp.xml +++ b/app/src/main/res/drawable/ic_directions_bike_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_directions_car_black_24dp.xml b/app/src/main/res/drawable/ic_directions_car_black_24dp.xml index 6d6337c3abf..b2fe8bdbd6d 100644 --- a/app/src/main/res/drawable/ic_directions_car_black_24dp.xml +++ b/app/src/main/res/drawable/ic_directions_car_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M18.92,6.01C18.72,5.42 18.16,5 17.5,5h-11c-0.66,0 -1.21,0.42 -1.42,1.01L3,12v8c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-1h12v1c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-8l-2.08,-5.99zM6.5,16c-0.83,0 -1.5,-0.67 -1.5,-1.5S5.67,13 6.5,13s1.5,0.67 1.5,1.5S7.33,16 6.5,16zM17.5,16c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zM5,11l1.5,-4.5h11L19,11L5,11z" /> diff --git a/app/src/main/res/drawable/ic_directions_car_white_24dp.xml b/app/src/main/res/drawable/ic_directions_car_white_24dp.xml index 981334c173e..26404bddbf2 100644 --- a/app/src/main/res/drawable/ic_directions_car_white_24dp.xml +++ b/app/src/main/res/drawable/ic_directions_car_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_done_black_24dp.xml b/app/src/main/res/drawable/ic_done_black_24dp.xml index 7affe9ba9f1..bda675f1429 100644 --- a/app/src/main/res/drawable/ic_done_black_24dp.xml +++ b/app/src/main/res/drawable/ic_done_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z" /> diff --git a/app/src/main/res/drawable/ic_done_white_24dp.xml b/app/src/main/res/drawable/ic_done_white_24dp.xml index cab2aed1ac6..bb657f6ec0d 100644 --- a/app/src/main/res/drawable/ic_done_white_24dp.xml +++ b/app/src/main/res/drawable/ic_done_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_drag_handle_black_24dp.xml b/app/src/main/res/drawable/ic_drag_handle_black_24dp.xml index 68a71905231..8f7f335a3b5 100644 --- a/app/src/main/res/drawable/ic_drag_handle_black_24dp.xml +++ b/app/src/main/res/drawable/ic_drag_handle_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M20,9H4v2h16V9zM4,15h16v-2H4v2z" /> diff --git a/app/src/main/res/drawable/ic_drag_handle_white_24dp.xml b/app/src/main/res/drawable/ic_drag_handle_white_24dp.xml index 50f9e6c2914..9378f4b23a6 100644 --- a/app/src/main/res/drawable/ic_drag_handle_white_24dp.xml +++ b/app/src/main/res/drawable/ic_drag_handle_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_edit_black_24dp.xml b/app/src/main/res/drawable/ic_edit_black_24dp.xml index 43489826e01..5505cc77560 100644 --- a/app/src/main/res/drawable/ic_edit_black_24dp.xml +++ b/app/src/main/res/drawable/ic_edit_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z" /> diff --git a/app/src/main/res/drawable/ic_edit_white_24dp.xml b/app/src/main/res/drawable/ic_edit_white_24dp.xml index 88f94780ffe..85d32985f9b 100644 --- a/app/src/main/res/drawable/ic_edit_white_24dp.xml +++ b/app/src/main/res/drawable/ic_edit_white_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z" /> diff --git a/app/src/main/res/drawable/ic_expand_less_black_24dp.xml b/app/src/main/res/drawable/ic_expand_less_black_24dp.xml index 3afdf968269..7b6a65628c3 100644 --- a/app/src/main/res/drawable/ic_expand_less_black_24dp.xml +++ b/app/src/main/res/drawable/ic_expand_less_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M12,8l-6,6 1.41,1.41L12,10.83l4.59,4.58L18,14z" /> diff --git a/app/src/main/res/drawable/ic_expand_less_white_24dp.xml b/app/src/main/res/drawable/ic_expand_less_white_24dp.xml index 5042d801afc..4ff5565fa6b 100644 --- a/app/src/main/res/drawable/ic_expand_less_white_24dp.xml +++ b/app/src/main/res/drawable/ic_expand_less_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_expand_more_black_24dp.xml b/app/src/main/res/drawable/ic_expand_more_black_24dp.xml index 8d57dbc10f8..c1f391b6170 100644 --- a/app/src/main/res/drawable/ic_expand_more_black_24dp.xml +++ b/app/src/main/res/drawable/ic_expand_more_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M16.59,8.59L12,13.17 7.41,8.59 6,10l6,6 6,-6z" /> diff --git a/app/src/main/res/drawable/ic_expand_more_white_24dp.xml b/app/src/main/res/drawable/ic_expand_more_white_24dp.xml index bc72bdce08d..b6a470043f3 100644 --- a/app/src/main/res/drawable/ic_expand_more_white_24dp.xml +++ b/app/src/main/res/drawable/ic_expand_more_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_explore_black_24dp.xml b/app/src/main/res/drawable/ic_explore_black_24dp.xml index c898ed9a570..e94079fedcc 100644 --- a/app/src/main/res/drawable/ic_explore_black_24dp.xml +++ b/app/src/main/res/drawable/ic_explore_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M12,10.9c-0.61,0 -1.1,0.49 -1.1,1.1s0.49,1.1 1.1,1.1c0.61,0 1.1,-0.49 1.1,-1.1s-0.49,-1.1 -1.1,-1.1zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM14.19,14.19L6,18l3.81,-8.19L18,6l-3.81,8.19z" /> diff --git a/app/src/main/res/drawable/ic_explore_white_24dp.xml b/app/src/main/res/drawable/ic_explore_white_24dp.xml index 65f2818a669..a910c54297d 100644 --- a/app/src/main/res/drawable/ic_explore_white_24dp.xml +++ b/app/src/main/res/drawable/ic_explore_white_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M12,10.9c-0.61,0 -1.1,0.49 -1.1,1.1s0.49,1.1 1.1,1.1c0.61,0 1.1,-0.49 1.1,-1.1s-0.49,-1.1 -1.1,-1.1zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM14.19,14.19L6,18l3.81,-8.19L18,6l-3.81,8.19z" /> diff --git a/app/src/main/res/drawable/ic_fast_forward_white_24dp.xml b/app/src/main/res/drawable/ic_fast_forward_white_24dp.xml index da7c3fb1ef4..ab5ae6c37b9 100644 --- a/app/src/main/res/drawable/ic_fast_forward_white_24dp.xml +++ b/app/src/main/res/drawable/ic_fast_forward_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_fast_rewind_white_24dp.xml b/app/src/main/res/drawable/ic_fast_rewind_white_24dp.xml index 4bab93ecb77..ccc0721584a 100644 --- a/app/src/main/res/drawable/ic_fast_rewind_white_24dp.xml +++ b/app/src/main/res/drawable/ic_fast_rewind_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_fastfood_black_24dp.xml b/app/src/main/res/drawable/ic_fastfood_black_24dp.xml index 4de2eb9af33..4d43eafd281 100644 --- a/app/src/main/res/drawable/ic_fastfood_black_24dp.xml +++ b/app/src/main/res/drawable/ic_fastfood_black_24dp.xml @@ -3,7 +3,7 @@ android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"> - + diff --git a/app/src/main/res/drawable/ic_fastfood_white_24dp.xml b/app/src/main/res/drawable/ic_fastfood_white_24dp.xml index 517b925731f..ddb9b6257e1 100644 --- a/app/src/main/res/drawable/ic_fastfood_white_24dp.xml +++ b/app/src/main/res/drawable/ic_fastfood_white_24dp.xml @@ -3,7 +3,7 @@ android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"> - + diff --git a/app/src/main/res/drawable/ic_favorite_black_24dp.xml b/app/src/main/res/drawable/ic_favorite_black_24dp.xml index cfba5d846f5..17cea9270c7 100644 --- a/app/src/main/res/drawable/ic_favorite_black_24dp.xml +++ b/app/src/main/res/drawable/ic_favorite_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M12,21.35l-1.45,-1.32C5.4,15.36 2,12.28 2,8.5 2,5.42 4.42,3 7.5,3c1.74,0 3.41,0.81 4.5,2.09C13.09,3.81 14.76,3 16.5,3 19.58,3 22,5.42 22,8.5c0,3.78 -3.4,6.86 -8.55,11.54L12,21.35z" /> diff --git a/app/src/main/res/drawable/ic_favorite_white_24dp.xml b/app/src/main/res/drawable/ic_favorite_white_24dp.xml index 67a25e713e8..efc717ee9ad 100644 --- a/app/src/main/res/drawable/ic_favorite_white_24dp.xml +++ b/app/src/main/res/drawable/ic_favorite_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_file_download_black_24dp.xml b/app/src/main/res/drawable/ic_file_download_black_24dp.xml index 492b41d3466..370bba93dd0 100644 --- a/app/src/main/res/drawable/ic_file_download_black_24dp.xml +++ b/app/src/main/res/drawable/ic_file_download_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z" /> diff --git a/app/src/main/res/drawable/ic_file_download_white_24dp.xml b/app/src/main/res/drawable/ic_file_download_white_24dp.xml index b8e836142db..97bdac0f1e6 100644 --- a/app/src/main/res/drawable/ic_file_download_white_24dp.xml +++ b/app/src/main/res/drawable/ic_file_download_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_filter_list_black_24dp.xml b/app/src/main/res/drawable/ic_filter_list_black_24dp.xml index b99b672f4e6..6826b3d5acf 100644 --- a/app/src/main/res/drawable/ic_filter_list_black_24dp.xml +++ b/app/src/main/res/drawable/ic_filter_list_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M10,18h4v-2h-4v2zM3,6v2h18L21,6L3,6zM6,13h12v-2L6,11v2z" /> diff --git a/app/src/main/res/drawable/ic_filter_list_white_24dp.xml b/app/src/main/res/drawable/ic_filter_list_white_24dp.xml index 5d4ec18eef8..2df495e1549 100644 --- a/app/src/main/res/drawable/ic_filter_list_white_24dp.xml +++ b/app/src/main/res/drawable/ic_filter_list_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_fitness_center_black_24dp.xml b/app/src/main/res/drawable/ic_fitness_center_black_24dp.xml index 846deb4315d..3e2425e4080 100644 --- a/app/src/main/res/drawable/ic_fitness_center_black_24dp.xml +++ b/app/src/main/res/drawable/ic_fitness_center_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M20.57,14.86L22,13.43 20.57,12 17,15.57 8.43,7 12,3.43 10.57,2 9.14,3.43 7.71,2 5.57,4.14 4.14,2.71 2.71,4.14l1.43,1.43L2,7.71l1.43,1.43L2,10.57 3.43,12 7,8.43 15.57,17 12,20.57 13.43,22l1.43,-1.43L16.29,22l2.14,-2.14 1.43,1.43 1.43,-1.43 -1.43,-1.43L22,16.29z" /> diff --git a/app/src/main/res/drawable/ic_fitness_center_white_24dp.xml b/app/src/main/res/drawable/ic_fitness_center_white_24dp.xml index fec3c955cc0..892def49124 100644 --- a/app/src/main/res/drawable/ic_fitness_center_white_24dp.xml +++ b/app/src/main/res/drawable/ic_fitness_center_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_fullscreen_exit_white_24dp.xml b/app/src/main/res/drawable/ic_fullscreen_exit_white_24dp.xml index bb7140f29bb..a940aa13c86 100644 --- a/app/src/main/res/drawable/ic_fullscreen_exit_white_24dp.xml +++ b/app/src/main/res/drawable/ic_fullscreen_exit_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_fullscreen_white_24dp.xml b/app/src/main/res/drawable/ic_fullscreen_white_24dp.xml index 86b7649b65f..eeda8b43090 100644 --- a/app/src/main/res/drawable/ic_fullscreen_white_24dp.xml +++ b/app/src/main/res/drawable/ic_fullscreen_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_headset_black_24dp.xml b/app/src/main/res/drawable/ic_headset_black_24dp.xml index d4503ce607e..674aa8def40 100644 --- a/app/src/main/res/drawable/ic_headset_black_24dp.xml +++ b/app/src/main/res/drawable/ic_headset_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M12,1c-4.97,0 -9,4.03 -9,9v7c0,1.66 1.34,3 3,3h3v-8H5v-2c0,-3.87 3.13,-7 7,-7s7,3.13 7,7v2h-4v8h3c1.66,0 3,-1.34 3,-3v-7c0,-4.97 -4.03,-9 -9,-9z" /> diff --git a/app/src/main/res/drawable/ic_headset_shadow.xml b/app/src/main/res/drawable/ic_headset_shadow.xml index 53a3ec31a5c..2d6f61eee70 100644 --- a/app/src/main/res/drawable/ic_headset_shadow.xml +++ b/app/src/main/res/drawable/ic_headset_shadow.xml @@ -1,14 +1,17 @@ + android:viewportHeight="24"> + android:strokeWidth="1" + android:strokeAlpha="0.34090909" + android:strokeColor="#000000" + android:strokeLineCap="butt" + android:strokeLineJoin="miter" /> + android:fillColor="#ffffff" + android:pathData="M12,1c-4.97,0 -9,4.03 -9,9v7c0,1.66 1.34,3 3,3h3v-8H5v-2c0,-3.87 3.13,-7 7,-7s7,3.13 7,7v2h-4v8h3c1.66,0 3,-1.34 3,-3v-7c0,-4.97 -4.03,-9 -9,-9z" /> diff --git a/app/src/main/res/drawable/ic_headset_white_24dp.xml b/app/src/main/res/drawable/ic_headset_white_24dp.xml index 2027245b3c7..3ca2936b8cb 100644 --- a/app/src/main/res/drawable/ic_headset_white_24dp.xml +++ b/app/src/main/res/drawable/ic_headset_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_help_black_24dp.xml b/app/src/main/res/drawable/ic_help_black_24dp.xml index 1517747d07b..b1d7a2cf55a 100644 --- a/app/src/main/res/drawable/ic_help_black_24dp.xml +++ b/app/src/main/res/drawable/ic_help_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,19h-2v-2h2v2zM15.07,11.25l-0.9,0.92C13.45,12.9 13,13.5 13,15h-2v-0.5c0,-1.1 0.45,-2.1 1.17,-2.83l1.24,-1.26c0.37,-0.36 0.59,-0.86 0.59,-1.41 0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2L8,9c0,-2.21 1.79,-4 4,-4s4,1.79 4,4c0,0.88 -0.36,1.68 -0.93,2.25z" /> diff --git a/app/src/main/res/drawable/ic_help_white_24dp.xml b/app/src/main/res/drawable/ic_help_white_24dp.xml index d813b72b81f..04c1c00fc25 100644 --- a/app/src/main/res/drawable/ic_help_white_24dp.xml +++ b/app/src/main/res/drawable/ic_help_white_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,19h-2v-2h2v2zM15.07,11.25l-0.9,0.92C13.45,12.9 13,13.5 13,15h-2v-0.5c0,-1.1 0.45,-2.1 1.17,-2.83l1.24,-1.26c0.37,-0.36 0.59,-0.86 0.59,-1.41 0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2L8,9c0,-2.21 1.79,-4 4,-4s4,1.79 4,4c0,0.88 -0.36,1.68 -0.93,2.25z" /> diff --git a/app/src/main/res/drawable/ic_history_black_24dp.xml b/app/src/main/res/drawable/ic_history_black_24dp.xml index a61de1bc9e0..d9f75ea6d73 100644 --- a/app/src/main/res/drawable/ic_history_black_24dp.xml +++ b/app/src/main/res/drawable/ic_history_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z" /> diff --git a/app/src/main/res/drawable/ic_history_white_24dp.xml b/app/src/main/res/drawable/ic_history_white_24dp.xml index de25eb445f3..2418fd6f94b 100644 --- a/app/src/main/res/drawable/ic_history_white_24dp.xml +++ b/app/src/main/res/drawable/ic_history_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_home_black_24dp.xml b/app/src/main/res/drawable/ic_home_black_24dp.xml index 70fb2910c01..f8bb0b55633 100644 --- a/app/src/main/res/drawable/ic_home_black_24dp.xml +++ b/app/src/main/res/drawable/ic_home_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z" /> diff --git a/app/src/main/res/drawable/ic_home_white_24dp.xml b/app/src/main/res/drawable/ic_home_white_24dp.xml index 30296ba99b4..12afe905189 100644 --- a/app/src/main/res/drawable/ic_home_white_24dp.xml +++ b/app/src/main/res/drawable/ic_home_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_hourglass_top_white_24dp.xml b/app/src/main/res/drawable/ic_hourglass_top_white_24dp.xml index d6156dfa18c..59ad4b2d2e3 100644 --- a/app/src/main/res/drawable/ic_hourglass_top_white_24dp.xml +++ b/app/src/main/res/drawable/ic_hourglass_top_white_24dp.xml @@ -3,7 +3,7 @@ android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"> - + diff --git a/app/src/main/res/drawable/ic_import_export_black_24dp.xml b/app/src/main/res/drawable/ic_import_export_black_24dp.xml index a2d1fa99f36..d826451b477 100644 --- a/app/src/main/res/drawable/ic_import_export_black_24dp.xml +++ b/app/src/main/res/drawable/ic_import_export_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M9,3L5,6.99h3L8,14h2L10,6.99h3L9,3zM16,17.01L16,10h-2v7.01h-3L15,21l4,-3.99h-3z" /> diff --git a/app/src/main/res/drawable/ic_import_export_white_24dp.xml b/app/src/main/res/drawable/ic_import_export_white_24dp.xml index 4c6fc6ef6e9..bab84a60dc1 100644 --- a/app/src/main/res/drawable/ic_import_export_white_24dp.xml +++ b/app/src/main/res/drawable/ic_import_export_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_info_outline_black_24dp.xml b/app/src/main/res/drawable/ic_info_outline_black_24dp.xml index cf53e145cd7..9c51a4ffb9a 100644 --- a/app/src/main/res/drawable/ic_info_outline_black_24dp.xml +++ b/app/src/main/res/drawable/ic_info_outline_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z" /> diff --git a/app/src/main/res/drawable/ic_info_outline_white_24dp.xml b/app/src/main/res/drawable/ic_info_outline_white_24dp.xml index af0d4d067a5..2465f780819 100644 --- a/app/src/main/res/drawable/ic_info_outline_white_24dp.xml +++ b/app/src/main/res/drawable/ic_info_outline_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_insert_emoticon_black_24dp.xml b/app/src/main/res/drawable/ic_insert_emoticon_black_24dp.xml index 43d5552cd45..7c2d4625564 100644 --- a/app/src/main/res/drawable/ic_insert_emoticon_black_24dp.xml +++ b/app/src/main/res/drawable/ic_insert_emoticon_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zM15.5,11c0.83,0 1.5,-0.67 1.5,-1.5S16.33,8 15.5,8 14,8.67 14,9.5s0.67,1.5 1.5,1.5zM8.5,11c0.83,0 1.5,-0.67 1.5,-1.5S9.33,8 8.5,8 7,8.67 7,9.5 7.67,11 8.5,11zM12,17.5c2.33,0 4.31,-1.46 5.11,-3.5L6.89,14c0.8,2.04 2.78,3.5 5.11,3.5z" /> diff --git a/app/src/main/res/drawable/ic_insert_emoticon_white_24dp.xml b/app/src/main/res/drawable/ic_insert_emoticon_white_24dp.xml index a438c34efe0..de8e66530cd 100644 --- a/app/src/main/res/drawable/ic_insert_emoticon_white_24dp.xml +++ b/app/src/main/res/drawable/ic_insert_emoticon_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_language_black_24dp.xml b/app/src/main/res/drawable/ic_language_black_24dp.xml index d07324c87df..340a4bf0f54 100644 --- a/app/src/main/res/drawable/ic_language_black_24dp.xml +++ b/app/src/main/res/drawable/ic_language_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM18.92,8h-2.95c-0.32,-1.25 -0.78,-2.45 -1.38,-3.56 1.84,0.63 3.37,1.91 4.33,3.56zM12,4.04c0.83,1.2 1.48,2.53 1.91,3.96h-3.82c0.43,-1.43 1.08,-2.76 1.91,-3.96zM4.26,14C4.1,13.36 4,12.69 4,12s0.1,-1.36 0.26,-2h3.38c-0.08,0.66 -0.14,1.32 -0.14,2 0,0.68 0.06,1.34 0.14,2L4.26,14zM5.08,16h2.95c0.32,1.25 0.78,2.45 1.38,3.56 -1.84,-0.63 -3.37,-1.9 -4.33,-3.56zM8.03,8L5.08,8c0.96,-1.66 2.49,-2.93 4.33,-3.56C8.81,5.55 8.35,6.75 8.03,8zM12,19.96c-0.83,-1.2 -1.48,-2.53 -1.91,-3.96h3.82c-0.43,1.43 -1.08,2.76 -1.91,3.96zM14.34,14L9.66,14c-0.09,-0.66 -0.16,-1.32 -0.16,-2 0,-0.68 0.07,-1.35 0.16,-2h4.68c0.09,0.65 0.16,1.32 0.16,2 0,0.68 -0.07,1.34 -0.16,2zM14.59,19.56c0.6,-1.11 1.06,-2.31 1.38,-3.56h2.95c-0.96,1.65 -2.49,2.93 -4.33,3.56zM16.36,14c0.08,-0.66 0.14,-1.32 0.14,-2 0,-0.68 -0.06,-1.34 -0.14,-2h3.38c0.16,0.64 0.26,1.31 0.26,2s-0.1,1.36 -0.26,2h-3.38z" /> diff --git a/app/src/main/res/drawable/ic_language_white_24dp.xml b/app/src/main/res/drawable/ic_language_white_24dp.xml index 74bc2790343..9b97aa59221 100644 --- a/app/src/main/res/drawable/ic_language_white_24dp.xml +++ b/app/src/main/res/drawable/ic_language_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_list_black_24dp.xml b/app/src/main/res/drawable/ic_list_black_24dp.xml index 4c2fb88349a..1471c52f5ce 100644 --- a/app/src/main/res/drawable/ic_list_black_24dp.xml +++ b/app/src/main/res/drawable/ic_list_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M3,13h2v-2L3,11v2zM3,17h2v-2L3,15v2zM3,9h2L5,7L3,7v2zM7,13h14v-2L7,11v2zM7,17h14v-2L7,15v2zM7,7v2h14L21,7L7,7z" /> diff --git a/app/src/main/res/drawable/ic_list_white_24dp.xml b/app/src/main/res/drawable/ic_list_white_24dp.xml index f47037629f0..4fd341d82cf 100644 --- a/app/src/main/res/drawable/ic_list_white_24dp.xml +++ b/app/src/main/res/drawable/ic_list_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_megaphone_black_24dp.xml b/app/src/main/res/drawable/ic_megaphone_black_24dp.xml index 21622c16271..f75a104db79 100644 --- a/app/src/main/res/drawable/ic_megaphone_black_24dp.xml +++ b/app/src/main/res/drawable/ic_megaphone_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M12,8H4A2,2 0 0,0 2,10V14A2,2 0 0,0 4,16H5V20A1,1 0 0,0 6,21H8A1,1 0 0,0 9,20V16H12L17,20V4L12,8M21.5,12C21.5,13.71 20.54,15.26 19,16V8C20.53,8.75 21.5,10.3 21.5,12Z" /> diff --git a/app/src/main/res/drawable/ic_megaphone_white_24dp.xml b/app/src/main/res/drawable/ic_megaphone_white_24dp.xml index 90e6ff21563..dd87fde1798 100644 --- a/app/src/main/res/drawable/ic_megaphone_white_24dp.xml +++ b/app/src/main/res/drawable/ic_megaphone_white_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M12,8H4A2,2 0 0,0 2,10V14A2,2 0 0,0 4,16H5V20A1,1 0 0,0 6,21H8A1,1 0 0,0 9,20V16H12L17,20V4L12,8M21.5,12C21.5,13.71 20.54,15.26 19,16V8C20.53,8.75 21.5,10.3 21.5,12Z" /> diff --git a/app/src/main/res/drawable/ic_mic_black_24dp.xml b/app/src/main/res/drawable/ic_mic_black_24dp.xml index 25d8951a72f..8b765ffd42c 100644 --- a/app/src/main/res/drawable/ic_mic_black_24dp.xml +++ b/app/src/main/res/drawable/ic_mic_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M12,14c1.66,0 2.99,-1.34 2.99,-3L15,5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6c0,1.66 1.34,3 3,3zM17.3,11c0,3 -2.54,5.1 -5.3,5.1S6.7,14 6.7,11L5,11c0,3.41 2.72,6.23 6,6.72L11,21h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z" /> diff --git a/app/src/main/res/drawable/ic_mic_white_24dp.xml b/app/src/main/res/drawable/ic_mic_white_24dp.xml index 36ee9ff81e4..c0c92fcc76e 100644 --- a/app/src/main/res/drawable/ic_mic_white_24dp.xml +++ b/app/src/main/res/drawable/ic_mic_white_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M12,14c1.66,0 2.99,-1.34 2.99,-3L15,5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6c0,1.66 1.34,3 3,3zM17.3,11c0,3 -2.54,5.1 -5.3,5.1S6.7,14 6.7,11L5,11c0,3.41 2.72,6.23 6,6.72L11,21h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z" /> diff --git a/app/src/main/res/drawable/ic_more_vert_black_24dp.xml b/app/src/main/res/drawable/ic_more_vert_black_24dp.xml index 5176d8a4b15..7b7f195546f 100644 --- a/app/src/main/res/drawable/ic_more_vert_black_24dp.xml +++ b/app/src/main/res/drawable/ic_more_vert_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z" /> diff --git a/app/src/main/res/drawable/ic_more_vert_white_24dp.xml b/app/src/main/res/drawable/ic_more_vert_white_24dp.xml index c097d3e4030..19703e8e7db 100644 --- a/app/src/main/res/drawable/ic_more_vert_white_24dp.xml +++ b/app/src/main/res/drawable/ic_more_vert_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_motorcycle_black_24dp.xml b/app/src/main/res/drawable/ic_motorcycle_black_24dp.xml index 539182f831c..185d6992c58 100644 --- a/app/src/main/res/drawable/ic_motorcycle_black_24dp.xml +++ b/app/src/main/res/drawable/ic_motorcycle_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M19.44,9.03L15.41,5H11v2h3.59l2,2H5c-2.8,0 -5,2.2 -5,5s2.2,5 5,5c2.46,0 4.45,-1.69 4.9,-4h1.65l2.77,-2.77c-0.21,0.54 -0.32,1.14 -0.32,1.77 0,2.8 2.2,5 5,5s5,-2.2 5,-5c0,-2.65 -1.97,-4.77 -4.56,-4.97zM7.82,15C7.4,16.15 6.28,17 5,17c-1.63,0 -3,-1.37 -3,-3s1.37,-3 3,-3c1.28,0 2.4,0.85 2.82,2H5v2h2.82zM19,17c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3z" /> diff --git a/app/src/main/res/drawable/ic_motorcycle_white_24dp.xml b/app/src/main/res/drawable/ic_motorcycle_white_24dp.xml index d5f2519d7a0..2a42a1d093e 100644 --- a/app/src/main/res/drawable/ic_motorcycle_white_24dp.xml +++ b/app/src/main/res/drawable/ic_motorcycle_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_movie_black_24dp.xml b/app/src/main/res/drawable/ic_movie_black_24dp.xml index d70c00f0065..4e5289d38b8 100644 --- a/app/src/main/res/drawable/ic_movie_black_24dp.xml +++ b/app/src/main/res/drawable/ic_movie_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M18,4l2,4h-3l-2,-4h-2l2,4h-3l-2,-4H8l2,4H7L5,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V4h-4z" /> diff --git a/app/src/main/res/drawable/ic_movie_white_24dp.xml b/app/src/main/res/drawable/ic_movie_white_24dp.xml index a1d539a3f26..79f93d1c1ad 100644 --- a/app/src/main/res/drawable/ic_movie_white_24dp.xml +++ b/app/src/main/res/drawable/ic_movie_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_music_note_black_24dp.xml b/app/src/main/res/drawable/ic_music_note_black_24dp.xml index 736c004ef49..830a7fab179 100644 --- a/app/src/main/res/drawable/ic_music_note_black_24dp.xml +++ b/app/src/main/res/drawable/ic_music_note_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M12,3v10.55c-0.59,-0.34 -1.27,-0.55 -2,-0.55 -2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4V7h4V3h-6z" /> diff --git a/app/src/main/res/drawable/ic_music_note_white_24dp.xml b/app/src/main/res/drawable/ic_music_note_white_24dp.xml index 69f0a3a4dd8..ca80ad5ad9b 100644 --- a/app/src/main/res/drawable/ic_music_note_white_24dp.xml +++ b/app/src/main/res/drawable/ic_music_note_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_next_white_24dp.xml b/app/src/main/res/drawable/ic_next_white_24dp.xml index 603880c2b40..1459bff0a0c 100644 --- a/app/src/main/res/drawable/ic_next_white_24dp.xml +++ b/app/src/main/res/drawable/ic_next_white_24dp.xml @@ -1,10 +1,11 @@ + android:width="24dp" + android:height="24dp" + android:tint="#ffffff" + android:viewportWidth="24" + android:viewportHeight="24"> - + diff --git a/app/src/main/res/drawable/ic_palette_black_24dp.xml b/app/src/main/res/drawable/ic_palette_black_24dp.xml index f75e2fbe39a..71c37ddacff 100644 --- a/app/src/main/res/drawable/ic_palette_black_24dp.xml +++ b/app/src/main/res/drawable/ic_palette_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M12,3c-4.97,0 -9,4.03 -9,9s4.03,9 9,9c0.83,0 1.5,-0.67 1.5,-1.5 0,-0.39 -0.15,-0.74 -0.39,-1.01 -0.23,-0.26 -0.38,-0.61 -0.38,-0.99 0,-0.83 0.67,-1.5 1.5,-1.5L16,16c2.76,0 5,-2.24 5,-5 0,-4.42 -4.03,-8 -9,-8zM6.5,12c-0.83,0 -1.5,-0.67 -1.5,-1.5S5.67,9 6.5,9 8,9.67 8,10.5 7.33,12 6.5,12zM9.5,8C8.67,8 8,7.33 8,6.5S8.67,5 9.5,5s1.5,0.67 1.5,1.5S10.33,8 9.5,8zM14.5,8c-0.83,0 -1.5,-0.67 -1.5,-1.5S13.67,5 14.5,5s1.5,0.67 1.5,1.5S15.33,8 14.5,8zM17.5,12c-0.83,0 -1.5,-0.67 -1.5,-1.5S16.67,9 17.5,9s1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5z" /> diff --git a/app/src/main/res/drawable/ic_palette_white_24dp.xml b/app/src/main/res/drawable/ic_palette_white_24dp.xml index 4abeea58f9a..a6cdb536e7a 100644 --- a/app/src/main/res/drawable/ic_palette_white_24dp.xml +++ b/app/src/main/res/drawable/ic_palette_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_pause_black_24dp.xml b/app/src/main/res/drawable/ic_pause_black_24dp.xml index bb28a6c415c..53b5e6a9aac 100644 --- a/app/src/main/res/drawable/ic_pause_black_24dp.xml +++ b/app/src/main/res/drawable/ic_pause_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z" /> diff --git a/app/src/main/res/drawable/ic_pause_white_24dp.xml b/app/src/main/res/drawable/ic_pause_white_24dp.xml index 08b34c2da94..ea843aff309 100644 --- a/app/src/main/res/drawable/ic_pause_white_24dp.xml +++ b/app/src/main/res/drawable/ic_pause_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_people_black_24dp.xml b/app/src/main/res/drawable/ic_people_black_24dp.xml index 4cfd869603b..603c006db55 100644 --- a/app/src/main/res/drawable/ic_people_black_24dp.xml +++ b/app/src/main/res/drawable/ic_people_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M16,11c1.66,0 2.99,-1.34 2.99,-3S17.66,5 16,5c-1.66,0 -3,1.34 -3,3s1.34,3 3,3zM8,11c1.66,0 2.99,-1.34 2.99,-3S9.66,5 8,5C6.34,5 5,6.34 5,8s1.34,3 3,3zM8,13c-2.33,0 -7,1.17 -7,3.5L1,19h14v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5zM16,13c-0.29,0 -0.62,0.02 -0.97,0.05 1.16,0.84 1.97,1.97 1.97,3.45L17,19h6v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5z" /> diff --git a/app/src/main/res/drawable/ic_people_white_24dp.xml b/app/src/main/res/drawable/ic_people_white_24dp.xml index 23afe227031..8b925badcb1 100644 --- a/app/src/main/res/drawable/ic_people_white_24dp.xml +++ b/app/src/main/res/drawable/ic_people_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_person_black_24dp.xml b/app/src/main/res/drawable/ic_person_black_24dp.xml index b2cb337b0d3..55495d5a08e 100644 --- a/app/src/main/res/drawable/ic_person_black_24dp.xml +++ b/app/src/main/res/drawable/ic_person_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z" /> diff --git a/app/src/main/res/drawable/ic_person_white_24dp.xml b/app/src/main/res/drawable/ic_person_white_24dp.xml index d7366bda062..5efaaf0dd79 100644 --- a/app/src/main/res/drawable/ic_person_white_24dp.xml +++ b/app/src/main/res/drawable/ic_person_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_pets_black_24dp.xml b/app/src/main/res/drawable/ic_pets_black_24dp.xml index b6247bd8716..58e52bf6cb9 100644 --- a/app/src/main/res/drawable/ic_pets_black_24dp.xml +++ b/app/src/main/res/drawable/ic_pets_black_24dp.xml @@ -1,21 +1,21 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M4.5,9.5m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0" /> + android:pathData="M9,5.5m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0" /> + android:pathData="M15,5.5m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0" /> + android:pathData="M19.5,9.5m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0" /> + android:pathData="M17.34,14.86c-0.87,-1.02 -1.6,-1.89 -2.48,-2.91 -0.46,-0.54 -1.05,-1.08 -1.75,-1.32 -0.11,-0.04 -0.22,-0.07 -0.33,-0.09 -0.25,-0.04 -0.52,-0.04 -0.78,-0.04s-0.53,0 -0.79,0.05c-0.11,0.02 -0.22,0.05 -0.33,0.09 -0.7,0.24 -1.28,0.78 -1.75,1.32 -0.87,1.02 -1.6,1.89 -2.48,2.91 -1.31,1.31 -2.92,2.76 -2.62,4.79 0.29,1.02 1.02,2.03 2.33,2.32 0.73,0.15 3.06,-0.44 5.54,-0.44h0.18c2.48,0 4.81,0.58 5.54,0.44 1.31,-0.29 2.04,-1.31 2.33,-2.32 0.31,-2.04 -1.3,-3.49 -2.61,-4.8z" /> diff --git a/app/src/main/res/drawable/ic_pets_white_24dp.xml b/app/src/main/res/drawable/ic_pets_white_24dp.xml index 46724a33d00..14373a3c5b8 100644 --- a/app/src/main/res/drawable/ic_pets_white_24dp.xml +++ b/app/src/main/res/drawable/ic_pets_white_24dp.xml @@ -1,21 +1,21 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M4.5,9.5m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0" /> + android:pathData="M9,5.5m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0" /> + android:pathData="M15,5.5m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0" /> + android:pathData="M19.5,9.5m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0" /> + android:pathData="M17.34,14.86c-0.87,-1.02 -1.6,-1.89 -2.48,-2.91 -0.46,-0.54 -1.05,-1.08 -1.75,-1.32 -0.11,-0.04 -0.22,-0.07 -0.33,-0.09 -0.25,-0.04 -0.52,-0.04 -0.78,-0.04s-0.53,0 -0.79,0.05c-0.11,0.02 -0.22,0.05 -0.33,0.09 -0.7,0.24 -1.28,0.78 -1.75,1.32 -0.87,1.02 -1.6,1.89 -2.48,2.91 -1.31,1.31 -2.92,2.76 -2.62,4.79 0.29,1.02 1.02,2.03 2.33,2.32 0.73,0.15 3.06,-0.44 5.54,-0.44h0.18c2.48,0 4.81,0.58 5.54,0.44 1.31,-0.29 2.04,-1.31 2.33,-2.32 0.31,-2.04 -1.3,-3.49 -2.61,-4.8z" /> diff --git a/app/src/main/res/drawable/ic_picture_in_picture_black_24dp.xml b/app/src/main/res/drawable/ic_picture_in_picture_black_24dp.xml index b61c5218b52..326ff030430 100644 --- a/app/src/main/res/drawable/ic_picture_in_picture_black_24dp.xml +++ b/app/src/main/res/drawable/ic_picture_in_picture_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M19,7h-8v6h8L19,7zM21,3L3,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,1.98 2,1.98h18c1.1,0 2,-0.88 2,-1.98L23,5c0,-1.1 -0.9,-2 -2,-2zM21,19.01L3,19.01L3,4.98h18v14.03z" /> diff --git a/app/src/main/res/drawable/ic_picture_in_picture_white_24dp.xml b/app/src/main/res/drawable/ic_picture_in_picture_white_24dp.xml index db1b46f8141..f6b3205cc0c 100644 --- a/app/src/main/res/drawable/ic_picture_in_picture_white_24dp.xml +++ b/app/src/main/res/drawable/ic_picture_in_picture_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_play_arrow_black_24dp.xml b/app/src/main/res/drawable/ic_play_arrow_black_24dp.xml index bf9b895aca9..dbe3ec664e8 100644 --- a/app/src/main/res/drawable/ic_play_arrow_black_24dp.xml +++ b/app/src/main/res/drawable/ic_play_arrow_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M8,5v14l11,-7z" /> diff --git a/app/src/main/res/drawable/ic_play_arrow_shadow.xml b/app/src/main/res/drawable/ic_play_arrow_shadow.xml index 8d5871fad31..bf4b895b0ae 100644 --- a/app/src/main/res/drawable/ic_play_arrow_shadow.xml +++ b/app/src/main/res/drawable/ic_play_arrow_shadow.xml @@ -1,12 +1,25 @@ - - + - + + android:strokeWidth="5" + android:strokeAlpha="0.73333333" + android:strokeColor="#ffffff" + android:strokeLineCap="butt" + android:strokeLineJoin="miter" /> diff --git a/app/src/main/res/drawable/ic_play_arrow_white_24dp.xml b/app/src/main/res/drawable/ic_play_arrow_white_24dp.xml index e135a55b789..098b71d1fb1 100644 --- a/app/src/main/res/drawable/ic_play_arrow_white_24dp.xml +++ b/app/src/main/res/drawable/ic_play_arrow_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_playlist_add_black_24dp.xml b/app/src/main/res/drawable/ic_playlist_add_black_24dp.xml index 905d86e6461..f30f2955781 100644 --- a/app/src/main/res/drawable/ic_playlist_add_black_24dp.xml +++ b/app/src/main/res/drawable/ic_playlist_add_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M14,10L2,10v2h12v-2zM14,6L2,6v2h12L14,6zM18,14v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zM2,16h8v-2L2,14v2z" /> diff --git a/app/src/main/res/drawable/ic_playlist_add_check_black_24dp.xml b/app/src/main/res/drawable/ic_playlist_add_check_black_24dp.xml index 4f7a1c13f42..d99e6e75c12 100644 --- a/app/src/main/res/drawable/ic_playlist_add_check_black_24dp.xml +++ b/app/src/main/res/drawable/ic_playlist_add_check_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M14,10L2,10v2h12v-2zM14,6L2,6v2h12L14,6zM2,16h8v-2L2,14v2zM21.5,11.5L23,13l-6.99,7 -4.51,-4.5L13,14l3.01,3 5.49,-5.5z" /> diff --git a/app/src/main/res/drawable/ic_playlist_add_check_white_24dp.xml b/app/src/main/res/drawable/ic_playlist_add_check_white_24dp.xml index 04b4b785591..0581bbe4ba6 100644 --- a/app/src/main/res/drawable/ic_playlist_add_check_white_24dp.xml +++ b/app/src/main/res/drawable/ic_playlist_add_check_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_playlist_add_white_24dp.xml b/app/src/main/res/drawable/ic_playlist_add_white_24dp.xml index ed27c167e09..1ea41dc2205 100644 --- a/app/src/main/res/drawable/ic_playlist_add_white_24dp.xml +++ b/app/src/main/res/drawable/ic_playlist_add_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_playlist_play_white_24dp.xml b/app/src/main/res/drawable/ic_playlist_play_white_24dp.xml index 06ccbb8ebb2..4e31a4dded5 100644 --- a/app/src/main/res/drawable/ic_playlist_play_white_24dp.xml +++ b/app/src/main/res/drawable/ic_playlist_play_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_previous_white_24dp.xml b/app/src/main/res/drawable/ic_previous_white_24dp.xml index 14279ecb20d..e9fe7448d69 100644 --- a/app/src/main/res/drawable/ic_previous_white_24dp.xml +++ b/app/src/main/res/drawable/ic_previous_white_24dp.xml @@ -1,10 +1,11 @@ + android:width="24dp" + android:height="24dp" + android:tint="#ffffff" + android:viewportWidth="24" + android:viewportHeight="24"> - + diff --git a/app/src/main/res/drawable/ic_public_black_24dp.xml b/app/src/main/res/drawable/ic_public_black_24dp.xml index d976b424424..19288457023 100644 --- a/app/src/main/res/drawable/ic_public_black_24dp.xml +++ b/app/src/main/res/drawable/ic_public_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM11,19.93c-3.95,-0.49 -7,-3.85 -7,-7.93 0,-0.62 0.08,-1.21 0.21,-1.79L9,15v1c0,1.1 0.9,2 2,2v1.93zM17.9,17.39c-0.26,-0.81 -1,-1.39 -1.9,-1.39h-1v-3c0,-0.55 -0.45,-1 -1,-1L8,12v-2h2c0.55,0 1,-0.45 1,-1L11,7h2c1.1,0 2,-0.9 2,-2v-0.41c2.93,1.19 5,4.06 5,7.41 0,2.08 -0.8,3.97 -2.1,5.39z" /> diff --git a/app/src/main/res/drawable/ic_public_white_24dp.xml b/app/src/main/res/drawable/ic_public_white_24dp.xml index 880e4277095..6ae97422a84 100644 --- a/app/src/main/res/drawable/ic_public_white_24dp.xml +++ b/app/src/main/res/drawable/ic_public_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_radio_black_24dp.xml b/app/src/main/res/drawable/ic_radio_black_24dp.xml index 00da9101fc7..ca4501bb7c6 100644 --- a/app/src/main/res/drawable/ic_radio_black_24dp.xml +++ b/app/src/main/res/drawable/ic_radio_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M3.24,6.15C2.51,6.43 2,7.17 2,8v12c0,1.1 0.89,2 2,2h16c1.11,0 2,-0.9 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2L8.3,6l8.26,-3.34L15.88,1 3.24,6.15zM7,20c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM20,12h-2v-2h-2v2L4,12L4,8h16v4z" /> diff --git a/app/src/main/res/drawable/ic_radio_white_24dp.xml b/app/src/main/res/drawable/ic_radio_white_24dp.xml index df563ec1d27..d0902426bec 100644 --- a/app/src/main/res/drawable/ic_radio_white_24dp.xml +++ b/app/src/main/res/drawable/ic_radio_white_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M3.24,6.15C2.51,6.43 2,7.17 2,8v12c0,1.1 0.89,2 2,2h16c1.11,0 2,-0.9 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2L8.3,6l8.26,-3.34L15.88,1 3.24,6.15zM7,20c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM20,12h-2v-2h-2v2L4,12L4,8h16v4z" /> diff --git a/app/src/main/res/drawable/ic_refresh_black_24dp.xml b/app/src/main/res/drawable/ic_refresh_black_24dp.xml index 8229a9a64c2..1f9072a36fa 100644 --- a/app/src/main/res/drawable/ic_refresh_black_24dp.xml +++ b/app/src/main/res/drawable/ic_refresh_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z" /> diff --git a/app/src/main/res/drawable/ic_refresh_white_24dp.xml b/app/src/main/res/drawable/ic_refresh_white_24dp.xml index cc2d1e04ff6..4ca5e73a705 100644 --- a/app/src/main/res/drawable/ic_refresh_white_24dp.xml +++ b/app/src/main/res/drawable/ic_refresh_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_repeat_white_24dp.xml b/app/src/main/res/drawable/ic_repeat_white_24dp.xml index f4e1a4f39b0..24d9f44f0a6 100644 --- a/app/src/main/res/drawable/ic_repeat_white_24dp.xml +++ b/app/src/main/res/drawable/ic_repeat_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_replay_white_24dp.xml b/app/src/main/res/drawable/ic_replay_white_24dp.xml index 8e84c195bd9..d00231b51c1 100644 --- a/app/src/main/res/drawable/ic_replay_white_24dp.xml +++ b/app/src/main/res/drawable/ic_replay_white_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M12,5V1L7,6l5,5V7c3.31,0 6,2.69 6,6s-2.69,6 -6,6 -6,-2.69 -6,-6H4c0,4.42 3.58,8 8,8s8,-3.58 8,-8 -3.58,-8 -8,-8z" /> diff --git a/app/src/main/res/drawable/ic_restaurant_black_24dp.xml b/app/src/main/res/drawable/ic_restaurant_black_24dp.xml index e14429d09ec..51f1145c682 100644 --- a/app/src/main/res/drawable/ic_restaurant_black_24dp.xml +++ b/app/src/main/res/drawable/ic_restaurant_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M11,9L9,9L9,2L7,2v7L5,9L5,2L3,2v7c0,2.12 1.66,3.84 3.75,3.97L6.75,22h2.5v-9.03C11.34,12.84 13,11.12 13,9L13,2h-2v7zM16,6v8h2.5v8L21,22L21,2c-2.76,0 -5,2.24 -5,4z" /> diff --git a/app/src/main/res/drawable/ic_restaurant_white_24dp.xml b/app/src/main/res/drawable/ic_restaurant_white_24dp.xml index 1e2d89c0ff4..dbb84968023 100644 --- a/app/src/main/res/drawable/ic_restaurant_white_24dp.xml +++ b/app/src/main/res/drawable/ic_restaurant_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_rss_feed_black_24dp.xml b/app/src/main/res/drawable/ic_rss_feed_black_24dp.xml index 4da9b623b63..ed6228cc253 100644 --- a/app/src/main/res/drawable/ic_rss_feed_black_24dp.xml +++ b/app/src/main/res/drawable/ic_rss_feed_black_24dp.xml @@ -1,12 +1,12 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M6.18,17.82m-2.18,0a2.18,2.18 0,1 1,4.36 0a2.18,2.18 0,1 1,-4.36 0" /> + android:pathData="M4,4.44v2.83c7.03,0 12.73,5.7 12.73,12.73h2.83c0,-8.59 -6.97,-15.56 -15.56,-15.56zM4,10.1v2.83c3.9,0 7.07,3.17 7.07,7.07h2.83c0,-5.47 -4.43,-9.9 -9.9,-9.9z" /> diff --git a/app/src/main/res/drawable/ic_rss_feed_white_24dp.xml b/app/src/main/res/drawable/ic_rss_feed_white_24dp.xml index 42a802c7ef0..193f4fe924e 100644 --- a/app/src/main/res/drawable/ic_rss_feed_white_24dp.xml +++ b/app/src/main/res/drawable/ic_rss_feed_white_24dp.xml @@ -1,6 +1,13 @@ - - - + + + diff --git a/app/src/main/res/drawable/ic_save_black_24dp.xml b/app/src/main/res/drawable/ic_save_black_24dp.xml index a561d632a54..0651fcc6c36 100644 --- a/app/src/main/res/drawable/ic_save_black_24dp.xml +++ b/app/src/main/res/drawable/ic_save_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M17,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,7l-4,-4zM12,19c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM15,9L5,9L5,5h10v4z" /> diff --git a/app/src/main/res/drawable/ic_save_white_24dp.xml b/app/src/main/res/drawable/ic_save_white_24dp.xml index 74ca299c360..b32b11451fc 100644 --- a/app/src/main/res/drawable/ic_save_white_24dp.xml +++ b/app/src/main/res/drawable/ic_save_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_school_black_24dp.xml b/app/src/main/res/drawable/ic_school_black_24dp.xml index 30d83f84072..54dc17ddb53 100644 --- a/app/src/main/res/drawable/ic_school_black_24dp.xml +++ b/app/src/main/res/drawable/ic_school_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M5,13.18v4L12,21l7,-3.82v-4L12,17l-7,-3.82zM12,3L1,9l11,6 9,-4.91V17h2V9L12,3z" /> diff --git a/app/src/main/res/drawable/ic_school_white_24dp.xml b/app/src/main/res/drawable/ic_school_white_24dp.xml index e9fbe593135..dc16c478223 100644 --- a/app/src/main/res/drawable/ic_school_white_24dp.xml +++ b/app/src/main/res/drawable/ic_school_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_screen_rotation_white_24dp.xml b/app/src/main/res/drawable/ic_screen_rotation_white_24dp.xml index 1372f04a03c..cc851bd83ca 100644 --- a/app/src/main/res/drawable/ic_screen_rotation_white_24dp.xml +++ b/app/src/main/res/drawable/ic_screen_rotation_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_search_add_black_24dp.xml b/app/src/main/res/drawable/ic_search_add_black_24dp.xml index a5264a6a3c2..398f1407db2 100644 --- a/app/src/main/res/drawable/ic_search_add_black_24dp.xml +++ b/app/src/main/res/drawable/ic_search_add_black_24dp.xml @@ -3,7 +3,7 @@ android:height="24dp" android:viewportWidth="24.0" android:viewportHeight="24.0"> - + diff --git a/app/src/main/res/drawable/ic_search_add_white_24dp.xml b/app/src/main/res/drawable/ic_search_add_white_24dp.xml index 9341522dff6..57c8a1ada51 100644 --- a/app/src/main/res/drawable/ic_search_add_white_24dp.xml +++ b/app/src/main/res/drawable/ic_search_add_white_24dp.xml @@ -3,7 +3,7 @@ android:height="24dp" android:viewportWidth="24.0" android:viewportHeight="24.0"> - + diff --git a/app/src/main/res/drawable/ic_search_black_24dp.xml b/app/src/main/res/drawable/ic_search_black_24dp.xml index affc7ba26f8..d23ea57f880 100644 --- a/app/src/main/res/drawable/ic_search_black_24dp.xml +++ b/app/src/main/res/drawable/ic_search_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z" /> diff --git a/app/src/main/res/drawable/ic_search_white_24dp.xml b/app/src/main/res/drawable/ic_search_white_24dp.xml index be5ad99c638..4d0f185842a 100644 --- a/app/src/main/res/drawable/ic_search_white_24dp.xml +++ b/app/src/main/res/drawable/ic_search_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_settings_backup_restore_black_24dp.xml b/app/src/main/res/drawable/ic_settings_backup_restore_black_24dp.xml index aa424c0d4e5..57f96653687 100644 --- a/app/src/main/res/drawable/ic_settings_backup_restore_black_24dp.xml +++ b/app/src/main/res/drawable/ic_settings_backup_restore_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M14,12c0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2 0.9,2 2,2 2,-0.9 2,-2zM12,3c-4.97,0 -9,4.03 -9,9L0,12l4,4 4,-4L5,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.51,0 -2.91,-0.49 -4.06,-1.3l-1.42,1.44C8.04,20.3 9.94,21 12,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9z" /> diff --git a/app/src/main/res/drawable/ic_settings_backup_restore_white_24dp.xml b/app/src/main/res/drawable/ic_settings_backup_restore_white_24dp.xml index e3e6530bfe3..ae22bc141f8 100644 --- a/app/src/main/res/drawable/ic_settings_backup_restore_white_24dp.xml +++ b/app/src/main/res/drawable/ic_settings_backup_restore_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_settings_black_24dp.xml b/app/src/main/res/drawable/ic_settings_black_24dp.xml index 24a5623cd86..a671514fcf9 100644 --- a/app/src/main/res/drawable/ic_settings_black_24dp.xml +++ b/app/src/main/res/drawable/ic_settings_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M19.1,12.9a2.8,2.8 0,0 0,0.1 -0.9,2.8 2.8,0 0,0 -0.1,-0.9l2.1,-1.6a0.7,0.7 0,0 0,0.1 -0.6L19.4,5.5a0.7,0.7 0,0 0,-0.6 -0.2l-2.4,1a6.5,6.5 0,0 0,-1.6 -0.9l-0.4,-2.6a0.5,0.5 0,0 0,-0.5 -0.4H10.1a0.5,0.5 0,0 0,-0.5 0.4L9.3,5.4a5.6,5.6 0,0 0,-1.7 0.9l-2.4,-1a0.4,0.4 0,0 0,-0.5 0.2l-2,3.4c-0.1,0.2 0,0.4 0.2,0.6l2,1.6a2.8,2.8 0,0 0,-0.1 0.9,2.8 2.8,0 0,0 0.1,0.9L2.8,14.5a0.7,0.7 0,0 0,-0.1 0.6l1.9,3.4a0.7,0.7 0,0 0,0.6 0.2l2.4,-1a6.5,6.5 0,0 0,1.6 0.9l0.4,2.6a0.5,0.5 0,0 0,0.5 0.4h3.8a0.5,0.5 0,0 0,0.5 -0.4l0.3,-2.6a5.6,5.6 0,0 0,1.7 -0.9l2.4,1a0.4,0.4 0,0 0,0.5 -0.2l2,-3.4c0.1,-0.2 0,-0.4 -0.2,-0.6ZM12,15.6A3.6,3.6 0,1 1,15.6 12,3.6 3.6,0 0,1 12,15.6Z" /> diff --git a/app/src/main/res/drawable/ic_settings_white_24dp.xml b/app/src/main/res/drawable/ic_settings_white_24dp.xml index 1397d370ead..bc30c52d8f3 100644 --- a/app/src/main/res/drawable/ic_settings_white_24dp.xml +++ b/app/src/main/res/drawable/ic_settings_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_share_black_24dp.xml b/app/src/main/res/drawable/ic_share_black_24dp.xml index e3fe874d61e..338d95ad509 100644 --- a/app/src/main/res/drawable/ic_share_black_24dp.xml +++ b/app/src/main/res/drawable/ic_share_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z" /> diff --git a/app/src/main/res/drawable/ic_share_white_24dp.xml b/app/src/main/res/drawable/ic_share_white_24dp.xml index 045bbc0c0e0..9dad7b85f24 100644 --- a/app/src/main/res/drawable/ic_share_white_24dp.xml +++ b/app/src/main/res/drawable/ic_share_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_shopping_cart_black_24dp.xml b/app/src/main/res/drawable/ic_shopping_cart_black_24dp.xml index 452332095ec..18e1b930d61 100644 --- a/app/src/main/res/drawable/ic_shopping_cart_black_24dp.xml +++ b/app/src/main/res/drawable/ic_shopping_cart_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> - \ No newline at end of file + android:pathData="M7,18c-1.1,0 -1.99,0.9 -1.99,2S5.9,22 7,22s2,-0.9 2,-2 -0.9,-2 -2,-2zM1,2v2h2l3.6,7.59 -1.35,2.45c-0.16,0.28 -0.25,0.61 -0.25,0.96 0,1.1 0.9,2 2,2h12v-2L7.42,15c-0.14,0 -0.25,-0.11 -0.25,-0.25l0.03,-0.12 0.9,-1.63h7.45c0.75,0 1.41,-0.41 1.75,-1.03l3.58,-6.49c0.08,-0.14 0.12,-0.31 0.12,-0.48 0,-0.55 -0.45,-1 -1,-1L5.21,4l-0.94,-2L1,2zM17,18c-1.1,0 -1.99,0.9 -1.99,2s0.89,2 1.99,2 2,-0.9 2,-2 -0.9,-2 -2,-2z" /> + diff --git a/app/src/main/res/drawable/ic_shopping_cart_white_24dp.xml b/app/src/main/res/drawable/ic_shopping_cart_white_24dp.xml index a55bf8a88bb..75c330cefc4 100644 --- a/app/src/main/res/drawable/ic_shopping_cart_white_24dp.xml +++ b/app/src/main/res/drawable/ic_shopping_cart_white_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M7,18c-1.1,0 -1.99,0.9 -1.99,2S5.9,22 7,22s2,-0.9 2,-2 -0.9,-2 -2,-2zM1,2v2h2l3.6,7.59 -1.35,2.45c-0.16,0.28 -0.25,0.61 -0.25,0.96 0,1.1 0.9,2 2,2h12v-2L7.42,15c-0.14,0 -0.25,-0.11 -0.25,-0.25l0.03,-0.12 0.9,-1.63h7.45c0.75,0 1.41,-0.41 1.75,-1.03l3.58,-6.49c0.08,-0.14 0.12,-0.31 0.12,-0.48 0,-0.55 -0.45,-1 -1,-1L5.21,4l-0.94,-2L1,2zM17,18c-1.1,0 -1.99,0.9 -1.99,2s0.89,2 1.99,2 2,-0.9 2,-2 -0.9,-2 -2,-2z" /> diff --git a/app/src/main/res/drawable/ic_shuffle_white_24dp.xml b/app/src/main/res/drawable/ic_shuffle_white_24dp.xml index 9ab22017bd9..1192dec9ffd 100644 --- a/app/src/main/res/drawable/ic_shuffle_white_24dp.xml +++ b/app/src/main/res/drawable/ic_shuffle_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_sort_black_24dp.xml b/app/src/main/res/drawable/ic_sort_black_24dp.xml index fd4c56f0e09..b537e982ec1 100644 --- a/app/src/main/res/drawable/ic_sort_black_24dp.xml +++ b/app/src/main/res/drawable/ic_sort_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M3,18h6v-2L3,16v2zM3,6v2h18L21,6L3,6zM3,13h12v-2L3,11v2z" /> diff --git a/app/src/main/res/drawable/ic_sort_white_24dp.xml b/app/src/main/res/drawable/ic_sort_white_24dp.xml index a0c153ad014..484be5ad2db 100644 --- a/app/src/main/res/drawable/ic_sort_white_24dp.xml +++ b/app/src/main/res/drawable/ic_sort_white_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M3,18h6v-2L3,16v2zM3,6v2h18L21,6L3,6zM3,13h12v-2L3,11v2z" /> diff --git a/app/src/main/res/drawable/ic_stars_black_24dp.xml b/app/src/main/res/drawable/ic_stars_black_24dp.xml index 61c5d7ace25..35957427d0a 100644 --- a/app/src/main/res/drawable/ic_stars_black_24dp.xml +++ b/app/src/main/res/drawable/ic_stars_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM16.23,18L12,15.45 7.77,18l1.12,-4.81 -3.73,-3.23 4.92,-0.42L12,5l1.92,4.53 4.92,0.42 -3.73,3.23L16.23,18z" /> diff --git a/app/src/main/res/drawable/ic_stars_white_24dp.xml b/app/src/main/res/drawable/ic_stars_white_24dp.xml index 926e5a10612..135980afebe 100644 --- a/app/src/main/res/drawable/ic_stars_white_24dp.xml +++ b/app/src/main/res/drawable/ic_stars_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_subtitles_white_24dp.xml b/app/src/main/res/drawable/ic_subtitles_white_24dp.xml index 1052d147570..1d997a03251 100644 --- a/app/src/main/res/drawable/ic_subtitles_white_24dp.xml +++ b/app/src/main/res/drawable/ic_subtitles_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_telescope_black_24dp.xml b/app/src/main/res/drawable/ic_telescope_black_24dp.xml index 9c6132eccbd..8077e9325ac 100644 --- a/app/src/main/res/drawable/ic_telescope_black_24dp.xml +++ b/app/src/main/res/drawable/ic_telescope_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M21.9,8.9L20.2,9.9L16.2,3L17.9,2L21.9,8.9M9.8,7.9L12.8,13.1L18.9,9.6L15.9,4.4L9.8,7.9M11.4,12.7L9.4,9.2L5.1,11.7L7.1,15.2L11.4,12.7M2.1,14.6L3.1,16.3L5.7,14.8L4.7,13.1L2.1,14.6M12.1,14L11.8,13.6L7.5,16.1L7.8,16.5C8,16.8 8.3,17.1 8.6,17.3L7,22H9L10.4,17.7H10.5L12,22H14L12.1,16.4C12.6,15.7 12.6,14.8 12.1,14Z" /> diff --git a/app/src/main/res/drawable/ic_telescope_white_24dp.xml b/app/src/main/res/drawable/ic_telescope_white_24dp.xml index ea870fd8733..86468f34ad7 100644 --- a/app/src/main/res/drawable/ic_telescope_white_24dp.xml +++ b/app/src/main/res/drawable/ic_telescope_white_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M21.9,8.9L20.2,9.9L16.2,3L17.9,2L21.9,8.9M9.8,7.9L12.8,13.1L18.9,9.6L15.9,4.4L9.8,7.9M11.4,12.7L9.4,9.2L5.1,11.7L7.1,15.2L11.4,12.7M2.1,14.6L3.1,16.3L5.7,14.8L4.7,13.1L2.1,14.6M12.1,14L11.8,13.6L7.5,16.1L7.8,16.5C8,16.8 8.3,17.1 8.6,17.3L7,22H9L10.4,17.7H10.5L12,22H14L12.1,16.4C12.6,15.7 12.6,14.8 12.1,14Z" /> diff --git a/app/src/main/res/drawable/ic_thumb_down_black_24dp.xml b/app/src/main/res/drawable/ic_thumb_down_black_24dp.xml index 26ba95c859a..0484764da9c 100644 --- a/app/src/main/res/drawable/ic_thumb_down_black_24dp.xml +++ b/app/src/main/res/drawable/ic_thumb_down_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M15,3L6,3c-0.83,0 -1.54,0.5 -1.84,1.22l-3.02,7.05c-0.09,0.23 -0.14,0.47 -0.14,0.73v1.91l0.01,0.01L1,14c0,1.1 0.9,2 2,2h6.31l-0.95,4.57 -0.03,0.32c0,0.41 0.17,0.79 0.44,1.06L9.83,23l6.59,-6.59c0.36,-0.36 0.58,-0.86 0.58,-1.41L17,5c0,-1.1 -0.9,-2 -2,-2zM19,3v12h4L23,3h-4z" /> diff --git a/app/src/main/res/drawable/ic_thumb_down_white_24dp.xml b/app/src/main/res/drawable/ic_thumb_down_white_24dp.xml index 72a99e6b0bc..0303551c792 100644 --- a/app/src/main/res/drawable/ic_thumb_down_white_24dp.xml +++ b/app/src/main/res/drawable/ic_thumb_down_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_thumb_up_black_24dp.xml b/app/src/main/res/drawable/ic_thumb_up_black_24dp.xml index 34fb51ab30c..58dbfafe413 100644 --- a/app/src/main/res/drawable/ic_thumb_up_black_24dp.xml +++ b/app/src/main/res/drawable/ic_thumb_up_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M1,21h4L5,9L1,9v12zM23,10c0,-1.1 -0.9,-2 -2,-2h-6.31l0.95,-4.57 0.03,-0.32c0,-0.41 -0.17,-0.79 -0.44,-1.06L14.17,1 7.59,7.59C7.22,7.95 7,8.45 7,9v10c0,1.1 0.9,2 2,2h9c0.83,0 1.54,-0.5 1.84,-1.22l3.02,-7.05c0.09,-0.23 0.14,-0.47 0.14,-0.73v-1.91l-0.01,-0.01L23,10z" /> diff --git a/app/src/main/res/drawable/ic_thumb_up_white_24dp.xml b/app/src/main/res/drawable/ic_thumb_up_white_24dp.xml index d9acf75000b..f83de1ecc8a 100644 --- a/app/src/main/res/drawable/ic_thumb_up_white_24dp.xml +++ b/app/src/main/res/drawable/ic_thumb_up_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_trending_up_black_24dp.xml b/app/src/main/res/drawable/ic_trending_up_black_24dp.xml index 4c9da94b922..c00c42e4392 100644 --- a/app/src/main/res/drawable/ic_trending_up_black_24dp.xml +++ b/app/src/main/res/drawable/ic_trending_up_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M16,6l2.29,2.29 -4.88,4.88 -4,-4L2,16.59 3.41,18l6,-6 4,4 6.3,-6.29L22,12V6z" /> diff --git a/app/src/main/res/drawable/ic_trending_up_white_24dp.xml b/app/src/main/res/drawable/ic_trending_up_white_24dp.xml index 4d3859d53a1..ca4eb654b46 100644 --- a/app/src/main/res/drawable/ic_trending_up_white_24dp.xml +++ b/app/src/main/res/drawable/ic_trending_up_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_tv_black_24dp.xml b/app/src/main/res/drawable/ic_tv_black_24dp.xml index 771363883e5..11d2d25b649 100644 --- a/app/src/main/res/drawable/ic_tv_black_24dp.xml +++ b/app/src/main/res/drawable/ic_tv_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M21,6h-7.59l3.29,-3.29L16,2l-4,4 -4,-4 -0.71,0.71L10.59,6L3,6c-1.1,0 -2,0.89 -2,2v12c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2L23,8c0,-1.11 -0.9,-2 -2,-2zM21,20L3,20L3,8h18v12z" /> diff --git a/app/src/main/res/drawable/ic_tv_white_24dp.xml b/app/src/main/res/drawable/ic_tv_white_24dp.xml index 0286ef16e2a..b9d14869b61 100644 --- a/app/src/main/res/drawable/ic_tv_white_24dp.xml +++ b/app/src/main/res/drawable/ic_tv_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_videogame_asset_black_24dp.xml b/app/src/main/res/drawable/ic_videogame_asset_black_24dp.xml index 52658f650e6..02fa7eb56a4 100644 --- a/app/src/main/res/drawable/ic_videogame_asset_black_24dp.xml +++ b/app/src/main/res/drawable/ic_videogame_asset_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M21,6L3,6c-1.1,0 -2,0.9 -2,2v8c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2L23,8c0,-1.1 -0.9,-2 -2,-2zM11,13L8,13v3L6,16v-3L3,13v-2h3L6,8h2v3h3v2zM15.5,15c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zM19.5,12c-0.83,0 -1.5,-0.67 -1.5,-1.5S18.67,9 19.5,9s1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5z" /> diff --git a/app/src/main/res/drawable/ic_videogame_asset_white_24dp.xml b/app/src/main/res/drawable/ic_videogame_asset_white_24dp.xml index 46ec002cb05..4861bf809d0 100644 --- a/app/src/main/res/drawable/ic_videogame_asset_white_24dp.xml +++ b/app/src/main/res/drawable/ic_videogame_asset_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_volume_down_white_24dp.xml b/app/src/main/res/drawable/ic_volume_down_white_24dp.xml index 3a769637bf1..bcc36327986 100644 --- a/app/src/main/res/drawable/ic_volume_down_white_24dp.xml +++ b/app/src/main/res/drawable/ic_volume_down_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_volume_mute_white_24dp.xml b/app/src/main/res/drawable/ic_volume_mute_white_24dp.xml index dac85f981d8..2c915139695 100644 --- a/app/src/main/res/drawable/ic_volume_mute_white_24dp.xml +++ b/app/src/main/res/drawable/ic_volume_mute_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_volume_off_black_24dp.xml b/app/src/main/res/drawable/ic_volume_off_black_24dp.xml index 3aed66ddc7b..19f166ddce8 100644 --- a/app/src/main/res/drawable/ic_volume_off_black_24dp.xml +++ b/app/src/main/res/drawable/ic_volume_off_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v2.21l2.45,2.45c0.03,-0.2 0.05,-0.41 0.05,-0.63zM19,12c0,0.94 -0.2,1.82 -0.54,2.64l1.51,1.51C20.63,14.91 21,13.5 21,12c0,-4.28 -2.99,-7.86 -7,-8.77v2.06c2.89,0.86 5,3.54 5,6.71zM4.27,3L3,4.27 7.73,9L3,9v6h4l5,5v-6.73l4.25,4.25c-0.67,0.52 -1.42,0.93 -2.25,1.18v2.06c1.38,-0.31 2.63,-0.95 3.69,-1.81L19.73,21 21,19.73l-9,-9L4.27,3zM12,4L9.91,6.09 12,8.18L12,4z" /> diff --git a/app/src/main/res/drawable/ic_volume_off_white_24dp.xml b/app/src/main/res/drawable/ic_volume_off_white_24dp.xml index a266d9731bf..2f8d6cfb430 100644 --- a/app/src/main/res/drawable/ic_volume_off_white_24dp.xml +++ b/app/src/main/res/drawable/ic_volume_off_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_volume_up_black_24dp.xml b/app/src/main/res/drawable/ic_volume_up_black_24dp.xml index bb0c74ba12d..2ee5bce4333 100644 --- a/app/src/main/res/drawable/ic_volume_up_black_24dp.xml +++ b/app/src/main/res/drawable/ic_volume_up_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z" /> diff --git a/app/src/main/res/drawable/ic_volume_up_white_24dp.xml b/app/src/main/res/drawable/ic_volume_up_white_24dp.xml index 2715409466b..5d604f823ad 100644 --- a/app/src/main/res/drawable/ic_volume_up_white_24dp.xml +++ b/app/src/main/res/drawable/ic_volume_up_white_24dp.xml @@ -5,5 +5,5 @@ android:viewportHeight="24.0"> + android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z" /> diff --git a/app/src/main/res/drawable/ic_watch_later_black_24dp.xml b/app/src/main/res/drawable/ic_watch_later_black_24dp.xml index 5a1b9ac74b5..5fe8273d929 100644 --- a/app/src/main/res/drawable/ic_watch_later_black_24dp.xml +++ b/app/src/main/res/drawable/ic_watch_later_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M12,2C6.5,2 2,6.5 2,12s4.5,10 10,10 10,-4.5 10,-10S17.5,2 12,2zM16.2,16.2L11,13L11,7h1.5v5.2l4.5,2.7 -0.8,1.3z" /> diff --git a/app/src/main/res/drawable/ic_watch_later_white_24dp.xml b/app/src/main/res/drawable/ic_watch_later_white_24dp.xml index f9fffbc435d..b7991f0c7cf 100644 --- a/app/src/main/res/drawable/ic_watch_later_white_24dp.xml +++ b/app/src/main/res/drawable/ic_watch_later_white_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M12,2C6.5,2 2,6.5 2,12s4.5,10 10,10 10,-4.5 10,-10S17.5,2 12,2zM16.2,16.2L11,13L11,7h1.5v5.2l4.5,2.7 -0.8,1.3z" /> diff --git a/app/src/main/res/drawable/ic_wb_sunny_black_24dp.xml b/app/src/main/res/drawable/ic_wb_sunny_black_24dp.xml index a56fb5049c8..36c8089a324 100644 --- a/app/src/main/res/drawable/ic_wb_sunny_black_24dp.xml +++ b/app/src/main/res/drawable/ic_wb_sunny_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M6.76,4.84l-1.8,-1.79 -1.41,1.41 1.79,1.79 1.42,-1.41zM4,10.5L1,10.5v2h3v-2zM13,0.55h-2L11,3.5h2L13,0.55zM20.45,4.46l-1.41,-1.41 -1.79,1.79 1.41,1.41 1.79,-1.79zM17.24,18.16l1.79,1.8 1.41,-1.41 -1.8,-1.79 -1.4,1.4zM20,10.5v2h3v-2h-3zM12,5.5c-3.31,0 -6,2.69 -6,6s2.69,6 6,6 6,-2.69 6,-6 -2.69,-6 -6,-6zM11,22.45h2L13,19.5h-2v2.95zM3.55,18.54l1.41,1.41 1.79,-1.8 -1.41,-1.41 -1.79,1.8z" /> diff --git a/app/src/main/res/drawable/ic_wb_sunny_white_24dp.xml b/app/src/main/res/drawable/ic_wb_sunny_white_24dp.xml index 5d22bab000d..12a5d977491 100644 --- a/app/src/main/res/drawable/ic_wb_sunny_white_24dp.xml +++ b/app/src/main/res/drawable/ic_wb_sunny_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_whatshot_black_24dp.xml b/app/src/main/res/drawable/ic_whatshot_black_24dp.xml index 1cbc037f728..07965067e95 100644 --- a/app/src/main/res/drawable/ic_whatshot_black_24dp.xml +++ b/app/src/main/res/drawable/ic_whatshot_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M13.5,0.67s0.74,2.65 0.74,4.8c0,2.06 -1.35,3.73 -3.41,3.73 -2.07,0 -3.63,-1.67 -3.63,-3.73l0.03,-0.36C5.21,7.51 4,10.62 4,14c0,4.42 3.58,8 8,8s8,-3.58 8,-8C20,8.61 17.41,3.8 13.5,0.67zM11.71,19c-1.78,0 -3.22,-1.4 -3.22,-3.14 0,-1.62 1.05,-2.76 2.81,-3.12 1.77,-0.36 3.6,-1.21 4.62,-2.58 0.39,1.29 0.59,2.65 0.59,4.04 0,2.65 -2.15,4.8 -4.8,4.8z" /> diff --git a/app/src/main/res/drawable/ic_whatshot_white_24dp.xml b/app/src/main/res/drawable/ic_whatshot_white_24dp.xml index 9aa2124f3f9..935ac845088 100644 --- a/app/src/main/res/drawable/ic_whatshot_white_24dp.xml +++ b/app/src/main/res/drawable/ic_whatshot_white_24dp.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_work_black_24dp.xml b/app/src/main/res/drawable/ic_work_black_24dp.xml index 2668f2c43d3..2ee55ea23e0 100644 --- a/app/src/main/res/drawable/ic_work_black_24dp.xml +++ b/app/src/main/res/drawable/ic_work_black_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M20,6h-4L16,4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2L4,6c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM14,6h-4L10,4h4v2z" /> diff --git a/app/src/main/res/drawable/ic_work_white_24dp.xml b/app/src/main/res/drawable/ic_work_white_24dp.xml index 8a1db78288a..8af0219f954 100644 --- a/app/src/main/res/drawable/ic_work_white_24dp.xml +++ b/app/src/main/res/drawable/ic_work_white_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M20,6h-4L16,4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2L4,6c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM14,6h-4L10,4h4v2z" /> diff --git a/app/src/main/res/drawable/player_controls_background.xml b/app/src/main/res/drawable/player_controls_background.xml index f250e3558f5..cd25f2b04ec 100644 --- a/app/src/main/res/drawable/player_controls_background.xml +++ b/app/src/main/res/drawable/player_controls_background.xml @@ -3,5 +3,5 @@ - \ No newline at end of file + android:startColor="@color/video_overlay_color" /> + diff --git a/app/src/main/res/drawable/player_controls_top_background.xml b/app/src/main/res/drawable/player_controls_top_background.xml index ba62ce8638d..92f9fddca9f 100644 --- a/app/src/main/res/drawable/player_controls_top_background.xml +++ b/app/src/main/res/drawable/player_controls_top_background.xml @@ -3,5 +3,5 @@ - \ No newline at end of file + android:startColor="@color/video_overlay_color" /> + diff --git a/app/src/main/res/drawable/progress_circular_white.xml b/app/src/main/res/drawable/progress_circular_white.xml index 0de71afec1b..79e6f54a604 100644 --- a/app/src/main/res/drawable/progress_circular_white.xml +++ b/app/src/main/res/drawable/progress_circular_white.xml @@ -9,4 +9,4 @@ android:useLevel="true"> - \ No newline at end of file + diff --git a/app/src/main/res/drawable/progress_soundcloud_horizontal_dark.xml b/app/src/main/res/drawable/progress_soundcloud_horizontal_dark.xml index bf6a3ae2317..54a85012543 100644 --- a/app/src/main/res/drawable/progress_soundcloud_horizontal_dark.xml +++ b/app/src/main/res/drawable/progress_soundcloud_horizontal_dark.xml @@ -1,6 +1,6 @@ - + @@ -12,4 +12,4 @@ - \ No newline at end of file + diff --git a/app/src/main/res/drawable/progress_soundcloud_horizontal_light.xml b/app/src/main/res/drawable/progress_soundcloud_horizontal_light.xml index 0b3000de02b..3fb6651fa57 100644 --- a/app/src/main/res/drawable/progress_soundcloud_horizontal_light.xml +++ b/app/src/main/res/drawable/progress_soundcloud_horizontal_light.xml @@ -12,4 +12,4 @@ - \ No newline at end of file + diff --git a/app/src/main/res/drawable/progress_youtube_horizontal_dark.xml b/app/src/main/res/drawable/progress_youtube_horizontal_dark.xml index 7f4520eb880..4815aec7c9f 100644 --- a/app/src/main/res/drawable/progress_youtube_horizontal_dark.xml +++ b/app/src/main/res/drawable/progress_youtube_horizontal_dark.xml @@ -12,4 +12,4 @@ - \ No newline at end of file + diff --git a/app/src/main/res/drawable/progress_youtube_horizontal_light.xml b/app/src/main/res/drawable/progress_youtube_horizontal_light.xml index d1556de914c..4c85370d55d 100644 --- a/app/src/main/res/drawable/progress_youtube_horizontal_light.xml +++ b/app/src/main/res/drawable/progress_youtube_horizontal_light.xml @@ -12,4 +12,4 @@ - \ No newline at end of file + diff --git a/app/src/main/res/drawable/selector_checked_dark.xml b/app/src/main/res/drawable/selector_checked_dark.xml index 59019470ff0..da05e96c685 100644 --- a/app/src/main/res/drawable/selector_checked_dark.xml +++ b/app/src/main/res/drawable/selector_checked_dark.xml @@ -4,4 +4,4 @@ - \ No newline at end of file + diff --git a/app/src/main/res/drawable/selector_checked_light.xml b/app/src/main/res/drawable/selector_checked_light.xml index b782a3688d3..e64b8d083ae 100644 --- a/app/src/main/res/drawable/selector_checked_light.xml +++ b/app/src/main/res/drawable/selector_checked_light.xml @@ -4,4 +4,4 @@ - \ No newline at end of file + diff --git a/app/src/main/res/drawable/selector_dark.xml b/app/src/main/res/drawable/selector_dark.xml index eb658e16d5f..0c79be29279 100644 --- a/app/src/main/res/drawable/selector_dark.xml +++ b/app/src/main/res/drawable/selector_dark.xml @@ -1,5 +1,5 @@ - - + + diff --git a/app/src/main/res/drawable/selector_focused_dark.xml b/app/src/main/res/drawable/selector_focused_dark.xml index 102f40d76ee..508083fcde2 100644 --- a/app/src/main/res/drawable/selector_focused_dark.xml +++ b/app/src/main/res/drawable/selector_focused_dark.xml @@ -1,5 +1,5 @@ - - - \ No newline at end of file + + + diff --git a/app/src/main/res/drawable/selector_focused_light.xml b/app/src/main/res/drawable/selector_focused_light.xml index 102f40d76ee..508083fcde2 100644 --- a/app/src/main/res/drawable/selector_focused_light.xml +++ b/app/src/main/res/drawable/selector_focused_light.xml @@ -1,5 +1,5 @@ - - - \ No newline at end of file + + + diff --git a/app/src/main/res/drawable/selector_light.xml b/app/src/main/res/drawable/selector_light.xml index 63f2ccaf36d..0c79be29279 100644 --- a/app/src/main/res/drawable/selector_light.xml +++ b/app/src/main/res/drawable/selector_light.xml @@ -1,5 +1,5 @@ - - - \ No newline at end of file + + + diff --git a/app/src/main/res/drawable/splash_background.xml b/app/src/main/res/drawable/splash_background.xml index 5b805cffaf6..61c3d058ff7 100644 --- a/app/src/main/res/drawable/splash_background.xml +++ b/app/src/main/res/drawable/splash_background.xml @@ -1,7 +1,6 @@ - + - \ No newline at end of file + diff --git a/app/src/main/res/drawable/splash_foreground.xml b/app/src/main/res/drawable/splash_foreground.xml index cfb65075840..63fd0351f28 100644 --- a/app/src/main/res/drawable/splash_foreground.xml +++ b/app/src/main/res/drawable/splash_foreground.xml @@ -3,8 +3,8 @@ android:height="100dp" android:viewportWidth="100" android:viewportHeight="100"> - + diff --git a/app/src/main/res/drawable/tab_selector.xml b/app/src/main/res/drawable/tab_selector.xml index dc472133f06..35581f25d42 100644 --- a/app/src/main/res/drawable/tab_selector.xml +++ b/app/src/main/res/drawable/tab_selector.xml @@ -1,8 +1,7 @@ - + - - \ No newline at end of file + + diff --git a/app/src/main/res/drawable/toolbar_shadow_dark.xml b/app/src/main/res/drawable/toolbar_shadow_dark.xml index d5ebfc8fd82..b9419029f23 100644 --- a/app/src/main/res/drawable/toolbar_shadow_dark.xml +++ b/app/src/main/res/drawable/toolbar_shadow_dark.xml @@ -3,5 +3,5 @@ + android:startColor="@color/dark_shadow_start_color" /> diff --git a/app/src/main/res/drawable/toolbar_shadow_light.xml b/app/src/main/res/drawable/toolbar_shadow_light.xml index 7b800786c7c..6546bdd2039 100644 --- a/app/src/main/res/drawable/toolbar_shadow_light.xml +++ b/app/src/main/res/drawable/toolbar_shadow_light.xml @@ -3,5 +3,5 @@ + android:startColor="@color/light_shadow_start_color" /> diff --git a/app/src/main/res/layout-land/activity_player_queue_control.xml b/app/src/main/res/layout-land/activity_player_queue_control.xml index a7872a83a82..2adea98683c 100644 --- a/app/src/main/res/layout-land/activity_player_queue_control.xml +++ b/app/src/main/res/layout-land/activity_player_queue_control.xml @@ -1,7 +1,7 @@ + app:title="@string/app_name" /> @@ -30,33 +30,33 @@ android:id="@+id/play_queue" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_below="@+id/appbar" android:layout_above="@id/progress_bar" - android:layout_toLeftOf="@+id/control_pane" + android:layout_below="@+id/appbar" android:layout_toStartOf="@+id/control_pane" + android:layout_toLeftOf="@+id/control_pane" android:scrollbars="vertical" app:layoutManager="LinearLayoutManager" - tools:listitem="@layout/play_queue_item"/> + tools:listitem="@layout/play_queue_item" /> + android:layout_below="@id/appbar" + android:layout_alignParentEnd="true" + android:layout_alignParentRight="true"> + tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis nec aliquam augue, eget cursus est. Ut id tristique enim, ut scelerisque tellus. Sed ultricies ipsum non mauris ultricies, commodo malesuada velit porta." /> + tools:visibility="visible" /> + android:tint="?attr/colorAccent" + tools:ignore="ContentDescription" /> + tools:ignore="ContentDescription" /> + android:padding="2dp" + android:scaleType="fitCenter" + android:tint="?attr/colorAccent" + android:visibility="invisible" /> + android:tint="?attr/colorAccent" + tools:ignore="ContentDescription" /> + tools:ignore="ContentDescription" /> - + android:layout_centerInParent="true" /> + tools:ignore="ContentDescription" /> @@ -263,10 +263,10 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" + android:background="@drawable/player_controls_background" android:gravity="center" android:orientation="horizontal" android:paddingLeft="16dp" - android:background="@drawable/player_controls_background" android:paddingRight="16dp"> + tools:text="1:06:29" /> + tools:secondaryProgress="50" /> + tools:text="1:23:49" /> + android:visibility="gone" /> - \ No newline at end of file + diff --git a/app/src/main/res/layout-large-land/fragment_video_detail.xml b/app/src/main/res/layout-large-land/fragment_video_detail.xml index 9c0f03cb3bc..13e3c1b5fac 100644 --- a/app/src/main/res/layout-large-land/fragment_video_detail.xml +++ b/app/src/main/res/layout-large-land/fragment_video_detail.xml @@ -2,696 +2,695 @@ - - - - + android:background="?attr/windowBackground"> - + + + - + android:background="@android:color/transparent" + android:touchscreenBlocksFocus="false" + app:elevation="0dp" + app:layout_behavior="com.google.android.material.appbar.FlingBehavior"> - - - - - - + app:layout_scrollFlags="scroll"> - + + android:background="@android:color/black" + android:clickable="true" + android:descendantFocusability="afterDescendants" + android:focusable="true" + android:foreground="?attr/selectableItemBackground" + app:layout_collapseMode="parallax"> - + + + - + - + - - + - + + + + - + - - + - - - - - - - - - - - + - - - - - - - - - + + + - - - + + + android:layout_marginEnd="30dp" + android:ellipsize="end" + android:maxLines="1" + android:paddingTop="12dp" + android:paddingBottom="8dp" + android:textAppearance="?android:attr/textAppearanceLarge" + android:textSize="@dimen/video_item_detail_title_text_size" + tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed a ultricies ex. Integer sit amet sodales risus. Duis non mi et urna pretium bibendum. Nunc eleifend est quis ipsum porttitor egestas. Sed facilisis, nisl quis eleifend pellentesque, orci metus egestas dolor, at accumsan eros metus quis libero." /> + + + + + + + - - - - - + + + + + - + + + + android:orientation="horizontal" + android:padding="6dp"> + + + + + + + + + + + + + + + + + + + + + + tools:text="2,816,821,505 views" /> + + + + + + + + - - - - + tools:visibility="visible" /> + + - - + + - - + android:id="@+id/detail_controls_playlist_append" + android:layout_width="80dp" + android:layout_height="55dp" + android:layout_gravity="center_vertical" + android:layout_weight="1" + android:background="?attr/selectableItemBackgroundBorderless" + android:clickable="true" + android:contentDescription="@string/append_playlist" + android:focusable="true" + android:gravity="center" + android:paddingTop="6dp" + android:paddingBottom="6dp" + android:text="@string/controls_add_to_playlist_title" + android:textSize="12sp" + app:drawableTopCompat="?attr/ic_playlist_add" /> - - + android:id="@+id/detail_controls_background" + android:layout_width="80dp" + android:layout_height="55dp" + android:layout_gravity="center_vertical" + android:layout_weight="1" + android:background="?attr/selectableItemBackgroundBorderless" + android:clickable="true" + android:contentDescription="@string/play_audio" + android:focusable="true" + android:gravity="center" + android:paddingTop="6dp" + android:paddingBottom="6dp" + android:text="@string/controls_background_title" + android:textSize="12sp" + app:drawableTopCompat="?attr/ic_headset" /> + android:id="@+id/detail_controls_popup" + android:layout_width="80dp" + android:layout_height="55dp" + android:layout_gravity="center_vertical" + android:layout_weight="1" + android:background="?attr/selectableItemBackgroundBorderless" + android:clickable="true" + android:contentDescription="@string/open_in_popup_mode" + android:focusable="true" + android:gravity="center" + android:paddingTop="6dp" + android:paddingBottom="6dp" + android:text="@string/controls_popup_title" + android:textSize="12sp" + app:drawableTopCompat="?attr/ic_popup" /> - - - - + android:id="@+id/detail_controls_download" + android:layout_width="80dp" + android:layout_height="55dp" + android:layout_gravity="center_vertical" + android:layout_weight="1" + android:background="?attr/selectableItemBackgroundBorderless" + android:clickable="true" + android:contentDescription="@string/controls_download_desc" + android:focusable="true" + android:gravity="center" + android:paddingTop="6dp" + android:paddingBottom="6dp" + android:text="@string/download" + android:textSize="12sp" + app:drawableTopCompat="?attr/ic_file_download" /> - - + - + - + + android:orientation="vertical" + android:visibility="gone" + tools:visibility="visible"> - + - + - + - - + - + + - + - + - + - - + - + - + android:layout_marginTop="10dp" + android:layout_weight="3"> - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + android:alpha="0.9" + android:background="?attr/windowBackground" + android:descendantFocusability="blocksDescendants"> + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout-large-land/player.xml b/app/src/main/res/layout-large-land/player.xml index 23481f40be3..145d1e2f891 100644 --- a/app/src/main/res/layout-large-land/player.xml +++ b/app/src/main/res/layout-large-land/player.xml @@ -1,8 +1,7 @@ - + android:layout_centerInParent="true" /> + android:layout_alignBottom="@+id/surfaceView" + android:background="@android:color/black" /> + android:layout_gravity="center" /> + android:background="@drawable/player_controls_top_background" + android:visibility="gone" /> + android:background="@drawable/player_controls_background" + android:visibility="gone" /> + tools:visibility="visible" /> @@ -74,256 +73,256 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" - android:orientation="vertical" - android:gravity="top" - android:descendantFocusability="afterDescendants" - android:paddingTop="@dimen/player_main_top_padding" - android:paddingStart="@dimen/player_main_controls_padding" - android:paddingEnd="@dimen/player_main_controls_padding" - android:baselineAligned="false"> - - - - + android:orientation="vertical" + android:paddingStart="@dimen/player_main_controls_padding" + android:paddingTop="@dimen/player_main_top_padding" + android:paddingEnd="@dimen/player_main_controls_padding"> + android:minHeight="45dp" + tools:ignore="RtlHardcoded"> - + + + + + + + + +