diff --git a/app/build.gradle b/app/build.gradle index 2fe94d576..7025d46ad 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,9 +11,10 @@ repositories { dependencies { compile "com.android.support:support-v4:$supportVersion" compile "com.android.support:appcompat-v7:$supportVersion" - compile "com.android.support:gridlayout-v7:$supportVersion" - compile "com.android.support:cardview-v7:$supportVersion" compile "com.android.support:design:$supportVersion" + compile "com.android.support:gridlayout-v7:$supportVersion" + compile "com.android.support:palette-v7:$supportVersion" + compile "com.android.support:percent:$supportVersion" compile "com.android.support:recyclerview-v7:$supportVersion" compile "org.apache.commons:commons-lang3:$commonslangVersion" compile("org.shredzone.flattr4j:flattr4j-core:$flattr4jVersion") { @@ -37,6 +38,9 @@ dependencies { transitive = true } compile "com.yqritc:recyclerview-flexibledivider:$recyclerviewFlexibledividerVersion" + compile("com.githang:viewpagerindicator:2.5@aar") { + exclude module: "support-v4" + } compile "com.github.AntennaPod:AntennaPod-AudioPlayer:$audioPlayerVersion" diff --git a/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java b/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java index fabc399ba..897098c00 100644 --- a/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java +++ b/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java @@ -3,17 +3,10 @@ package de.test.antennapod.ui; import android.content.Context; import android.content.res.Resources; import android.test.ActivityInstrumentationTestCase2; -import android.test.FlakyTest; -import com.robotium.solo.Condition; import com.robotium.solo.Solo; import com.robotium.solo.Timeout; -import org.apache.commons.io.IOUtils; - -import java.io.IOException; -import java.io.InputStream; -import java.util.List; import java.util.concurrent.TimeUnit; import de.danoeh.antennapod.R; diff --git a/app/src/main/assets/LICENSE_ANTENNAPOD_AUDIOPLAYER.txt b/app/src/main/assets/LICENSE_ANTENNAPOD_AUDIOPLAYER.txt deleted file mode 100644 index 8dada3eda..000000000 --- a/app/src/main/assets/LICENSE_ANTENNAPOD_AUDIOPLAYER.txt +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/app/src/main/assets/LICENSE_APACHE_COMMONS.txt b/app/src/main/assets/LICENSE_APACHE-2.0.txt similarity index 100% rename from app/src/main/assets/LICENSE_APACHE_COMMONS.txt rename to app/src/main/assets/LICENSE_APACHE-2.0.txt diff --git a/app/src/main/assets/LICENSE_EVENTBUS.txt b/app/src/main/assets/LICENSE_EVENTBUS.txt deleted file mode 100644 index d64569567..000000000 --- a/app/src/main/assets/LICENSE_EVENTBUS.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/app/src/main/assets/LICENSE_FLATTR4J.txt b/app/src/main/assets/LICENSE_FLATTR4J.txt deleted file mode 100644 index d64569567..000000000 --- a/app/src/main/assets/LICENSE_FLATTR4J.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/app/src/main/assets/LICENSE_FLEXIBLE_DIVIDER.txt b/app/src/main/assets/LICENSE_FLEXIBLE_DIVIDER.txt deleted file mode 100644 index 5c304d1a4..000000000 --- a/app/src/main/assets/LICENSE_FLEXIBLE_DIVIDER.txt +++ /dev/null @@ -1,201 +0,0 @@ -Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/app/src/main/assets/LICENSE_OKIO.txt b/app/src/main/assets/LICENSE_OKIO.txt deleted file mode 100644 index d64569567..000000000 --- a/app/src/main/assets/LICENSE_OKIO.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/app/src/main/assets/LICENSE_RX_ANDROID.txt b/app/src/main/assets/LICENSE_RX_ANDROID.txt deleted file mode 100644 index 4fe2d187f..000000000 --- a/app/src/main/assets/LICENSE_RX_ANDROID.txt +++ /dev/null @@ -1,200 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/app/src/main/assets/LICENSE_STACKBLUR.txt b/app/src/main/assets/LICENSE_STACKBLUR.txt deleted file mode 100644 index b66736ccb..000000000 --- a/app/src/main/assets/LICENSE_STACKBLUR.txt +++ /dev/null @@ -1,71 +0,0 @@ -Apache License, Version 2.0 - -Version 2.0, January 2004 - -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. - -"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of this License; and -You must cause any modified files to carry prominent notices stating that You changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. - -You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. -5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work -To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. - - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java index 442515010..014c65e11 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java @@ -1,54 +1,43 @@ package de.danoeh.antennapod.activity; +import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; -import android.content.res.TypedArray; -import android.graphics.drawable.BitmapDrawable; -import android.os.Bundle; +import android.os.Build; +import android.support.design.widget.AppBarLayout; import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentTransaction; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentStatePagerAdapter; import android.support.v4.app.ListFragment; +import android.support.v4.view.ViewPager; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.widget.Toolbar; import android.text.TextUtils; import android.util.Log; +import android.util.TypedValue; import android.view.ContextMenu; -import android.view.Gravity; -import android.view.LayoutInflater; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; -import android.view.ViewGroup; -import android.view.animation.LinearInterpolator; -import android.view.animation.ScaleAnimation; import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.ImageButton; import android.widget.ListView; -import android.widget.PopupWindow; -import android.widget.SeekBar; -import android.widget.TextView; -import com.bumptech.glide.Glide; +import com.viewpagerindicator.CirclePageIndicator; -import org.apache.commons.lang3.ArrayUtils; +import java.util.List; import de.danoeh.antennapod.R; -import de.danoeh.antennapod.adapter.ChapterListAdapter; +import de.danoeh.antennapod.adapter.ChaptersListAdapter; import de.danoeh.antennapod.adapter.NavListAdapter; import de.danoeh.antennapod.core.asynctask.FeedRemover; import de.danoeh.antennapod.core.dialog.ConfirmationDialog; -import de.danoeh.antennapod.core.feed.Chapter; import de.danoeh.antennapod.core.feed.EventDistributor; import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.MediaType; -import de.danoeh.antennapod.core.feed.SimpleChapter; -import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.playback.PlaybackService; import de.danoeh.antennapod.core.service.playback.PlayerStatus; @@ -57,8 +46,14 @@ import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.util.playback.ExternalMedia; import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.PlaybackController; +import de.danoeh.antennapod.fragment.AddFeedFragment; +import de.danoeh.antennapod.fragment.ChaptersFragment; import de.danoeh.antennapod.fragment.CoverFragment; +import de.danoeh.antennapod.fragment.DownloadsFragment; +import de.danoeh.antennapod.fragment.EpisodesFragment; import de.danoeh.antennapod.fragment.ItemDescriptionFragment; +import de.danoeh.antennapod.fragment.PlaybackHistoryFragment; +import de.danoeh.antennapod.fragment.QueueFragment; import de.danoeh.antennapod.menuhandler.NavDrawerActivity; import de.danoeh.antennapod.preferences.PreferenceController; import rx.Observable; @@ -69,85 +64,38 @@ import rx.schedulers.Schedulers; /** * Activity for playing audio files. */ -public class AudioplayerActivity extends MediaplayerActivity implements ItemDescriptionFragment.ItemDescriptionFragmentCallback, - NavDrawerActivity { +public class AudioplayerActivity extends MediaplayerActivity implements NavDrawerActivity { private static final int POS_COVER = 0; private static final int POS_DESCR = 1; private static final int POS_CHAPTERS = 2; private static final int NUM_CONTENT_FRAGMENTS = 3; - private static final int POS_NONE = -1; final String TAG = "AudioplayerActivity"; private static final String PREFS = "AudioPlayerActivityPreferences"; private static final String PREF_KEY_SELECTED_FRAGMENT_POSITION = "selectedFragmentPosition"; - private static final String PREF_PLAYABLE_ID = "playableId"; + + public static final String[] NAV_DRAWER_TAGS = { + QueueFragment.TAG, + EpisodesFragment.TAG, + DownloadsFragment.TAG, + PlaybackHistoryFragment.TAG, + AddFeedFragment.TAG + }; private DrawerLayout drawerLayout; private NavListAdapter navAdapter; private ListView navList; - private AdapterView.AdapterContextMenuInfo lastMenuInfo = null; private View navDrawer; private ActionBarDrawerToggle drawerToggle; + private int mPosition = -1; - private Fragment[] detachedFragments; - - private CoverFragment coverFragment; - private ItemDescriptionFragment descriptionFragment; - private ListFragment chapterFragment; - - private Fragment currentlyShownFragment; - private int currentlyShownPosition = -1; - private int lastShownPosition = POS_NONE; - /** - * Used if onResume was called without loadMediaInfo. - */ - private int savedPosition = -1; - - private TextView txtvTitle; - private Button butPlaybackSpeed; - private ImageButton butNavChaptersShownotes; - private ImageButton butShowCover; + private Playable media; + private ViewPager mPager; + private AudioplayerPagerAdapter mPagerAdapter; private Subscription subscription; - private PopupWindow popupWindow; - - private void resetFragmentView() { - FragmentTransaction fT = getSupportFragmentManager().beginTransaction(); - - if (coverFragment != null) { - Log.d(TAG, "Removing cover fragment"); - fT.remove(coverFragment); - } - if (descriptionFragment != null) { - Log.d(TAG, "Removing description fragment"); - fT.remove(descriptionFragment); - } - if (chapterFragment != null) { - Log.d(TAG, "Removing chapter fragment"); - fT.remove(chapterFragment); - } - if (currentlyShownFragment != null) { - Log.d(TAG, "Removing currently shown fragment"); - fT.remove(currentlyShownFragment); - } - for (int i = 0; i < detachedFragments.length; i++) { - Fragment f = detachedFragments[i]; - if (f != null) { - Log.d(TAG, "Removing detached fragment"); - fT.remove(f); - } - } - fT.commit(); - currentlyShownFragment = null; - coverFragment = null; - descriptionFragment = null; - chapterFragment = null; - currentlyShownPosition = -1; - detachedFragments = new Fragment[NUM_CONTENT_FRAGMENTS]; - } - @Override protected void onStop() { super.onStop(); @@ -156,6 +104,7 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc subscription.unsubscribe(); } EventDistributor.getInstance().unregister(contentUpdate); + saveCurrentFragment(); } @Override @@ -163,29 +112,16 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc setTheme(UserPreferences.getNoTitleTheme()); } - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - detachedFragments = new Fragment[NUM_CONTENT_FRAGMENTS]; - } + private void saveCurrentFragment() { + if(mPager == null) { + return; + } - private void savePreferences() { Log.d(TAG, "Saving preferences"); SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE); - SharedPreferences.Editor editor = prefs.edit(); - if (currentlyShownPosition >= 0 && controller != null - && controller.getMedia() != null) { - editor.putInt(PREF_KEY_SELECTED_FRAGMENT_POSITION, - currentlyShownPosition); - editor.putString(PREF_PLAYABLE_ID, controller.getMedia() - .getIdentifier().toString()); - } else { - editor.putInt(PREF_KEY_SELECTED_FRAGMENT_POSITION, -1); - editor.putString(PREF_PLAYABLE_ID, ""); - } - editor.commit(); - - savedPosition = currentlyShownPosition; + prefs.edit() + .putInt(PREF_KEY_SELECTED_FRAGMENT_POSITION, mPager.getCurrentItem()) + .commit(); } @Override @@ -194,53 +130,11 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc drawerToggle.onConfigurationChanged(newConfig); } - @Override - protected void onSaveInstanceState(Bundle outState) { - // super.onSaveInstanceState(outState); would cause crash - Log.d(TAG, "onSaveInstanceState"); - } - - @Override - protected void onPause() { - savePreferences(); - resetFragmentView(); - super.onPause(); - } - - @Override - protected void onRestoreInstanceState(Bundle savedInstanceState) { - super.onRestoreInstanceState(savedInstanceState); - restoreFromPreferences(); - } - - /** - * Tries to restore the selected fragment position from the Activity's - * preferences. - * - * @return true if restoreFromPrefernces changed the activity's state - */ - private boolean restoreFromPreferences() { + private void loadLastFragment() { Log.d(TAG, "Restoring instance state"); SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE); - int savedPosition = prefs.getInt(PREF_KEY_SELECTED_FRAGMENT_POSITION, - -1); - String playableId = prefs.getString(PREF_PLAYABLE_ID, ""); - - if (savedPosition != -1 - && controller != null - && controller.getMedia() != null - && controller.getMedia().getIdentifier().toString() - .equals(playableId)) { - switchToFragment(savedPosition); - return true; - } else if (controller == null || controller.getMedia() == null) { - Log.d(TAG, "Couldn't restore from preferences: controller or media was null"); - } else { - Log.d(TAG, "Couldn't restore from preferences: savedPosition was -1 or saved identifier and playable identifier didn't match.\nsavedPosition: " - + savedPosition + ", id: " + playableId); - - } - return false; + int lastPosition = prefs.getInt(PREF_KEY_SELECTED_FRAGMENT_POSITION, -1); + mPager.setCurrentItem(lastPosition); } @Override @@ -260,10 +154,12 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc true); startService(launchIntent); } - if (savedPosition != -1) { - switchToFragment(savedPosition); + if(mPagerAdapter != null && controller != null && controller.getMedia() != media) { + media = controller.getMedia(); + mPagerAdapter.notifyDataSetChanged(); } + EventDistributor.getInstance().register(contentUpdate); loadData(); } @@ -292,147 +188,25 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc @Override protected void clearStatusMsg() { // TODO Hide progress bar here - } - /** - * Changes the currently displayed fragment. - * - * @param pos Must be POS_COVER, POS_DESCR, or POS_CHAPTERS - */ - private void switchToFragment(int pos) { - Log.d(TAG, "Switching contentView to position " + pos); - if (currentlyShownPosition != pos && controller != null) { - Playable media = controller.getMedia(); - if (media != null) { - FragmentTransaction ft = getSupportFragmentManager() - .beginTransaction(); - if (currentlyShownFragment != null) { - detachedFragments[currentlyShownPosition] = currentlyShownFragment; - ft.detach(currentlyShownFragment); - } - switch (pos) { - case POS_COVER: - if (coverFragment == null) { - Log.i(TAG, "Using new coverfragment"); - coverFragment = CoverFragment.newInstance(media); - } - currentlyShownFragment = coverFragment; - break; - case POS_DESCR: - if (descriptionFragment == null) { - descriptionFragment = ItemDescriptionFragment - .newInstance(media, true, true); - } - currentlyShownFragment = descriptionFragment; - break; - case POS_CHAPTERS: - if (chapterFragment == null) { - chapterFragment = new ListFragment() { - - @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - // add padding - final ListView lv = getListView(); - lv.setClipToPadding(false); - final int vertPadding = getResources().getDimensionPixelSize(R.dimen.list_vertical_padding); - lv.setPadding(0, vertPadding, 0, vertPadding); - } - }; - chapterFragment.setListAdapter(new ChapterListAdapter( - AudioplayerActivity.this, 0, media - .getChapters(), media, position -> { - Chapter chapter = (Chapter) - chapterFragment.getListAdapter().getItem(position); - controller.seekToChapter(chapter); - } - )); - } - currentlyShownFragment = chapterFragment; - break; - } - if (currentlyShownFragment != null) { - lastShownPosition = currentlyShownPosition; - currentlyShownPosition = pos; - if (detachedFragments[pos] != null) { - Log.d(TAG, "Reattaching fragment at position " + pos); - ft.attach(detachedFragments[pos]); - } else { - ft.add(R.id.contentView, currentlyShownFragment); - } - ft.disallowAddToBackStack(); - ft.commit(); - updateNavButtonDrawable(); - } - } - } - } - - /** - * Switches to the fragment that was displayed before the current one or the description fragment - * if no fragment was previously displayed. - */ - public void switchToLastFragment() { - if (lastShownPosition != POS_NONE) { - switchToFragment(lastShownPosition); - } else { - switchToFragment(POS_DESCR); - } - } - - private void updateNavButtonDrawable() { - - final int[] buttonTexts = new int[]{R.string.show_shownotes_label, - R.string.show_chapters_label}; - - final TypedArray drawables = obtainStyledAttributes(new int[]{ - R.attr.navigation_shownotes, R.attr.navigation_chapters}); - final Playable media = controller.getMedia(); - if (butNavChaptersShownotes != null && butShowCover != null && media != null) { - - butNavChaptersShownotes.setTag(R.id.imageloader_key, null); - setNavButtonVisibility(); - switch (currentlyShownPosition) { - case POS_COVER: - butShowCover.setVisibility(View.GONE); - if (lastShownPosition == POS_CHAPTERS) { - butNavChaptersShownotes.setImageDrawable(drawables.getDrawable(1)); - butNavChaptersShownotes.setContentDescription(getString(buttonTexts[1])); - } else { - butNavChaptersShownotes.setImageDrawable(drawables.getDrawable(0)); - butNavChaptersShownotes.setContentDescription(getString(buttonTexts[0])); - } - break; - case POS_DESCR: - butShowCover.setVisibility(View.VISIBLE); - butNavChaptersShownotes.setImageDrawable(drawables.getDrawable(1)); - butNavChaptersShownotes.setContentDescription(getString(buttonTexts[1])); - break; - case POS_CHAPTERS: - butShowCover.setVisibility(View.VISIBLE); - butNavChaptersShownotes.setImageDrawable(drawables.getDrawable(0)); - butNavChaptersShownotes.setContentDescription(getString(buttonTexts[0])); - break; - } - } - drawables.recycle(); - } @Override protected void setupGUI() { super.setupGUI(); - resetFragmentView(); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setTitle(""); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + findViewById(R.id.shadow).setVisibility(View.GONE); + AppBarLayout appBarLayout = (AppBarLayout) findViewById(R.id.appBar); + float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, getResources().getDisplayMetrics()); + appBarLayout.setElevation(px); + } drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); navList = (ListView) findViewById(R.id.nav_list); navDrawer = findViewById(R.id.nav_layout); - butPlaybackSpeed = (Button) findViewById(R.id.butPlaybackSpeed); - butNavChaptersShownotes = (ImageButton) findViewById(R.id.butNavChaptersShownotes); - butShowCover = (ImageButton) findViewById(R.id.butCover); - txtvTitle = (TextView) findViewById(R.id.txtvTitle); drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.string.drawer_open, R.string.drawer_close); drawerToggle.setDrawerIndicatorEnabled(false); @@ -450,6 +224,15 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc } drawerLayout.closeDrawer(navDrawer); }); + navList.setOnItemLongClickListener((parent, view, position, id) -> { + if (position < navAdapter.getTags().size()) { + showDrawerPreferencesDialog(); + return true; + } else { + mPosition = position; + return false; + } + }); registerForContextMenu(navList); drawerToggle.syncState(); @@ -458,128 +241,15 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc startActivity(new Intent(AudioplayerActivity.this, PreferenceController.getPreferenceActivity())); }); - butNavChaptersShownotes.setOnClickListener(v -> { - if (currentlyShownPosition == POS_CHAPTERS) { - switchToFragment(POS_DESCR); - } else if (currentlyShownPosition == POS_DESCR) { - switchToFragment(POS_CHAPTERS); - } else if (currentlyShownPosition == POS_COVER) { - switchToLastFragment(); - } - }); - - butShowCover.setOnClickListener(v -> switchToFragment(POS_COVER)); - - butPlaybackSpeed.setOnClickListener(v -> { - if (controller != null && controller.canSetPlaybackSpeed()) { - String[] availableSpeeds = UserPreferences - .getPlaybackSpeedArray(); - String currentSpeed = UserPreferences.getPlaybackSpeed(); - - // Provide initial value in case the speed list has changed - // out from under us - // and our current speed isn't in the new list - String newSpeed; - if (availableSpeeds.length > 0) { - newSpeed = availableSpeeds[0]; - } else { - newSpeed = "1.0"; - } - - for (int i = 0; i < availableSpeeds.length; i++) { - if (availableSpeeds[i].equals(currentSpeed)) { - if (i == availableSpeeds.length - 1) { - newSpeed = availableSpeeds[0]; - } else { - newSpeed = availableSpeeds[i + 1]; - } - break; - } - } - UserPreferences.setPlaybackSpeed(newSpeed); - controller.setPlaybackSpeed(Float.parseFloat(newSpeed)); - } - }); - - butPlaybackSpeed.setOnLongClickListener(v -> { - - String[] availableSpeeds = getResources().getStringArray(R.array.playback_speed_values); - String currentSpeed = UserPreferences.getPlaybackSpeed(); - - LayoutInflater inflater = getLayoutInflater(); - View popupView = inflater.inflate(R.layout.choose_speed_dialog, null); - TextView txtvSelectedSpeed = (TextView) popupView.findViewById(R.id.txtvSelectedSpeed); - SeekBar sbSelectSpeed = (SeekBar) popupView.findViewById(R.id.sbSelectSpeed); - - txtvSelectedSpeed.setText(currentSpeed); - int progress = ArrayUtils.indexOf(availableSpeeds, currentSpeed); - int max = Math.max(progress, ArrayUtils.indexOf(availableSpeeds, "2.50")); - sbSelectSpeed.setMax(max); - sbSelectSpeed.setProgress(progress); - sbSelectSpeed.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - txtvSelectedSpeed.setText(availableSpeeds[progress]); - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - String selectedSpeed = availableSpeeds[sbSelectSpeed.getProgress()]; - UserPreferences.setPlaybackSpeed(selectedSpeed); - controller.setPlaybackSpeed(Float.parseFloat(selectedSpeed)); - if (popupWindow != null && popupWindow.isShowing()) { - popupWindow.dismiss(); - } - ScaleAnimation anim = new ScaleAnimation(1.0f, 1.33f, 1.0f, 1.33f, - butPlaybackSpeed.getWidth()/2, butPlaybackSpeed.getHeight()/2); - anim.setDuration(150); - anim.setRepeatMode(ScaleAnimation.REVERSE); - anim.setRepeatCount(1); - anim.setInterpolator(new LinearInterpolator()); - butPlaybackSpeed.startAnimation(anim); - } - }); - popupWindow = new PopupWindow(popupView, - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT, - true); - popupWindow.setBackgroundDrawable(new BitmapDrawable()); - popupWindow.setOutsideTouchable(true); - popupWindow.showAtLocation(popupView, Gravity.CENTER, 0, 0); - return true; - }); - } - - private void setNavButtonVisibility() { - if (butNavChaptersShownotes != null) { - if (controller != null) { - Playable media = controller.getMedia(); - if (media != null) { - if (media.getChapters() != null || currentlyShownPosition == POS_COVER) { - butNavChaptersShownotes.setVisibility(View.VISIBLE); - return; - } - } - } - butNavChaptersShownotes.setVisibility(View.GONE); - } - - } - - @Override - protected void onPlaybackSpeedChange() { - super.onPlaybackSpeedChange(); - updateButPlaybackSpeed(); - } - - private void updateButPlaybackSpeed() { - if (controller != null && controller.canSetPlaybackSpeed()) { - butPlaybackSpeed.setText(UserPreferences.getPlaybackSpeed()); + mPager = (ViewPager) findViewById(R.id.pager); + if(mPager.getAdapter() == null) { + mPagerAdapter = new AudioplayerPagerAdapter(getSupportFragmentManager()); + mPager.setAdapter(mPagerAdapter); + CirclePageIndicator pageIndicator = (CirclePageIndicator) findViewById(R.id.page_indicator); + pageIndicator.setViewPager(mPager); + loadLastFragment(); } + mPager.onSaveInstanceState(); } @Override @@ -593,48 +263,17 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc if (!super.loadMediaInfo()) { return false; } - final Playable media = controller.getMedia(); - if (media == null) { - return false; + if(controller.getMedia() != media) { + media = controller.getMedia(); + mPagerAdapter.notifyDataSetChanged(); } - txtvTitle.setText(media.getEpisodeTitle()); - getSupportActionBar().setTitle(""); - Glide.with(this) - .load(media.getImageUri()) - .placeholder(R.color.light_gray) - .error(R.color.light_gray) - .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) - .fitCenter() - .dontAnimate() - .into(butShowCover); - - setNavButtonVisibility(); - - if (currentlyShownPosition == -1) { - if (!restoreFromPreferences()) { - switchToFragment(POS_COVER); - } - } - if (currentlyShownFragment instanceof AudioplayerContentFragment) { - ((AudioplayerContentFragment) currentlyShownFragment) - .onDataSetChanged(media); - } - - if (controller == null - || !controller.canSetPlaybackSpeed()) { - butPlaybackSpeed.setVisibility(View.GONE); - } else { - butPlaybackSpeed.setVisibility(View.VISIBLE); - } - - updateButPlaybackSpeed(); return true; } public void notifyMediaPositionChanged() { - if (chapterFragment != null) { - ArrayAdapter adapter = (ArrayAdapter) chapterFragment - .getListAdapter(); + ListFragment chapterFragment = (ListFragment) mPagerAdapter.getItem(POS_CHAPTERS); + ChaptersListAdapter adapter = (ChaptersListAdapter) chapterFragment.getListAdapter(); + if(adapter != null) { adapter.notifyDataSetChanged(); } } @@ -659,7 +298,6 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc clearStatusMsg(); } - @Override public PlaybackController getPlaybackController() { return controller; } @@ -669,10 +307,6 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc return drawerLayout != null && navDrawer != null && drawerLayout.isDrawerOpen(navDrawer); } - public interface AudioplayerContentFragment { - void onDataSetChanged(Playable media); - } - @Override protected int getContentViewResourceId() { return R.layout.audioplayer_activity; @@ -704,24 +338,15 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset()); menu.setHeaderTitle(feed.getTitle()); // episodes are not loaded, so we cannot check if the podcast has new or unplayed ones! - - // we may need to reference this elsewhere... - lastMenuInfo = (AdapterView.AdapterContextMenuInfo) menuInfo; } @Override public boolean onContextItemSelected(MenuItem item) { - AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); - - if(menuInfo == null) { - menuInfo = lastMenuInfo; - } - - if(menuInfo.targetView.getParent() instanceof ListView == false - || ((ListView)menuInfo.targetView.getParent()).getId() != R.id.nav_list) { + final int position = mPosition; + mPosition = -1; // reset + if(position < 0) { return false; } - int position = menuInfo.position; Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset()); switch(item.getItemId()) { case R.id.mark_all_seen_item: @@ -772,11 +397,43 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc public void onBackPressed() { if(isDrawerOpen()) { drawerLayout.closeDrawer(navDrawer); - } else { + } else if (mPager.getCurrentItem() == 0) { + // If the user is currently looking at the first step, allow the system to handle the + // Back button. This calls finish() on this activity and pops the back stack. super.onBackPressed(); + } else { + // Otherwise, select the previous step. + mPager.setCurrentItem(mPager.getCurrentItem() - 1); } } + public void showDrawerPreferencesDialog() { + final List hiddenDrawerItems = UserPreferences.getHiddenDrawerItems(); + String[] navLabels = new String[NAV_DRAWER_TAGS.length]; + final boolean[] checked = new boolean[NAV_DRAWER_TAGS.length]; + for (int i = 0; i < NAV_DRAWER_TAGS.length; i++) { + String tag = NAV_DRAWER_TAGS[i]; + navLabels[i] = navAdapter.getLabel(tag); + if (!hiddenDrawerItems.contains(tag)) { + checked[i] = true; + } + } + + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.drawer_preferences); + builder.setMultiChoiceItems(navLabels, checked, (dialog, which, isChecked) -> { + if (isChecked) { + hiddenDrawerItems.remove(NAV_DRAWER_TAGS[which]); + } else { + hiddenDrawerItems.add(NAV_DRAWER_TAGS[which]); + } + }); + builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> { + UserPreferences.setHiddenDrawerItems(hiddenDrawerItems); + }); + builder.setNegativeButton(R.string.cancel_label, null); + builder.create().show(); + } private DBReader.NavDrawerData navDrawerData; @@ -847,4 +504,36 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc } }; + public interface AudioplayerContentFragment { + void onDataSetChanged(Playable media); + } + + private class AudioplayerPagerAdapter extends FragmentStatePagerAdapter { + + public AudioplayerPagerAdapter(FragmentManager fm) { + super(fm); + } + + @Override + public Fragment getItem(int position) { + Log.d(TAG, "getItem(" + position + ")"); + switch (position) { + case POS_COVER: + return CoverFragment.newInstance(media); + case POS_DESCR: + return ItemDescriptionFragment.newInstance(media, true, true); + case POS_CHAPTERS: + return ChaptersFragment.newInstance(media, controller); + default: + return null; + } + } + + + @Override + public int getCount() { + return NUM_CONTENT_FRAGMENTS; + } + } + } diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java index bfbf3d86f..75100e717 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java @@ -1,34 +1,44 @@ package de.danoeh.antennapod.activity; import android.annotation.TargetApi; -import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; +import android.content.res.TypedArray; +import android.graphics.Color; import android.graphics.PixelFormat; import android.media.AudioManager; import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.support.v7.app.ActionBarActivity; +import android.support.v4.view.ViewCompat; import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; +import android.widget.Button; +import android.widget.CheckBox; import android.widget.ImageButton; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; +import android.widget.Toast; import com.afollestad.materialdialogs.MaterialDialog; import com.bumptech.glide.Glide; +import com.joanzapata.iconify.IconDrawable; +import com.joanzapata.iconify.fonts.FontAwesomeIcons; import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.playback.PlaybackService; +import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBTasks; +import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.util.Converter; import de.danoeh.antennapod.core.util.ShareUtils; import de.danoeh.antennapod.core.util.StorageUtils; @@ -36,13 +46,17 @@ import de.danoeh.antennapod.core.util.playback.MediaPlayerError; import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.PlaybackController; import de.danoeh.antennapod.dialog.SleepTimerDialog; +import de.danoeh.antennapod.dialog.VariableSpeedDialog; +import rx.Observable; +import rx.android.schedulers.AndroidSchedulers; +import rx.schedulers.Schedulers; + /** * Provides general features which are both needed for playing audio and video * files. */ -public abstract class MediaplayerActivity extends ActionBarActivity - implements OnSeekBarChangeListener { +public abstract class MediaplayerActivity extends AppCompatActivity implements OnSeekBarChangeListener { private static final String TAG = "MediaplayerActivity"; private static final String PREFS = "MediaPlayerActivityPreferences"; private static final String PREF_SHOW_TIME_LEFT = "showTimeLeft"; @@ -52,12 +66,17 @@ public abstract class MediaplayerActivity extends ActionBarActivity protected TextView txtvPosition; protected TextView txtvLength; protected SeekBar sbPosition; - protected ImageButton butPlay; + protected Button butPlaybackSpeed; protected ImageButton butRev; - protected boolean showTimeLeft = false; protected TextView txtvRev; + protected ImageButton butPlay; protected ImageButton butFF; protected TextView txtvFF; + protected ImageButton butSkip; + + protected boolean showTimeLeft = false; + + private boolean isFavorite = false; private PlaybackController newPlaybackController() { return new PlaybackController(this, false) { @@ -157,7 +176,7 @@ public abstract class MediaplayerActivity extends ActionBarActivity } protected void onPlaybackSpeedChange() { - + updateButPlaybackSpeed(); } protected void onServiceQueried() { @@ -268,7 +287,7 @@ public abstract class MediaplayerActivity extends ActionBarActivity @Override public boolean onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); - if(controller == null) { + if (controller == null) { return false; } Playable media = controller.getMedia(); @@ -294,19 +313,35 @@ public abstract class MediaplayerActivity extends ActionBarActivity menu.findItem(R.id.share_item).setVisible(hasWebsiteLink || isItemAndHasLink || isItemHasDownloadLink); - menu.findItem(R.id.skip_episode_item).setVisible(media != null); + menu.findItem(R.id.add_to_favorites_item).setVisible(false); + menu.findItem(R.id.remove_from_favorites_item).setVisible(false); + if(media != null && media instanceof FeedMedia) { + menu.findItem(R.id.add_to_favorites_item).setVisible(!isFavorite); + menu.findItem(R.id.remove_from_favorites_item).setVisible(isFavorite); + } boolean sleepTimerSet = controller.sleepTimerActive(); boolean sleepTimerNotSet = controller.sleepTimerNotActive(); menu.findItem(R.id.set_sleeptimer_item).setVisible(sleepTimerNotSet); menu.findItem(R.id.disable_sleeptimer_item).setVisible(sleepTimerSet); + if (this instanceof AudioplayerActivity) { + int[] attrs = {R.attr.action_bar_icon_color}; + TypedArray ta = obtainStyledAttributes(UserPreferences.getTheme(), attrs); + int textColor = ta.getColor(0, Color.GRAY); + ta.recycle(); + menu.findItem(R.id.audio_controls).setIcon(new IconDrawable(this, + FontAwesomeIcons.fa_sliders).color(textColor).actionBarSize()); + } else { + menu.findItem(R.id.audio_controls).setVisible(false); + } + return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { - if(controller == null) { + if (controller == null) { return false; } Playable media = controller.getMedia(); @@ -317,84 +352,217 @@ public abstract class MediaplayerActivity extends ActionBarActivity | Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); return true; - } else if (media != null) { - switch (item.getItemId()) { - case R.id.disable_sleeptimer_item: - if (controller.serviceAvailable()) { + } else { + if (media != null) { + switch (item.getItemId()) { + case R.id.add_to_favorites_item: + if(media instanceof FeedMedia) { + FeedItem feedItem = ((FeedMedia)media).getItem(); + if(feedItem != null) { + DBWriter.addFavoriteItem(feedItem); + isFavorite = true; + invalidateOptionsMenu(); + Toast.makeText(this, R.string.added_to_favorites, Toast.LENGTH_SHORT) + .show(); + } + } + break; + case R.id.remove_from_favorites_item: + if(media instanceof FeedMedia) { + FeedItem feedItem = ((FeedMedia)media).getItem(); + if(feedItem != null) { + DBWriter.removeFavoriteItem(feedItem); + isFavorite = false; + invalidateOptionsMenu(); + Toast.makeText(this, R.string.removed_from_favorites, Toast.LENGTH_SHORT) + .show(); + } + } + break; + case R.id.disable_sleeptimer_item: + if (controller.serviceAvailable()) { - MaterialDialog.Builder stDialog = new MaterialDialog.Builder(this); - stDialog.title(R.string.sleep_timer_label); - stDialog.content(getString(R.string.time_left_label) - + Converter.getDurationStringLong((int) controller - .getSleepTimerTimeLeft())); - stDialog.positiveText(R.string.disable_sleeptimer_label); - stDialog.negativeText(R.string.cancel_label); - stDialog.callback(new MaterialDialog.ButtonCallback() { + MaterialDialog.Builder stDialog = new MaterialDialog.Builder(this); + stDialog.title(R.string.sleep_timer_label); + stDialog.content(getString(R.string.time_left_label) + + Converter.getDurationStringLong((int) controller + .getSleepTimerTimeLeft())); + stDialog.positiveText(R.string.disable_sleeptimer_label); + stDialog.negativeText(R.string.cancel_label); + stDialog.callback(new MaterialDialog.ButtonCallback() { + @Override + public void onPositive(MaterialDialog dialog) { + dialog.dismiss(); + controller.disableSleepTimer(); + } + + @Override + public void onNegative(MaterialDialog dialog) { + dialog.dismiss(); + } + }); + stDialog.build().show(); + } + break; + case R.id.set_sleeptimer_item: + if (controller.serviceAvailable()) { + SleepTimerDialog td = new SleepTimerDialog(this) { + @Override + public void onTimerSet(long millis, boolean shakeToReset, boolean vibrate) { + controller.setSleepTimer(millis, shakeToReset, vibrate); + } + }; + td.createNewDialog().show(); + } + break; + case R.id.audio_controls: + MaterialDialog dialog = new MaterialDialog.Builder(this) + .title(R.string.audio_controls) + .customView(R.layout.audio_controls, false) + .neutralText(R.string.close_label) + .onNeutral((dialog1, which) -> { + final SeekBar left = (SeekBar) dialog1.findViewById(R.id.volume_left); + final SeekBar right = (SeekBar) dialog1.findViewById(R.id.volume_right); + UserPreferences.setVolume(left.getProgress(), right.getProgress()); + }) + .show(); + final SeekBar barPlaybackSpeed = (SeekBar) dialog.findViewById(R.id.playback_speed); + + final Button butDecSpeed = (Button) dialog.findViewById(R.id.butDecSpeed); + butDecSpeed.setOnClickListener(v -> { + barPlaybackSpeed.setProgress(barPlaybackSpeed.getProgress() - 2); + }); + final Button butIncSpeed = (Button) dialog.findViewById(R.id.butIncSpeed); + butIncSpeed.setOnClickListener(v -> { + barPlaybackSpeed.setProgress(barPlaybackSpeed.getProgress() + 2); + }); + final TextView txtvPlaybackSpeed = (TextView) dialog.findViewById(R.id.txtvPlaybackSpeed); + float currentSpeed = 1.0f; + try { + currentSpeed = Float.parseFloat(UserPreferences.getPlaybackSpeed()); + } catch (NumberFormatException e) { + Log.e(TAG, Log.getStackTraceString(e)); + UserPreferences.setPlaybackSpeed(String.valueOf(currentSpeed)); + } + + txtvPlaybackSpeed.setText(String.format("%.2fx", currentSpeed)); + barPlaybackSpeed.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { @Override - public void onPositive(MaterialDialog dialog) { - dialog.dismiss(); - controller.disableSleepTimer(); + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + float playbackSpeed = (progress + 10) / 20.0f; + controller.setPlaybackSpeed(playbackSpeed); + String speed = String.format("%.2f", playbackSpeed); + UserPreferences.setPlaybackSpeed(speed); + txtvPlaybackSpeed.setText(speed + "x"); } @Override - public void onNegative(MaterialDialog dialog) { - dialog.dismiss(); + public void onStartTrackingTouch(SeekBar seekBar) { + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { } }); - stDialog.build().show(); - } - break; - case R.id.set_sleeptimer_item: - if (controller.serviceAvailable()) { - SleepTimerDialog td = new SleepTimerDialog(this) { - @Override - public void onTimerSet(long millis, boolean shakeToReset, boolean vibrate) { - controller.setSleepTimer(millis, shakeToReset, vibrate); - } - }; - td.createNewDialog().show(); - } - break; - case R.id.visit_website_item: - Uri uri = Uri.parse(media.getWebsiteLink()); - startActivity(new Intent(Intent.ACTION_VIEW, uri)); - break; - case R.id.support_item: - if (media instanceof FeedMedia) { - DBTasks.flattrItemIfLoggedIn(this, ((FeedMedia) media).getItem()); - } - break; - case R.id.share_link_item: - if (media instanceof FeedMedia) { - ShareUtils.shareFeedItemLink(this, ((FeedMedia) media).getItem()); - } - break; - case R.id.share_download_url_item: - if (media instanceof FeedMedia) { - ShareUtils.shareFeedItemDownloadLink(this, ((FeedMedia) media).getItem()); - } - break; - case R.id.share_link_with_position_item: - if (media instanceof FeedMedia) { - ShareUtils.shareFeedItemLink(this, ((FeedMedia) media).getItem(), true); - } - break; - case R.id.share_download_url_with_position_item: - if (media instanceof FeedMedia) { - ShareUtils.shareFeedItemDownloadLink(this, ((FeedMedia) media).getItem(), true); - } - break; - case R.id.skip_episode_item: - sendBroadcast(new Intent( - PlaybackService.ACTION_SKIP_CURRENT_EPISODE)); - break; - default: - return false; + barPlaybackSpeed.setProgress((int) (20 * currentSpeed) - 10); + final SeekBar barLeftVolume = (SeekBar) dialog.findViewById(R.id.volume_left); + barLeftVolume.setProgress(100); + final SeekBar barRightVolume = (SeekBar) dialog.findViewById(R.id.volume_right); + barRightVolume.setProgress(100); + final CheckBox stereoToMono = (CheckBox) dialog.findViewById(R.id.stereo_to_mono); + stereoToMono.setChecked(UserPreferences.stereoToMono()); + if (controller != null && !controller.canDownmix()) { + stereoToMono.setEnabled(false); + String sonicOnly = getString(R.string.sonic_only); + stereoToMono.setText(stereoToMono.getText() + " [" + sonicOnly + "]"); + } + + barLeftVolume.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + float leftVolume = 1.0f, rightVolume = 1.0f; + if (progress < 100) { + leftVolume = progress / 100.0f; + } + if (barRightVolume.getProgress() < 100) { + rightVolume = barRightVolume.getProgress() / 100.0f; + } + controller.setVolume(leftVolume, rightVolume); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + } + }); + barRightVolume.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + float leftVolume = 1.0f, rightVolume = 1.0f; + if (progress < 100) { + rightVolume = progress / 100.0f; + } + if (barLeftVolume.getProgress() < 100) { + leftVolume = barLeftVolume.getProgress() / 100.0f; + } + controller.setVolume(leftVolume, rightVolume); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + } + }); + stereoToMono.setOnCheckedChangeListener((buttonView, isChecked) -> { + UserPreferences.stereoToMono(isChecked); + if (controller != null) { + controller.setDownmix(isChecked); + } + }); + break; + case R.id.visit_website_item: + Uri uri = Uri.parse(media.getWebsiteLink()); + startActivity(new Intent(Intent.ACTION_VIEW, uri)); + break; + case R.id.support_item: + if (media instanceof FeedMedia) { + DBTasks.flattrItemIfLoggedIn(this, ((FeedMedia) media).getItem()); + } + break; + case R.id.share_link_item: + if (media instanceof FeedMedia) { + ShareUtils.shareFeedItemLink(this, ((FeedMedia) media).getItem()); + } + break; + case R.id.share_download_url_item: + if (media instanceof FeedMedia) { + ShareUtils.shareFeedItemDownloadLink(this, ((FeedMedia) media).getItem()); + } + break; + case R.id.share_link_with_position_item: + if (media instanceof FeedMedia) { + ShareUtils.shareFeedItemLink(this, ((FeedMedia) media).getItem(), true); + } + break; + case R.id.share_download_url_with_position_item: + if (media instanceof FeedMedia) { + ShareUtils.shareFeedItemDownloadLink(this, ((FeedMedia) media).getItem(), true); + } + break; + default: + return false; + } + return true; + } else { + return false; } - return true; - } else { - return false; } } @@ -427,11 +595,10 @@ public abstract class MediaplayerActivity extends ActionBarActivity && controller.getMedia() != null) { txtvPosition.setText(Converter .getDurationStringLong(currentPosition)); - if(showTimeLeft) { - txtvLength.setText("-"+Converter + if (showTimeLeft) { + txtvLength.setText("-" + Converter .getDurationStringLong(duration - currentPosition)); - } - else { + } else { txtvLength.setText(Converter .getDurationStringLong(duration)); } @@ -443,7 +610,7 @@ public abstract class MediaplayerActivity extends ActionBarActivity } private void updateProgressbarPosition(int position, int duration) { - Log.d(TAG, "updateProgressbarPosition(" + position + ", " + duration +")"); + Log.d(TAG, "updateProgressbarPosition(" + position + ", " + duration + ")"); float progress = ((float) position) / duration; sbPosition.setProgress((int) (progress * sbPosition.getMax())); } @@ -458,22 +625,31 @@ public abstract class MediaplayerActivity extends ActionBarActivity Log.d(TAG, "loadMediaInfo()"); Playable media = controller.getMedia(); SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE); - showTimeLeft = prefs.getBoolean(PREF_SHOW_TIME_LEFT,false); + showTimeLeft = prefs.getBoolean(PREF_SHOW_TIME_LEFT, false); if (media != null) { - txtvPosition.setText(Converter.getDurationStringLong((media - .getPosition()))); + txtvPosition.setText(Converter.getDurationStringLong((media.getPosition()))); if (media.getDuration() != 0) { - txtvLength.setText(Converter.getDurationStringLong(media - .getDuration())); - float progress = ((float) media.getPosition()) - / media.getDuration(); + txtvLength.setText(Converter.getDurationStringLong(media.getDuration())); + float progress = ((float) media.getPosition()) / media.getDuration(); sbPosition.setProgress((int) (progress * sbPosition.getMax())); - if(showTimeLeft) { - txtvLength.setText("-"+Converter.getDurationStringLong((media - .getDuration()-media.getPosition()))); + if (showTimeLeft) { + int timeLeft = media.getDuration() - media.getPosition(); + txtvLength.setText("-" + Converter.getDurationStringLong(timeLeft)); } } + checkFavorite(); + if(controller == null) { + butPlaybackSpeed.setVisibility(View.GONE); + } else { + butPlaybackSpeed.setVisibility(View.VISIBLE); + if (controller.canSetPlaybackSpeed()) { + ViewCompat.setAlpha(butPlaybackSpeed, 1.0f); + } else { + ViewCompat.setAlpha(butPlaybackSpeed, 0.5f); + } + } + updateButPlaybackSpeed(); return true; } else { return false; @@ -486,43 +662,42 @@ public abstract class MediaplayerActivity extends ActionBarActivity txtvPosition = (TextView) findViewById(R.id.txtvPosition); SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE); - showTimeLeft = prefs.getBoolean(PREF_SHOW_TIME_LEFT,false); - Log.d("timeleft",showTimeLeft? "true":"false"); + showTimeLeft = prefs.getBoolean(PREF_SHOW_TIME_LEFT, false); + Log.d("timeleft", showTimeLeft ? "true" : "false"); txtvLength = (TextView) findViewById(R.id.txtvLength); - txtvLength.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - showTimeLeft = !showTimeLeft; - Playable media = controller.getMedia(); - if (media == null) { - return; - } - - if (showTimeLeft) { - txtvLength.setText("-"+Converter.getDurationStringLong((media - .getDuration()-media.getPosition()))); - } else { - txtvLength.setText(Converter.getDurationStringLong((media.getDuration()))); - } - - SharedPreferences.Editor editor = prefs.edit(); - editor.putBoolean(PREF_SHOW_TIME_LEFT,showTimeLeft); - editor.apply(); - Log.d("timeleft on click",showTimeLeft? "true":"false"); + txtvLength.setOnClickListener(v -> { + showTimeLeft = !showTimeLeft; + Playable media = controller.getMedia(); + if (media == null) { + return; } + + if (showTimeLeft) { + txtvLength.setText("-" + Converter.getDurationStringLong((media + .getDuration() - media.getPosition()))); + } else { + txtvLength.setText(Converter.getDurationStringLong((media.getDuration()))); + } + + SharedPreferences.Editor editor = prefs.edit(); + editor.putBoolean(PREF_SHOW_TIME_LEFT, showTimeLeft); + editor.apply(); + Log.d("timeleft on click", showTimeLeft ? "true" : "false"); }); - butPlay = (ImageButton) findViewById(R.id.butPlay); + butPlaybackSpeed = (Button) findViewById(R.id.butPlaybackSpeed); butRev = (ImageButton) findViewById(R.id.butRev); txtvRev = (TextView) findViewById(R.id.txtvRev); - if(txtvRev != null) { + if (txtvRev != null) { txtvRev.setText(String.valueOf(UserPreferences.getRewindSecs())); } + butPlay = (ImageButton) findViewById(R.id.butPlay); butFF = (ImageButton) findViewById(R.id.butFF); txtvFF = (TextView) findViewById(R.id.txtvFF); - if(txtvFF != null) { + if (txtvFF != null) { txtvFF.setText(String.valueOf(UserPreferences.getFastFowardSecs())); } + butSkip = (ImageButton) findViewById(R.id.butSkip); // SEEKBAR SETUP @@ -530,15 +705,88 @@ public abstract class MediaplayerActivity extends ActionBarActivity // BUTTON SETUP + if(butPlaybackSpeed != null) { + butPlaybackSpeed.setOnClickListener(v -> { + if (controller != null && controller.canSetPlaybackSpeed()) { + String[] availableSpeeds = UserPreferences.getPlaybackSpeedArray(); + String currentSpeed = UserPreferences.getPlaybackSpeed(); + + // Provide initial value in case the speed list has changed + // out from under us + // and our current speed isn't in the new list + String newSpeed; + if (availableSpeeds.length > 0) { + newSpeed = availableSpeeds[0]; + } else { + newSpeed = "1.00"; + } + + for (int i = 0; i < availableSpeeds.length; i++) { + if (availableSpeeds[i].equals(currentSpeed)) { + if (i == availableSpeeds.length - 1) { + newSpeed = availableSpeeds[0]; + } else { + newSpeed = availableSpeeds[i + 1]; + } + break; + } + } + UserPreferences.setPlaybackSpeed(newSpeed); + controller.setPlaybackSpeed(Float.parseFloat(newSpeed)); + } + }); + butPlaybackSpeed.setOnLongClickListener(v -> { + VariableSpeedDialog.showDialog(this); + return true; + }); + } + + if (butRev != null) { + butRev.setOnClickListener(v -> { + int curr = controller.getPosition(); + controller.seekTo(curr - UserPreferences.getRewindSecs() * 1000); + }); + butRev.setOnLongClickListener(new View.OnLongClickListener() { + + int choice; + + @Override + public boolean onLongClick(View v) { + int checked = 0; + int rewindSecs = UserPreferences.getRewindSecs(); + final int[] values = getResources().getIntArray(R.array.seek_delta_values); + final String[] choices = new String[values.length]; + for (int i = 0; i < values.length; i++) { + if (rewindSecs == values[i]) { + checked = i; + } + choices[i] = String.valueOf(values[i]) + " " + + getString(R.string.time_seconds); + } + choice = values[checked]; + AlertDialog.Builder builder = new AlertDialog.Builder(MediaplayerActivity.this); + builder.setTitle(R.string.pref_rewind); + builder.setSingleChoiceItems(choices, checked, + (dialog, which) -> { + choice = values[which]; + }); + builder.setNegativeButton(R.string.cancel_label, null); + builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> { + UserPreferences.setPrefRewindSecs(choice); + txtvRev.setText(String.valueOf(choice)); + }); + builder.create().show(); + return true; + } + }); + } + butPlay.setOnClickListener(controller.newOnPlayButtonClickListener()); if (butFF != null) { - butFF.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - int curr = controller.getPosition(); - controller.seekTo(curr + UserPreferences.getFastFowardSecs() * 1000); - } + butFF.setOnClickListener(v -> { + int curr = controller.getPosition(); + controller.seekTo(curr + UserPreferences.getFastFowardSecs() * 1000); }); butFF.setOnLongClickListener(new View.OnLongClickListener() { @@ -550,7 +798,7 @@ public abstract class MediaplayerActivity extends ActionBarActivity int rewindSecs = UserPreferences.getFastFowardSecs(); final int[] values = getResources().getIntArray(R.array.seek_delta_values); final String[] choices = new String[values.length]; - for(int i=0; i < values.length; i++) { + for (int i = 0; i < values.length; i++) { if (rewindSecs == values[i]) { checked = i; } @@ -574,55 +822,12 @@ public abstract class MediaplayerActivity extends ActionBarActivity } }); } - if (butRev != null) { - butRev.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - int curr = controller.getPosition(); - controller.seekTo(curr - UserPreferences.getRewindSecs() * 1000); - } - }); - butRev.setOnLongClickListener(new View.OnLongClickListener() { - int choice; - - @Override - public boolean onLongClick(View v) { - int checked = 0; - int rewindSecs = UserPreferences.getRewindSecs(); - final int[] values = getResources().getIntArray(R.array.seek_delta_values); - final String[] choices = new String[values.length]; - for(int i=0; i < values.length; i++) { - if (rewindSecs == values[i]) { - checked = i; - } - choices[i] = String.valueOf(values[i]) + " " - + getString(R.string.time_seconds); - } - choice = values[checked]; - AlertDialog.Builder builder = new AlertDialog.Builder(MediaplayerActivity.this); - builder.setTitle(R.string.pref_rewind); - builder.setSingleChoiceItems(choices, checked, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - choice = values[which]; - } - }); - builder.setNegativeButton(R.string.cancel_label, null); - builder.setPositiveButton(R.string.confirm_label, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - UserPreferences.setPrefRewindSecs(choice); - txtvRev.setText(String.valueOf(choice)); - } - }); - builder.create().show(); - return true; - } + if (butSkip != null) { + butSkip.setOnClickListener(v -> { + sendBroadcast(new Intent(PlaybackService.ACTION_SKIP_CURRENT_EPISODE)); }); } - } protected abstract int getContentViewResourceId(); @@ -633,12 +838,9 @@ public abstract class MediaplayerActivity extends ActionBarActivity errorDialog .setMessage(MediaPlayerError.getErrorString(this, errorCode)); errorDialog.setNeutralButton("OK", - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - finish(); - } + (dialog, which) -> { + dialog.dismiss(); + finish(); } ); errorDialog.create().show(); @@ -652,14 +854,20 @@ public abstract class MediaplayerActivity extends ActionBarActivity if (controller != null) { prog = controller.onSeekBarProgressChanged(seekBar, progress, fromUser, txtvPosition); - if(showTimeLeft && prog!=0) { + if (showTimeLeft && prog != 0) { int duration = controller.getDuration(); - txtvLength.setText("-"+Converter + txtvLength.setText("-" + Converter .getDurationStringLong(duration - (int) (prog * duration))); } } } + private void updateButPlaybackSpeed() { + if (controller != null) { + butPlaybackSpeed.setText(UserPreferences.getPlaybackSpeed() + "x"); + } + } + @Override public void onStartTrackingTouch(SeekBar seekBar) { if (controller != null) { @@ -674,4 +882,23 @@ public abstract class MediaplayerActivity extends ActionBarActivity } } + private void checkFavorite() { + Playable playable = controller.getMedia(); + if (playable != null && playable instanceof FeedMedia) { + FeedItem feedItem = ((FeedMedia) playable).getItem(); + if (feedItem != null) { + Observable.fromCallable(() -> DBReader.getFeedItem(feedItem.getId())) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(item -> { + boolean isFav = item.isTagged(FeedItem.TAG_FAVORITE); + if(isFavorite != isFav) { + isFavorite = isFav; + invalidateOptionsMenu(); + } + }); + } + } + } + } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/ChapterListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/ChapterListAdapter.java deleted file mode 100644 index cf0532cf1..000000000 --- a/app/src/main/java/de/danoeh/antennapod/adapter/ChapterListAdapter.java +++ /dev/null @@ -1,199 +0,0 @@ -package de.danoeh.antennapod.adapter; - -import android.content.Context; -import android.text.Layout; -import android.text.Selection; -import android.text.Spannable; -import android.text.Spanned; -import android.text.style.ClickableSpan; -import android.text.util.Linkify; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnTouchListener; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.ImageButton; -import android.widget.TextView; -import de.danoeh.antennapod.R; -import de.danoeh.antennapod.core.feed.Chapter; -import de.danoeh.antennapod.core.util.ChapterUtils; -import de.danoeh.antennapod.core.util.Converter; -import de.danoeh.antennapod.core.util.playback.Playable; - -import java.util.List; - -public class ChapterListAdapter extends ArrayAdapter { - - private static final String TAG = "ChapterListAdapter"; - - private List chapters; - private Playable media; - - private int defaultTextColor; - private final Callback callback; - - public ChapterListAdapter(Context context, int textViewResourceId, - List objects, Playable media, Callback callback) { - super(context, textViewResourceId, objects); - this.chapters = objects; - this.media = media; - this.callback = callback; - } - - @Override - public View getView(final int position, View convertView, ViewGroup parent) { - Holder holder; - - Chapter sc = getItem(position); - - // Inflate Layout - if (convertView == null) { - holder = new Holder(); - LayoutInflater inflater = (LayoutInflater) getContext() - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - - convertView = inflater.inflate(R.layout.simplechapter_item, parent, false); - holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); - defaultTextColor = holder.title.getTextColors().getDefaultColor(); - holder.start = (TextView) convertView.findViewById(R.id.txtvStart); - holder.link = (TextView) convertView.findViewById(R.id.txtvLink); - holder.butPlayChapter = (ImageButton) convertView.findViewById(R.id.butPlayChapter); - convertView.setTag(holder); - } else { - holder = (Holder) convertView.getTag(); - - } - - holder.title.setText(sc.getTitle()); - holder.start.setText(Converter.getDurationStringLong((int) sc - .getStart())); - if (sc.getLink() != null) { - holder.link.setVisibility(View.VISIBLE); - holder.link.setText(sc.getLink()); - Linkify.addLinks(holder.link, Linkify.WEB_URLS); - } else { - holder.link.setVisibility(View.GONE); - } - holder.link.setMovementMethod(null); - holder.link.setOnTouchListener(new OnTouchListener() { - - @Override - public boolean onTouch(View v, MotionEvent event) { - // from - // http://stackoverflow.com/questions/7236840/android-textview-linkify-intercepts-with-parent-view-gestures - TextView widget = (TextView) v; - Object text = widget.getText(); - if (text instanceof Spanned) { - Spannable buffer = (Spannable) text; - - int action = event.getAction(); - - if (action == MotionEvent.ACTION_UP - || action == MotionEvent.ACTION_DOWN) { - int x = (int) event.getX(); - int y = (int) event.getY(); - - x -= widget.getTotalPaddingLeft(); - y -= widget.getTotalPaddingTop(); - - x += widget.getScrollX(); - y += widget.getScrollY(); - - Layout layout = widget.getLayout(); - int line = layout.getLineForVertical(y); - int off = layout.getOffsetForHorizontal(line, x); - - ClickableSpan[] link = buffer.getSpans(off, off, - ClickableSpan.class); - - if (link.length != 0) { - if (action == MotionEvent.ACTION_UP) { - link[0].onClick(widget); - } else if (action == MotionEvent.ACTION_DOWN) { - Selection.setSelection(buffer, - buffer.getSpanStart(link[0]), - buffer.getSpanEnd(link[0])); - } - return true; - } - } - - } - - return false; - - } - }); - holder.butPlayChapter.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (callback != null) { - callback.onPlayChapterButtonClicked(position); - } - } - }); - Chapter current = ChapterUtils.getCurrentChapter(media); - if (current != null) { - if (current == sc) { - holder.title.setTextColor(convertView.getResources().getColor( - R.color.holo_blue_light)); - holder.start.setTextColor(convertView.getResources().getColor( - R.color.holo_blue_light)); - } else { - holder.title.setTextColor(defaultTextColor); - holder.start.setTextColor(defaultTextColor); - } - } else { - Log.w(TAG, "Could not find out what the current chapter is."); - } - - return convertView; - } - - static class Holder { - TextView title; - TextView start; - TextView link; - ImageButton butPlayChapter; - } - - @Override - public int getCount() { - // ignore invalid chapters - int counter = 0; - if (chapters != null) { - for (Chapter chapter : chapters) { - if (!ignoreChapter(chapter)) { - counter++; - } - } - } - return counter; - } - - private boolean ignoreChapter(Chapter c) { - return media.getDuration() > 0 && media.getDuration() < c.getStart(); - } - - @Override - public Chapter getItem(int position) { - int i = 0; - for (Chapter chapter : chapters) { - if (!ignoreChapter(chapter)) { - if (i == position) { - return chapter; - } else { - i++; - } - } - } - return super.getItem(position); - } - - public static interface Callback { - public void onPlayChapterButtonClicked(int position); - } - -} diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java new file mode 100644 index 000000000..1c9439ee6 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java @@ -0,0 +1,198 @@ +package de.danoeh.antennapod.adapter; + +import android.content.Context; +import android.text.Layout; +import android.text.Selection; +import android.text.Spannable; +import android.text.Spanned; +import android.text.style.ClickableSpan; +import android.text.util.Linkify; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ImageButton; +import android.widget.TextView; + +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.feed.Chapter; +import de.danoeh.antennapod.core.preferences.UserPreferences; +import de.danoeh.antennapod.core.util.ChapterUtils; +import de.danoeh.antennapod.core.util.Converter; +import de.danoeh.antennapod.core.util.playback.Playable; + +public class ChaptersListAdapter extends ArrayAdapter { + + private static final String TAG = "ChapterListAdapter"; + + private Playable media; + + private int defaultTextColor; + private final Callback callback; + + public ChaptersListAdapter(Context context, int textViewResourceId, Callback callback) { + super(context, textViewResourceId); + this.callback = callback; + } + + public void setMedia(Playable media) { + this.media = media; + } + + @Override + public View getView(final int position, View convertView, ViewGroup parent) { + Holder holder; + + Chapter sc = getItem(position); + + // Inflate Layout + if (convertView == null) { + holder = new Holder(); + LayoutInflater inflater = (LayoutInflater) getContext() + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + convertView = inflater.inflate(R.layout.simplechapter_item, parent, false); + holder.view = convertView; + holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); + defaultTextColor = holder.title.getTextColors().getDefaultColor(); + holder.start = (TextView) convertView.findViewById(R.id.txtvStart); + holder.link = (TextView) convertView.findViewById(R.id.txtvLink); + holder.butPlayChapter = (ImageButton) convertView.findViewById(R.id.butPlayChapter); + convertView.setTag(holder); + } else { + holder = (Holder) convertView.getTag(); + + } + + holder.title.setText(sc.getTitle()); + holder.start.setText(Converter.getDurationStringLong((int) sc + .getStart())); + if (sc.getLink() != null) { + holder.link.setVisibility(View.VISIBLE); + holder.link.setText(sc.getLink()); + Linkify.addLinks(holder.link, Linkify.WEB_URLS); + } else { + holder.link.setVisibility(View.GONE); + } + holder.link.setMovementMethod(null); + holder.link.setOnTouchListener((v, event) -> { + // from + // http://stackoverflow.com/questions/7236840/android-textview-linkify-intercepts-with-parent-view-gestures + TextView widget = (TextView) v; + Object text = widget.getText(); + if (text instanceof Spanned) { + Spannable buffer = (Spannable) text; + + int action = event.getAction(); + + if (action == MotionEvent.ACTION_UP + || action == MotionEvent.ACTION_DOWN) { + int x = (int) event.getX(); + int y = (int) event.getY(); + + x -= widget.getTotalPaddingLeft(); + y -= widget.getTotalPaddingTop(); + + x += widget.getScrollX(); + y += widget.getScrollY(); + + Layout layout = widget.getLayout(); + int line = layout.getLineForVertical(y); + int off = layout.getOffsetForHorizontal(line, x); + + ClickableSpan[] link = buffer.getSpans(off, off, + ClickableSpan.class); + + if (link.length != 0) { + if (action == MotionEvent.ACTION_UP) { + link[0].onClick(widget); + } else if (action == MotionEvent.ACTION_DOWN) { + Selection.setSelection(buffer, + buffer.getSpanStart(link[0]), + buffer.getSpanEnd(link[0])); + } + return true; + } + } + + } + + return false; + + }); + holder.butPlayChapter.setOnClickListener(v -> { + if (callback != null) { + callback.onPlayChapterButtonClicked(position); + } + }); + Chapter current = ChapterUtils.getCurrentChapter(media); + if (current != null) { + if (current == sc) { + int playingBackGroundColor; + if(UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) { + playingBackGroundColor = getContext().getResources().getColor(R.color.highlight_dark); + } else { + playingBackGroundColor = getContext().getResources().getColor(R.color.highlight_light); + } + holder.view.setBackgroundColor(playingBackGroundColor); + } else { + holder.view.setBackgroundColor(getContext().getResources().getColor(android.R.color.transparent)); + holder.title.setTextColor(defaultTextColor); + holder.start.setTextColor(defaultTextColor); + } + } else { + Log.w(TAG, "Could not find out what the current chapter is."); + } + + return convertView; + } + + static class Holder { + View view; + TextView title; + TextView start; + TextView link; + ImageButton butPlayChapter; + } + + @Override + public int getCount() { + if(media == null || media.getChapters() == null) { + return 0; + } + // ignore invalid chapters + int counter = 0; + for (Chapter chapter : media.getChapters()) { + if (!ignoreChapter(chapter)) { + counter++; + } + } + return counter; + } + + private boolean ignoreChapter(Chapter c) { + return media.getDuration() > 0 && media.getDuration() < c.getStart(); + } + + @Override + public Chapter getItem(int position) { + int i = 0; + for (Chapter chapter : media.getChapters()) { + if (!ignoreChapter(chapter)) { + if (i == position) { + return chapter; + } else { + i++; + } + } + } + return super.getItem(position); + } + + public interface Callback { + void onPlayChapterButtonClicked(int position); + } + +} diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java index 4b512a48d..f2e8b6f7a 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java @@ -21,107 +21,96 @@ import de.danoeh.antennapod.core.util.IntentUtils; public class VariableSpeedDialog { - private static final String TAG = VariableSpeedDialog.class.getSimpleName(); + private static final String TAG = VariableSpeedDialog.class.getSimpleName(); - private static final Intent playStoreIntent = new Intent(Intent.ACTION_VIEW, - Uri.parse("market://details?id=com.falconware.prestissimo")); + private static final Intent playStoreIntent = new Intent(Intent.ACTION_VIEW, + Uri.parse("market://details?id=com.falconware.prestissimo")); - private VariableSpeedDialog() { - } + private VariableSpeedDialog() { + } - public static void showDialog(final Context context) { - if (org.antennapod.audio.MediaPlayer.isPrestoLibraryInstalled(context) - || UserPreferences.useSonic()) { - showSpeedSelectorDialog(context); - } else { - showGetPluginDialog(context); - } - } + public static void showDialog(final Context context) { + if (org.antennapod.audio.MediaPlayer.isPrestoLibraryInstalled(context) + || UserPreferences.useSonic()) { + showSpeedSelectorDialog(context); + } else { + showGetPluginDialog(context); + } + } - private static void showGetPluginDialog(final Context context) { - MaterialDialog.Builder builder = new MaterialDialog.Builder(context); - builder.title(R.string.no_playback_plugin_title); - builder.content(R.string.no_playback_plugin_or_sonic_msg); - builder.positiveText(R.string.download_plugin_label); - builder.negativeText(R.string.enable_sonic); - builder.neutralText(R.string.close_label); - builder.callback(new MaterialDialog.ButtonCallback() { - @Override - public void onPositive(MaterialDialog dialog) { - try { - context.startActivity(playStoreIntent); - } catch (ActivityNotFoundException e) { - // this is usually thrown on an emulator if the Android market is not installed - Log.e(TAG, Log.getStackTraceString(e)); - } - } + private static void showGetPluginDialog(final Context context) { + MaterialDialog.Builder builder = new MaterialDialog.Builder(context); + builder.title(R.string.no_playback_plugin_title); + builder.content(R.string.no_playback_plugin_or_sonic_msg); + builder.positiveText(R.string.download_plugin_label); + builder.negativeText(R.string.enable_sonic); + builder.neutralText(R.string.close_label); + builder.onPositive((dialog, which) -> { + try { + context.startActivity(playStoreIntent); + } catch (ActivityNotFoundException e) { + // this is usually thrown on an emulator if the Android market is not installed + Log.e(TAG, Log.getStackTraceString(e)); + } + }); + builder.onNegative((dialog, which) -> { + if (Build.VERSION.SDK_INT >= 16) { // just to be safe + UserPreferences.enableSonic(true); + showSpeedSelectorDialog(context); + } + }); + MaterialDialog dialog = builder.show(); + if(!IntentUtils.isCallable(context.getApplicationContext(), playStoreIntent)) { + View pos = dialog.getActionButton(DialogAction.POSITIVE); + pos.setEnabled(false); + } + if (Build.VERSION.SDK_INT < 16) { + View pos = dialog.getActionButton(DialogAction.NEGATIVE); + pos.setEnabled(false); + } + } - @Override - public void onNegative(MaterialDialog dialog) { - if (Build.VERSION.SDK_INT >= 16) { // just to be safe - UserPreferences.enableSonic(true); - showSpeedSelectorDialog(context); - } - } + private static void showSpeedSelectorDialog(final Context context) { + final String[] speedValues = context.getResources().getStringArray( + R.array.playback_speed_values); + // According to Java spec these get initialized to false on creation + final boolean[] speedChecked = new boolean[speedValues.length]; - @Override - public void onNeutral(MaterialDialog dialog) { - super.onNeutral(dialog); - } - }); - builder.forceStacking(true); - MaterialDialog dialog = builder.show(); - if(!IntentUtils.isCallable(context.getApplicationContext(), playStoreIntent)) { - View pos = dialog.getActionButton(DialogAction.POSITIVE); - pos.setEnabled(false); - } - if (Build.VERSION.SDK_INT < 16) { - View pos = dialog.getActionButton(DialogAction.NEGATIVE); - pos.setEnabled(false); - } - } + // Build the "isChecked" array so that multiChoice dialog is + // populated correctly + List selectedSpeedList = Arrays.asList(UserPreferences + .getPlaybackSpeedArray()); + for (int i = 0; i < speedValues.length; i++) { + speedChecked[i] = selectedSpeedList.contains(speedValues[i]); + } - private static void showSpeedSelectorDialog(final Context context) { - final String[] speedValues = context.getResources().getStringArray( - R.array.playback_speed_values); - // According to Java spec these get initialized to false on creation - final boolean[] speedChecked = new boolean[speedValues.length]; + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(R.string.set_playback_speed_label); + builder.setMultiChoiceItems(R.array.playback_speed_values, + speedChecked, (dialog, which, isChecked) -> { + speedChecked[which] = isChecked; + }); + builder.setNegativeButton(android.R.string.cancel, null); + builder.setPositiveButton(android.R.string.ok, + (dialog, which) -> { + int choiceCount = 0; + for (int i = 0; i < speedChecked.length; i++) { + if (speedChecked[i]) { + choiceCount++; + } + } + String[] newSpeedValues = new String[choiceCount]; + int newSpeedIndex = 0; + for (int i = 0; i < speedChecked.length; i++) { + if (speedChecked[i]) { + newSpeedValues[newSpeedIndex++] = speedValues[i]; + } + } - // Build the "isChecked" array so that multiChoice dialog is - // populated correctly - List selectedSpeedList = Arrays.asList(UserPreferences - .getPlaybackSpeedArray()); - for (int i = 0; i < speedValues.length; i++) { - speedChecked[i] = selectedSpeedList.contains(speedValues[i]); - } + UserPreferences.setPlaybackSpeedArray(newSpeedValues); - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(R.string.set_playback_speed_label); - builder.setMultiChoiceItems(R.array.playback_speed_values, - speedChecked, (dialog, which, isChecked) -> { - speedChecked[which] = isChecked; - }); - builder.setNegativeButton(android.R.string.cancel, null); - builder.setPositiveButton(android.R.string.ok, - (dialog, which) -> { - int choiceCount = 0; - for (int i = 0; i < speedChecked.length; i++) { - if (speedChecked[i]) { - choiceCount++; - } - } - String[] newSpeedValues = new String[choiceCount]; - int newSpeedIndex = 0; - for (int i = 0; i < speedChecked.length; i++) { - if (speedChecked[i]) { - newSpeedValues[newSpeedIndex++] = speedValues[i]; - } - } - - UserPreferences.setPlaybackSpeedArray(newSpeedValues); - - }); - builder.create().show(); - } + }); + builder.create().show(); + } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java new file mode 100644 index 000000000..abc9f3d22 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java @@ -0,0 +1,68 @@ +package de.danoeh.antennapod.fragment; + +import android.os.Bundle; +import android.support.v4.app.ListFragment; +import android.view.View; +import android.widget.ListView; + +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.AudioplayerActivity.AudioplayerContentFragment; +import de.danoeh.antennapod.adapter.ChaptersListAdapter; +import de.danoeh.antennapod.core.feed.Chapter; +import de.danoeh.antennapod.core.util.playback.Playable; +import de.danoeh.antennapod.core.util.playback.PlaybackController; + + +public class ChaptersFragment extends ListFragment implements AudioplayerContentFragment { + + private Playable media; + private PlaybackController controller; + + private ChaptersListAdapter adapter; + + public static ChaptersFragment newInstance(Playable media, PlaybackController controller) { + ChaptersFragment f = new ChaptersFragment(); + f.media = media; + f.controller = controller; + return f; + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + // add padding + final ListView lv = getListView(); + lv.setClipToPadding(false); + final int vertPadding = getResources().getDimensionPixelSize(R.dimen.list_vertical_padding); + lv.setPadding(0, vertPadding, 0, vertPadding); + + adapter = new ChaptersListAdapter(getActivity(), 0, pos -> { + Chapter chapter = (Chapter) getListAdapter().getItem(pos); + controller.seekToChapter(chapter); + }); + setListAdapter(adapter); + } + + @Override + public void onResume() { + super.onResume(); + adapter.setMedia(media); + adapter.notifyDataSetChanged(); + if(media == null || media.getChapters() == null) { + setEmptyText(getString(R.string.no_chapters_label)); + } else { + setEmptyText(null); + } + } + + @Override + public void onDataSetChanged(Playable media) { + adapter.setMedia(media); + adapter.notifyDataSetChanged(); + if(media.getChapters() == null) { + setEmptyText(getString(R.string.no_items_label)); + } else { + setEmptyText(null); + } + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java index a1667cce0..60a607179 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java @@ -1,21 +1,21 @@ package de.danoeh.antennapod.fragment; -import android.app.Activity; -import android.content.Context; +import android.graphics.Bitmap; import android.os.Bundle; import android.support.v4.app.Fragment; +import android.support.v7.graphics.Palette; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; +import android.widget.TextView; import com.bumptech.glide.Glide; -import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.bumptech.glide.request.animation.GlideAnimation; +import com.bumptech.glide.request.target.BitmapImageViewTarget; -import de.danoeh.antennapod.BuildConfig; import de.danoeh.antennapod.R; -import de.danoeh.antennapod.activity.AudioplayerActivity; import de.danoeh.antennapod.activity.AudioplayerActivity.AudioplayerContentFragment; import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.util.playback.Playable; @@ -30,10 +30,11 @@ public class CoverFragment extends Fragment implements private Playable media; + private View root; + private TextView txtvPodcastTitle; + private TextView txtvEpisodeTitle; private ImageView imgvCover; - private boolean viewCreated = false; - public static CoverFragment newInstance(Playable item) { CoverFragment f = new CoverFragment(); if (item != null) { @@ -59,38 +60,45 @@ public class CoverFragment extends Fragment implements @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View root = inflater.inflate(R.layout.cover_fragment, container, false); + root = inflater.inflate(R.layout.cover_fragment, container, false); + txtvPodcastTitle = (TextView) root.findViewById(R.id.txtvPodcastTitle); + txtvEpisodeTitle = (TextView) root.findViewById(R.id.txtvEpisodeTitle); imgvCover = (ImageView) root.findViewById(R.id.imgvCover); - imgvCover.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Activity activity = getActivity(); - if (activity != null && activity instanceof AudioplayerActivity) { - ((AudioplayerActivity)activity).switchToLastFragment(); - } - } - }); - viewCreated = true; return root; } private void loadMediaInfo() { + if(imgvCover == null) { + return; + } if (media != null) { - imgvCover.post(new Runnable() { - - @Override - public void run() { - Context c = getActivity(); - if (c != null) { - Glide.with(c) - .load(media.getImageUri()) - .placeholder(R.color.light_gray) - .error(R.color.light_gray) - .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) - .dontAnimate() - .into(imgvCover); - } - } + Log.d(TAG, "feed title: " + media.getFeedTitle()); + Log.d(TAG, "episode title: " + media.getEpisodeTitle()); + txtvPodcastTitle.setText(media.getFeedTitle()); + txtvEpisodeTitle.setText(media.getEpisodeTitle()); + imgvCover.post(() -> { + Glide.with(this) + .load(media.getImageUri()) + .asBitmap() + .placeholder(R.color.light_gray) + .error(R.color.light_gray) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .dontAnimate() + .into(new BitmapImageViewTarget(imgvCover) { + @Override + public void onResourceReady(Bitmap bitmap, GlideAnimation anim) { + super.onResourceReady(bitmap, anim); + Palette.Builder builder = new Palette.Builder(bitmap); + builder.generate(palette -> { + Palette.Swatch swatch = palette.getMutedSwatch(); + if(swatch != null) { + root.setBackgroundColor(swatch.getRgb()); + txtvPodcastTitle.setTextColor(swatch.getTitleTextColor()); + txtvEpisodeTitle.setTextColor(swatch.getBodyTextColor()); + } + }); + } + }); }); } else { Log.w(TAG, "loadMediaInfo was called while media was null"); @@ -99,12 +107,10 @@ public class CoverFragment extends Fragment implements @Override public void onStart() { - if (BuildConfig.DEBUG) - Log.d(TAG, "On Start"); + Log.d(TAG, "On Start"); super.onStart(); if (media != null) { - if (BuildConfig.DEBUG) - Log.d(TAG, "Loading media info"); + Log.d(TAG, "Loading media info"); loadMediaInfo(); } else { Log.w(TAG, "Unable to load media info: media was null"); @@ -114,10 +120,7 @@ public class CoverFragment extends Fragment implements @Override public void onDataSetChanged(Playable media) { this.media = media; - if (viewCreated) { - loadMediaInfo(); - } - + loadMediaInfo(); } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java index 888684313..80a9bf0b3 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java @@ -13,7 +13,6 @@ import android.widget.ProgressBar; import android.widget.TextView; import com.bumptech.glide.Glide; -import com.bumptech.glide.load.engine.DiskCacheStrategy; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.glide.ApGlideSettings; @@ -79,51 +78,16 @@ public class ExternalPlayerFragment extends Fragment { private PlaybackController setupPlaybackController() { return new PlaybackController(getActivity(), true) { - @Override - public void setupGUI() { - } - @Override public void onPositionObserverUpdate() { ExternalPlayerFragment.this.onPositionObserverUpdate(); } - @Override - public void onReloadNotification(int code) { - } - - @Override - public void onBufferStart() { - } - - @Override - public void onBufferEnd() { - } - - @Override - public void onBufferUpdate(float progress) { - } - - @Override - public void onSleepTimerUpdate() { - } - - @Override - public void handleError(int code) { - } - @Override public ImageButton getPlayButton() { return butPlay; } - @Override - public void postStatusMsg(int msg) { - } - - @Override - public void clearStatusMsg() { - } @Override public boolean loadMediaInfo() { @@ -135,14 +99,6 @@ public class ExternalPlayerFragment extends Fragment { } } - @Override - public void onAwaitingVideoSurface() { - } - - @Override - public void onServiceQueried() { - } - @Override public void onShutdownNotification() { playbackDone(); @@ -152,10 +108,6 @@ public class ExternalPlayerFragment extends Fragment { public void onPlaybackEnd() { playbackDone(); } - - @Override - public void onPlaybackSpeedChange() { - } }; } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java index faa4413bb..6c9473176 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java @@ -8,7 +8,6 @@ import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; -import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.support.v4.app.Fragment; @@ -26,6 +25,8 @@ import android.webkit.WebViewClient; import android.widget.Toast; import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.AudioplayerActivity; +import de.danoeh.antennapod.activity.AudioplayerActivity.AudioplayerContentFragment; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.storage.DBReader; @@ -36,11 +37,15 @@ import de.danoeh.antennapod.core.util.ShownotesProvider; import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.PlaybackController; import de.danoeh.antennapod.core.util.playback.Timeline; +import rx.Observable; +import rx.Subscription; +import rx.android.schedulers.AndroidSchedulers; +import rx.schedulers.Schedulers; /** * Displays the description of a Playable object in a Webview. */ -public class ItemDescriptionFragment extends Fragment { +public class ItemDescriptionFragment extends Fragment implements AudioplayerContentFragment { private static final String TAG = "ItemDescriptionFragment"; @@ -55,12 +60,13 @@ public class ItemDescriptionFragment extends Fragment { private static final String ARG_HIGHLIGHT_TIMECODES = "arg.highlightTimecodes"; private WebView webvDescription; + private String webvData; private ShownotesProvider shownotesProvider; private Playable media; - private AsyncTask webViewLoader; + private Subscription webViewLoader; /** * URL that was selected via long-press. @@ -111,12 +117,10 @@ public class ItemDescriptionFragment extends Fragment { && Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { webvDescription.setLayerType(View.LAYER_TYPE_SOFTWARE, null); } - webvDescription.setBackgroundColor(getResources().getColor( - R.color.black)); + webvDescription.setBackgroundColor(getResources().getColor(R.color.black)); } webvDescription.getSettings().setUseWideViewPort(false); - webvDescription.getSettings().setLayoutAlgorithm( - LayoutAlgorithm.NARROW_COLUMNS); + webvDescription.getSettings().setLayoutAlgorithm(LayoutAlgorithm.SINGLE_COLUMN); webvDescription.getSettings().setLoadWithOverviewMode(true); webvDescription.setOnLongClickListener(webViewLongClickListener); webvDescription.setWebViewClient(new WebViewClient() { @@ -142,14 +146,7 @@ public class ItemDescriptionFragment extends Fragment { super.onPageFinished(view, url); Log.d(TAG, "Page finished"); // Restoring the scroll position might not always work - view.postDelayed(new Runnable() { - - @Override - public void run() { - restoreFromPreference(); - } - - }, 50); + view.postDelayed(() -> restoreFromPreference(), 50); } }); @@ -158,27 +155,12 @@ public class ItemDescriptionFragment extends Fragment { return webvDescription; } - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - Log.d(TAG, "Fragment attached"); - } - - @Override - public void onDetach() { - super.onDetach(); - Log.d(TAG, "Fragment detached"); - if (webViewLoader != null) { - webViewLoader.cancel(true); - } - } - @Override public void onDestroy() { super.onDestroy(); Log.d(TAG, "Fragment destroyed"); if (webViewLoader != null) { - webViewLoader.cancel(true); + webViewLoader.unsubscribe(); } if (webvDescription != null) { webvDescription.removeAllViews(); @@ -194,7 +176,6 @@ public class ItemDescriptionFragment extends Fragment { Bundle args = getArguments(); saveState = args.getBoolean(ARG_SAVE_STATE, false); highlightTimecodes = args.getBoolean(ARG_HIGHLIGHT_TIMECODES, false); - } @Override @@ -204,47 +185,22 @@ public class ItemDescriptionFragment extends Fragment { if (args.containsKey(ARG_PLAYABLE)) { media = args.getParcelable(ARG_PLAYABLE); shownotesProvider = media; - startLoader(); + load(); } else if (args.containsKey(ARG_FEEDITEM_ID)) { - AsyncTask itemLoadTask = new AsyncTask() { - - @Override - protected FeedItem doInBackground(Void... voids) { - return DBReader.getFeedItem(getArguments().getLong(ARG_FEEDITEM_ID)); - } - - @Override - protected void onPostExecute(FeedItem feedItem) { - super.onPostExecute(feedItem); - shownotesProvider = feedItem; - startLoader(); - } - }; - if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) { - itemLoadTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } else { - itemLoadTask.execute(); - } - } - - - } - - @Override - public void onResume() { - super.onResume(); - } - - @SuppressLint("NewApi") - private void startLoader() { - webViewLoader = createLoader(); - if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) { - webViewLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } else { - webViewLoader.execute(); + long id = getArguments().getLong(ARG_FEEDITEM_ID); + Observable.defer(() -> Observable.just(DBReader.getFeedItem(id))) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(feedItem -> { + shownotesProvider = feedItem; + load(); + }, error -> { + Log.e(TAG, Log.getStackTraceString(error)); + }); } } + private View.OnLongClickListener webViewLongClickListener = new View.OnLongClickListener() { @Override @@ -338,49 +294,31 @@ public class ItemDescriptionFragment extends Fragment { } } - private AsyncTask createLoader() { - return new AsyncTask() { - @Override - protected void onCancelled() { - super.onCancelled(); - webViewLoader = null; - } + private void load() { + Log.d(TAG, "load()"); + if(webViewLoader != null) { + webViewLoader.unsubscribe(); + } + if(shownotesProvider == null) { + return; + } + webViewLoader = Observable.defer(() -> Observable.just(loadData())) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(data -> { + webvData = data; + webvDescription.loadDataWithBaseURL(null, data, "text/html", + "utf-8", "about:blank"); + Log.d(TAG, "Webview loaded"); + }, error -> { + Log.e(TAG, Log.getStackTraceString(error)); + }); + } - String data; - - @Override - protected void onPostExecute(Void result) { - super.onPostExecute(result); - // /webvDescription.loadData(url, "text/html", "utf-8"); - webvDescription.loadDataWithBaseURL(null, data, "text/html", - "utf-8", "about:blank"); - Log.d(TAG, "Webview loaded"); - webViewLoader = null; - } - - @Override - protected void onPreExecute() { - super.onPreExecute(); - } - - @Override - protected Void doInBackground(Void... params) { - Log.d(TAG, "Loading Webview"); - try { - Activity activity = getActivity(); - if (activity != null) { - Timeline timeline = new Timeline(activity, shownotesProvider); - data = timeline.processShownotes(highlightTimecodes); - } else { - cancel(true); - } - } catch (Exception e) { - e.printStackTrace(); - } - return null; - } - - }; + private String loadData() { + Timeline timeline = new Timeline(getActivity(), shownotesProvider); + String data = timeline.processShownotes(highlightTimecodes); + return data; } @Override @@ -410,7 +348,7 @@ public class ItemDescriptionFragment extends Fragment { } private boolean restoreFromPreference() { - if (saveState) { + if (!saveState) { Log.d(TAG, "Restoring from preferences"); Activity activity = getActivity(); if (activity != null) { @@ -433,15 +371,19 @@ public class ItemDescriptionFragment extends Fragment { private void onTimecodeLinkSelected(String link) { int time = Timeline.getTimecodeLinkTime(link); - if (getActivity() != null && getActivity() instanceof ItemDescriptionFragmentCallback) { - PlaybackController pc = ((ItemDescriptionFragmentCallback) getActivity()).getPlaybackController(); + if (getActivity() != null && getActivity() instanceof AudioplayerActivity) { + PlaybackController pc = ((AudioplayerActivity) getActivity()).getPlaybackController(); if (pc != null) { pc.seekTo(time); } } } - public interface ItemDescriptionFragmentCallback { - public PlaybackController getPlaybackController(); + @Override + public void onDataSetChanged(Playable media) { + this.media = media; + this.shownotesProvider = media; + load(); } + } diff --git a/app/src/main/res/layout/audio_controls.xml b/app/src/main/res/layout/audio_controls.xml new file mode 100644 index 000000000..7532722dd --- /dev/null +++ b/app/src/main/res/layout/audio_controls.xml @@ -0,0 +1,146 @@ + + + + + + + + + + + + + +