Audio player redesign

Sleep timer and audio effects (e.g. balance) accessible from actionbar, fragment pager
This commit is contained in:
Martin Fietz 2015-11-02 15:24:18 +01:00
parent ce29d9f669
commit 99d7992dc8
90 changed files with 1601 additions and 2699 deletions

View File

@ -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"

View File

@ -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;

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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<SimpleChapter> adapter = (ArrayAdapter<SimpleChapter>) 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<String> 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;
}
}
}

View File

@ -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();
}
});
}
}
}
}

View File

@ -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<Chapter> {
private static final String TAG = "ChapterListAdapter";
private List<Chapter> chapters;
private Playable media;
private int defaultTextColor;
private final Callback callback;
public ChapterListAdapter(Context context, int textViewResourceId,
List<Chapter> 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);
}
}

View File

@ -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<Chapter> {
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);
}
}

View File

@ -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<String> 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<String> 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();
}
}

View File

@ -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);
}
}
}

View File

@ -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();
}
}

View File

@ -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() {
}
};
}

View File

@ -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<Void, Void, Void> 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<Void, Void, FeedItem> itemLoadTask = new AsyncTask<Void, Void, FeedItem>() {
@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<Void, Void, Void> createLoader() {
return new AsyncTask<Void, Void, Void>() {
@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();
}
}

View File

@ -0,0 +1,146 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:layout_marginLeft="24dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
android:text="@string/playback_speed"
android:textStyle="bold"/>
<TextView
android:id="@+id/txtvPlaybackSpeed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:text="1.00x"/>
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="-12dp"
android:layout_marginLeft="24dp"
android:layout_marginRight="24dp">
<Button
android:id="@+id/butDecSpeed"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_alignParentLeft="true"
android:gravity="center"
android:text="-"
android:textStyle="bold"
android:textColor="@color/status_progress"
android:textSize="24sp"
android:background="@drawable/borderless_button_dark"/>
<Button
android:id="@+id/butIncSpeed"
android:layout_width="32dp"
android:layout_height="32dp"
android:minWidth="0dp"
android:layout_alignParentRight="true"
android:gravity="center"
android:text="+"
android:textStyle="bold"
android:textColor="@color/status_progress"
android:textSize="24sp"
android:background="@drawable/borderless_button_dark"/>
<SeekBar
android:id="@+id/playback_speed"
android:layout_width="match_parent"
android:layout_height="32dp"
android:layout_toRightOf="@id/butDecSpeed"
android:layout_toLeftOf="@id/butIncSpeed"
android:layout_centerVertical="true"
android:max="40"/>
</RelativeLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginLeft="24dp"
style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
android:text="@string/volume"
android:textStyle="bold"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="-12dp"
android:layout_marginLeft="24dp"
android:layout_marginRight="24dp"
android:orientation="horizontal"
android:gravity="center">
<TextView
android:id="@+id/txtvLeft"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/left_short" />
<SeekBar
android:id="@+id/volume_left"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="100" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginLeft="24dp"
android:layout_marginRight="24dp"
android:orientation="horizontal"
android:gravity="center">
<TextView
android:id="@+id/txtvRight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/right_short" />
<SeekBar
android:id="@+id/volume_right"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="100"/>
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:layout_marginLeft="24dp"
style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
android:text="@string/audio_effects"
android:textStyle="bold"/>
<CheckBox
android:id="@+id/stereo_to_mono"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="-12dp"
android:layout_marginLeft="24dp"
android:layout_marginRight="24dp"
android:text="@string/stereo_to_mono" />
</LinearLayout>

View File

@ -1,219 +1,219 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:background="@android:color/holo_red_dark">
<LinearLayout
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="@+id/appBar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
tools:background="@android:color/darker_gray">
tools:background="@android:color/darker_gray"/>
<LinearLayout
<com.viewpagerindicator.CirclePageIndicator
android:id="@+id/page_indicator"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:paddingLeft="8dp"
android:paddingRight="8dp">
android:layout_marginTop="-12dp"
android:layout_marginBottom="4dp"
android:background="@android:color/transparent"
app:fillColor="?android:attr/textColorSecondary"
app:strokeColor="?android:attr/textColorSecondary"
app:radius="4dp" />
<TextView
android:id="@+id/txtvTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:ellipsize="end"
android:gravity="left"
android:maxLines="2"
android:textColor="?android:attr/textColorPrimary"
android:textSize="16sp"
tools:text="Audio title"
tools:background="@android:color/holo_green_dark" />
</android.support.design.widget.AppBarLayout>
<ImageButton
android:id="@+id/butCover"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="center_vertical"
android:layout_marginLeft="8dp"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/show_cover_label"
android:gravity="right"
tools:src="@drawable/ic_stat_antenna_default"
tools:background="@android:color/holo_green_dark" />
</LinearLayout>
</android.support.v7.widget.Toolbar>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/content"
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/playtime_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/non_transparent_background"
android:foreground="?android:windowContentOverlay"
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:layout_alignParentBottom="true"
android:background="?attr/overlay_drawable"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/txtvPosition"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginLeft="8dp"
android:text="@string/position_default_label"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_micro"
tools:background="@android:color/holo_green_dark" />
<TextView
android:id="@+id/txtvLength"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="8dp"
android:text="@string/position_default_label"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_micro"
tools:background="@android:color/holo_green_dark" />
<SeekBar
android:id="@+id/sbPosition"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_toLeftOf="@id/txtvLength"
android:layout_toRightOf="@id/txtvPosition"
android:max="500"
tools:background="@android:color/holo_green_dark" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/player_control"
android:layout_width="match_parent"
android:layout_height="@dimen/audioplayer_playercontrols_length"
android:layout_alignParentBottom="true"
android:layout_height="wrap_content"
android:paddingTop="4dp"
android:paddingBottom="8dp"
android:background="?attr/overlay_background"
tools:background="@android:color/holo_purple">
<ImageButton
android:id="@+id/butPlay"
android:layout_width="@dimen/audioplayer_playercontrols_length"
android:layout_height="match_parent"
android:layout_height="@dimen/audioplayer_playercontrols_length"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_centerHorizontal="true"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/pause_label"
android:src="?attr/av_pause"
android:scaleType="fitCenter"
tools:src="@drawable/ic_pause_white_36dp"
tools:background="@android:color/holo_green_dark" />
<ImageButton
android:id="@+id/butRev"
android:layout_width="@dimen/audioplayer_playercontrols_length"
android:layout_height="match_parent"
android:layout_height="@dimen/audioplayer_playercontrols_length"
android:layout_toLeftOf="@id/butPlay"
android:layout_marginLeft="16dp"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/rewind_label"
android:src="?attr/av_rew_big"
android:scaleType="fitCenter"
tools:src="@drawable/ic_fast_rewind_white_36dp"
tools:background="@android:color/holo_blue_dark" />
<TextView
android:id="@+id/txtvRev"
android:layout_width="wrap_content"
android:layout_height="32dp"
android:layout_alignTop="@id/butRev"
android:layout_height="wrap_content"
android:layout_below="@id/butRev"
android:layout_alignLeft="@id/butRev"
android:layout_alignRight="@id/butRev"
android:layout_marginTop="-8dp"
android:gravity="center"
android:text="30"
android:textSize="8dp"
android:textSize="10sp"
android:textColor="?android:attr/textColorSecondary"
android:clickable="false"/>
<Button
android:id="@+id/butPlaybackSpeed"
android:layout_width="@dimen/audioplayer_playercontrols_length"
android:layout_height="@dimen/audioplayer_playercontrols_length"
android:layout_toLeftOf="@id/butRev"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/set_playback_speed_label"
android:src="?attr/av_fast_forward"
android:textSize="@dimen/text_size_medium"
android:textAllCaps="false"
tools:background="@android:color/holo_green_dark" />
<ImageButton
android:id="@+id/butFF"
android:layout_width="@dimen/audioplayer_playercontrols_length"
android:layout_height="match_parent"
android:layout_height="@dimen/audioplayer_playercontrols_length"
android:layout_toRightOf="@id/butPlay"
android:layout_marginRight="16dp"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/fast_forward_label"
android:src="?attr/av_ff_big"
android:scaleType="fitCenter"
tools:src="@drawable/ic_fast_forward_white_36dp"
tools:background="@android:color/holo_blue_dark" />
<TextView
android:id="@+id/txtvFF"
android:layout_width="wrap_content"
android:layout_height="32dp"
android:layout_alignTop="@id/butFF"
android:layout_height="wrap_content"
android:layout_below="@id/butFF"
android:layout_alignLeft="@id/butFF"
android:layout_alignRight="@id/butFF"
android:layout_marginTop="-8dp"
android:gravity="center"
android:text="30"
android:textSize="8dp"
android:textSize="10sp"
android:textColor="?android:attr/textColorSecondary"
android:clickable="false"/>
<Button
android:id="@+id/butPlaybackSpeed"
<ImageButton
android:id="@+id/butSkip"
android:layout_width="@dimen/audioplayer_playercontrols_length"
android:layout_height="match_parent"
android:layout_height="@dimen/audioplayer_playercontrols_length"
android:layout_toRightOf="@id/butFF"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/set_playback_speed_label"
android:src="?attr/av_fast_forward"
android:textSize="@dimen/text_size_medium"
android:visibility="gone"
tools:background="@android:color/holo_green_dark" />
<ImageButton
android:id="@+id/butNavChaptersShownotes"
android:layout_width="@dimen/audioplayer_playercontrols_length"
android:layout_height="match_parent"
android:layout_toLeftOf="@id/butRev"
android:background="?attr/selectableItemBackground"
android:scaleType="centerInside"
android:src="@drawable/ic_toc_white_36dp"
android:scaleType="fitCenter"
android:src="?attr/av_skip_big"
android:contentDescription="@string/skip_episode_label"
tools:src="@drawable/ic_skip_white_36dp"
tools:background="@android:color/holo_green_dark" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/playtime_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@id/player_control"
android:layout_alignParentLeft="true"
android:background="?attr/overlay_drawable">
</LinearLayout>
<TextView
android:id="@+id/txtvPosition"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginLeft="8dp"
android:layout_marginTop="16dp"
android:text="@string/position_default_label"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_micro"
tools:background="@android:color/holo_green_dark" />
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_above="@id/playtime_layout"
android:layout_below="@id/appBar"
android:foreground="?android:windowContentOverlay"
tools:background="@android:color/holo_orange_light" />
<TextView
android:id="@+id/txtvLength"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_centerVertical="true"
android:layout_marginRight="8dp"
android:layout_marginTop="16dp"
android:text="@string/position_default_label"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_micro"
tools:background="@android:color/holo_green_dark" />
<View
android:id="@+id/shadow"
android:layout_width="match_parent"
android:layout_height="4dp"
android:layout_below="@id/appBar"
android:background="@drawable/shadow" />
<SeekBar
android:id="@+id/sbPosition"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="16dp"
android:layout_toLeftOf="@id/txtvLength"
android:layout_toRightOf="@id/txtvPosition"
android:max="500"
tools:background="@android:color/holo_green_dark" />
</RelativeLayout>
<FrameLayout
android:id="@+id/contentView"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_above="@id/playtime_layout"
android:layout_alignParentTop="true"
android:foreground="?android:windowContentOverlay"
tools:background="@android:color/holo_orange_light" />
</RelativeLayout>
</LinearLayout>
</RelativeLayout>
<include layout="@layout/nav_list" />

View File

@ -1,45 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="@+id/card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="16dp"
card_view:cardElevation="12dp"
card_view:cardCornerRadius="4dp"
card_view:cardUseCompatPadding="true"
card_view:cardBackgroundColor="?attr/overlay_background">
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center">
<TextView
android:id="@+id/txtvSelectedSpeed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textSize="22sp"
android:textStyle="bold"/>
<SeekBar
android:id="@+id/sbSelectSpeed"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"/>
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>

View File

@ -1,19 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<android.support.percent.PercentRelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/cover_fragment_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
android:background="@color/actionbar_gray">
<ImageView
android:id="@+id/imgvCover"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_centerInParent="true"
android:contentDescription="@string/cover_label"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
app:layout_aspectRatio="100%"
app:layout_widthPercent="82%"
tools:src="@android:drawable/sym_def_app_icon" />
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:layout_above="@id/imgvCover">
<TextView
android:id="@+id/txtvPodcastTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:gravity="center"
android:maxLines="2"
android:ellipsize="end"
android:text="Podcast" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:layout_below="@id/imgvCover">
<TextView
android:id="@+id/txtvEpisodeTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:gravity="center"
android:maxLines="2"
android:ellipsize="end"
android:text="Episode" />
</LinearLayout>
</android.support.percent.PercentRelativeLayout>

View File

@ -2,23 +2,36 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/add_to_favorites_item"
android:icon="?attr/ic_fav"
android:title="@string/add_to_favorite_label"
custom:showAsAction="always">
</item>
<item
android:id="@+id/remove_from_favorites_item"
android:icon="?attr/ic_unfav"
android:title="@string/remove_from_favorite_label"
custom:showAsAction="always">
</item>
<item
android:id="@+id/disable_sleeptimer_item"
android:icon="?attr/device_access_time"
android:icon="?attr/ic_sleep_off"
custom:showAsAction="always"
android:title="@string/sleep_timer_label">
</item>
<item
android:id="@+id/set_sleeptimer_item"
custom:showAsAction="collapseActionView"
android:icon="?attr/ic_sleep"
custom:showAsAction="always"
android:title="@string/set_sleeptimer_label">
</item>
<item
android:id="@+id/skip_episode_item"
custom:showAsAction="collapseActionView"
android:title="@string/skip_episode_label"
android:visible="true">
android:id="@+id/audio_controls"
android:title="@string/audio_controls"
custom:showAsAction="always">
</item>
<item
@ -30,7 +43,9 @@
</item>
<item
android:id="@+id/share_item"
android:icon="?attr/social_share"
android:menuCategory="container"
custom:showAsAction="ifRoom"
android:title="@string/share_label">
<menu>
<item

View File

@ -59,13 +59,13 @@
<h1>Used libraries</h1>
<h2>Apache Commons <a href="http://commons.apache.org/">(Link)</a></h2>
by The Apache Software Foundation, licensed under the Apache 2.0 license <a href="LICENSE_APACHE_COMMONS.txt">(View)</a>
by The Apache Software Foundation, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
<h2>EventBus <a href="https://github.com/greenrobot/EventBus">(Link)</a></h2>
by greenrobot, licensed under the Apache 2.0 license <a href="LICENSE_EVENTBUS.txt">(View)</a>
by greenrobot, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
<h2>flattr4j <a href="http://www.shredzone.org/projects/flattr4j/wiki">(Link)</a></h2>
licensed under the Apache 2.0 license <a href="LICENSE_FLATTR4J.txt">(View)</a>
licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
<h2>Glide <a href="https://github.com/bumptech/glide/">(Link)</a></h2>
licensed under the Simplified BSD license <a href="LICENSE_GLIDE.txt">(View)</a>
@ -86,22 +86,22 @@ by Aidan Michael Follestad, licensed under the MIT License <a href="LICENSE_MATE
by Square, licensed under the Apache 2.0 license <a href="LICENSE_OKHTTP.txt">(View)</a>
<h2>Okio <a href="https://github.com/square/okio">(Link)</a></h2>
by Square, licensed under the Apache 2.0 license <a href="LICENSE_OKIO.txt">(View)</a>
by Square, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
<h2>Presto Client <a href="http://www.aocate.com/presto/">(Link)</a></h2>
licensed under the Apache 2.0 license <a href="LICENSE_PRESTO.txt">(View)</a>
<h2>RecyclerView-FlexibleDivider <a href="https://github.com/yqritc/RecyclerView-FlexibleDivider">(Link)</a></h2>
licensed under the Apache 2.0 license <a href="LICENSE_FLEXIBLE_DIVIDER.txt">(View)</a>
licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
<h2>RxAndroid <a href="https://github.com/ReactiveX/RxAndroid">(Link)</a></h2>
licensed under the Apache 2.0 license <a href="LICENSE_RX_ANDROID.txt">(View)</a>
licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
<h2>StackBlur <a href="https://github.com/kikoso/android-stackblur">(Link)</a></h2>
by Enrique L&oacute;pez Ma&ntilde;as, licensed under the Apache 2.0 license <a href="LICENSE_STACKBLUR.txt">(View)</a>
by Enrique L&oacute;pez Ma&ntilde;as, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
<h2>AntennaPod-AudioPlayer <a href="https://github.com/AntennaPod/AntennaPod-AudioPlayer/">(Link)</a></h2>
by the AntennaPod team, licensed under the Apache 2.0 license <a href="LICENSE_ANTENNAPOD_AUDIOPLAYER.txt">(View)</a>
by the AntennaPod team, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
</body>
</html>

View File

@ -50,14 +50,14 @@ project.ext {
jsoupVersion = "1.7.3"
iconifyFontawesomeVersion = "2.1.1"
materialDialogsVersion = "0.8.5.3@aar"
okhttpVersion = "2.7.2"
okioVersion = "1.6.0"
recyclerviewFlexibledividerVersion = "1.2.6"
rxAndroidVersion = "1.1.0"
rxJavaVersion = "1.1.0"
rxJavaRulesVersion = "1.1.0.0"
okhttpVersion = "2.7.2"
okioVersion = "1.6.0"
audioPlayerVersion = "v1.0.8"
audioPlayerVersion = "v1.0.9"
}
task wrapper(type: Wrapper) {

View File

@ -40,7 +40,6 @@ repositories {
dependencies {
compile "com.android.support:support-v4:$supportVersion"
compile "com.android.support:appcompat-v7:$supportVersion"
compile "com.android.support:design:$supportVersion"
compile "org.apache.commons:commons-lang3:$commonslangVersion"
compile ("org.shredzone.flattr4j:flattr4j-core:$flattr4jVersion") {
exclude group: "org.json", module: "json"

View File

@ -20,7 +20,6 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;
@ -95,9 +94,12 @@ public class UserPreferences {
public static final String PREF_QUEUE_LOCKED = "prefQueueLocked";
public static final String IMAGE_CACHE_DEFAULT_VALUE = "100";
public static final int IMAGE_CACHE_SIZE_MINIMUM = 20;
public static final String PREF_LEFT_VOLUME = "prefLeftVolume";
public static final String PREF_RIGHT_VOLUME = "prefRightVolume";
// Experimental
public static final String PREF_SONIC = "prefSonic";
public static final String PREF_STEREO_TO_MONO = "PrefStereoToMono";
public static final String PREF_NORMALIZER = "prefNormalizer";
public static final int EPISODE_CLEANUP_QUEUE = -1;
public static final int EPISODE_CLEANUP_NULL = -2;
@ -249,17 +251,37 @@ public class UserPreferences {
}
public static String getPlaybackSpeed() {
return prefs.getString(PREF_PLAYBACK_SPEED, "1.0");
return prefs.getString(PREF_PLAYBACK_SPEED, "1.00");
}
public static String[] getPlaybackSpeedArray() {
return readPlaybackSpeedArray(prefs.getString(PREF_PLAYBACK_SPEED_ARRAY, null));
}
public static float getLeftVolume() {
int volume = prefs.getInt(PREF_LEFT_VOLUME, 100);
if(volume == 100) {
return 1.0f;
} else {
return (float) (1 - (Math.log(100 - volume) / Math.log(100)));
}
}
public static float getRightVolume() {
int volume = prefs.getInt(PREF_RIGHT_VOLUME, 100);
if(volume == 100) {
return 1.0f;
} else {
return (float) (1 - (Math.log(100 - volume) / Math.log(100)));
}
}
public static boolean shouldPauseForFocusLoss() {
return prefs.getBoolean(PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS, false);
}
public static long getUpdateInterval() {
String updateInterval = prefs.getString(PREF_UPDATE_INTERVAL, "0");
if(false == updateInterval.contains(":")) {
@ -385,6 +407,15 @@ public class UserPreferences {
.apply();
}
public static void setVolume(int leftVolume, int rightVolume) {
assert(0 <= leftVolume && leftVolume <= 100);
assert(0 <= rightVolume && rightVolume <= 100);
prefs.edit()
.putInt(PREF_LEFT_VOLUME, leftVolume)
.putInt(PREF_RIGHT_VOLUME, rightVolume)
.apply();
}
public static void setAutodownloadSelectedNetworks(String[] value) {
prefs.edit()
.putString(PREF_AUTODL_SELECTED_NETWORKS, TextUtils.join(",", value))
@ -472,7 +503,7 @@ public class UserPreferences {
// If this preference hasn't been set yet, return the default options
if (valueFromPrefs == null) {
String[] allSpeeds = context.getResources().getStringArray(R.array.playback_speed_values);
List<String> speedList = new LinkedList<String>();
List<String> speedList = new ArrayList<>();
for (String speedStr : allSpeeds) {
float speed = Float.parseFloat(speedStr);
if (speed < 2.0001 && speed * 10 % 1 == 0) {
@ -505,6 +536,16 @@ public class UserPreferences {
.apply();
}
public static boolean stereoToMono() {
return prefs.getBoolean(PREF_STEREO_TO_MONO, false);
}
public static void stereoToMono(boolean enable) {
prefs.edit()
.putBoolean(PREF_STEREO_TO_MONO, enable)
.apply();
}
public static EpisodeCleanupAlgorithm getEpisodeCleanupAlgorithm() {
int cleanupValue = Integer.valueOf(prefs.getString(PREF_EPISODE_CLEANUP, "-1"));

View File

@ -398,19 +398,25 @@ public class PlaybackService extends Service {
@Override
public void onSleepTimerAlmostExpired() {
mediaPlayer.setVolume(0.1f);
float leftVolume = 0.1f * UserPreferences.getLeftVolume();
float rightVolume = 0.1f * UserPreferences.getRightVolume();
mediaPlayer.setVolume(leftVolume, rightVolume);
}
@Override
public void onSleepTimerExpired() {
mediaPlayer.pause(true, true);
mediaPlayer.setVolume(1.0f);
float leftVolume = UserPreferences.getLeftVolume();
float rightVolume = UserPreferences.getRightVolume();
mediaPlayer.setVolume(leftVolume, rightVolume);
sendNotificationBroadcast(NOTIFICATION_TYPE_SLEEPTIMER_UPDATE, 0);
}
@Override
public void onSleepTimerReset() {
mediaPlayer.setVolume(1.0f);
float leftVolume = UserPreferences.getLeftVolume();
float rightVolume = UserPreferences.getRightVolume();
mediaPlayer.setVolume(leftVolume, rightVolume);
}
@Override
@ -1165,18 +1171,30 @@ public class PlaybackService extends Service {
public Playable getPlayable() { return mediaPlayer.getPlayable(); }
public void setSpeed(float speed) {
mediaPlayer.setSpeed(speed);
}
public boolean canSetSpeed() {
return mediaPlayer.canSetSpeed();
}
public void setSpeed(float speed) {
mediaPlayer.setSpeed(speed);
}
public void setVolume(float leftVolume, float rightVolume) {
mediaPlayer.setVolume(leftVolume, rightVolume);
}
public float getCurrentPlaybackSpeed() {
return mediaPlayer.getPlaybackSpeed();
}
public boolean canDownmix() {
return mediaPlayer.canDownmix();
}
public void setDownmix(boolean enable) {
mediaPlayer.setDownmix(enable);
}
public boolean isStartWhenPrepared() {
return mediaPlayer.isStartWhenPrepared();
}

View File

@ -24,6 +24,7 @@ import android.view.SurfaceHolder;
import android.view.WindowManager;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.target.Target;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
@ -293,6 +294,7 @@ public class PlaybackServiceMediaPlayer implements SharedPreferences.OnSharedPre
builder.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, p.getDuration());
builder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, p.getEpisodeTitle());
builder.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, p.getFeedTitle());
if (p.getImageUri() != null && UserPreferences.setLockscreenBackground()) {
builder.putString(MediaMetadataCompat.METADATA_KEY_ART_URI, p.getImageUri().toString());
try {
@ -322,13 +324,10 @@ public class PlaybackServiceMediaPlayer implements SharedPreferences.OnSharedPre
* This method is executed on an internal executor service.
*/
public void resume() {
executor.submit(new Runnable() {
@Override
public void run() {
playerLock.lock();
resumeSync();
playerLock.unlock();
}
executor.submit(() -> {
playerLock.lock();
resumeSync();
playerLock.unlock();
});
}
@ -339,7 +338,15 @@ public class PlaybackServiceMediaPlayer implements SharedPreferences.OnSharedPre
AudioManager.AUDIOFOCUS_GAIN);
if (focusGained == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
acquireWifiLockIfNecessary();
setSpeed(Float.parseFloat(UserPreferences.getPlaybackSpeed()));
float speed = 1.0f;
try {
speed = Float.parseFloat(UserPreferences.getPlaybackSpeed());
} catch(NumberFormatException e) {
Log.e(TAG, Log.getStackTraceString(e));
UserPreferences.setPlaybackSpeed(String.valueOf(speed));
}
setSpeed(speed);
setVolume(UserPreferences.getLeftVolume(), UserPreferences.getRightVolume());
if (playerStatus == PlayerStatus.PREPARED && media.getPosition() > 0) {
int newPosition = RewindAfterPauseUtils.calculatePositionWithRewind(
@ -690,24 +697,39 @@ public class PlaybackServiceMediaPlayer implements SharedPreferences.OnSharedPre
* Sets the playback speed.
* This method is executed on an internal executor service.
*/
public void setVolume(final float volume) {
executor.submit(new Runnable() {
@Override
public void run() {
setVolumeSync(volume);
}
});
public void setVolume(final float volumeLeft, float volumeRight) {
executor.submit(() -> setVolumeSync(volumeLeft, volumeRight));
}
/**
* Sets the playback speed.
* This method is executed on the caller's thread.
*/
private void setVolumeSync(float volume) {
private void setVolumeSync(float volumeLeft, float volumeRight) {
playerLock.lock();
if (media != null && media.getMediaType() == MediaType.AUDIO) {
mediaPlayer.setVolume(volume, volume);
Log.d(TAG, "Media player volume was set to " + volume);
mediaPlayer.setVolume(volumeLeft, volumeRight);
Log.d(TAG, "Media player volume was set to " + volumeLeft + " " + volumeRight);
}
playerLock.unlock();
}
/**
* Returns true if the mediaplayer can mix stereo down to mono
*/
public boolean canDownmix() {
boolean retVal = false;
if (mediaPlayer != null && media != null && media.getMediaType() == MediaType.AUDIO) {
retVal = mediaPlayer.canDownmix();
}
return retVal;
}
public void setDownmix(boolean enable) {
playerLock.lock();
if (media != null && media.getMediaType() == MediaType.AUDIO) {
mediaPlayer.setDownmix(enable);
Log.d(TAG, "Media player downmix was set to " + enable);
}
playerLock.unlock();
}

View File

@ -37,4 +37,9 @@ public class AudioPlayer extends MediaPlayer implements IPlayer {
protected boolean useSonic() {
return UserPreferences.useSonic();
}
@Override
protected boolean downmix() {
return UserPreferences.stereoToMono();
}
}

View File

@ -10,6 +10,8 @@ public interface IPlayer {
boolean canSetSpeed();
boolean canDownmix();
float getCurrentPitchStepsAdjustment();
int getCurrentPosition();
@ -57,6 +59,8 @@ public interface IPlayer {
void setPlaybackSpeed(float f);
void setDownmix(boolean enable);
void setVolume(float left, float right);
void start();

View File

@ -27,7 +27,6 @@ import android.widget.TextView;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@ -48,6 +47,7 @@ import de.danoeh.antennapod.core.util.playback.Playable.PlayableUtils;
* control playback instead of communicating with the PlaybackService directly.
*/
public abstract class PlaybackController {
private static final String TAG = "PlaybackController";
public static final int INVALID_TIME = -1;
@ -78,23 +78,18 @@ public abstract class PlaybackController {
this.activity = activity;
this.reinitOnPause = reinitOnPause;
schedExecutor = new ScheduledThreadPoolExecutor(SCHED_EX_POOLSIZE,
new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setPriority(Thread.MIN_PRIORITY);
return t;
}
}, new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r,
ThreadPoolExecutor executor) {
Log.w(TAG,
r -> {
Thread t = new Thread(r);
t.setPriority(Thread.MIN_PRIORITY);
return t;
}, new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r,
ThreadPoolExecutor executor) {
Log.w(TAG,
"Rejected execution of runnable in schedExecutor");
}
}
}
);
}
@ -104,10 +99,10 @@ public abstract class PlaybackController {
*/
public void init() {
activity.registerReceiver(statusUpdate, new IntentFilter(
PlaybackService.ACTION_PLAYER_STATUS_CHANGED));
PlaybackService.ACTION_PLAYER_STATUS_CHANGED));
activity.registerReceiver(notificationReceiver, new IntentFilter(
PlaybackService.ACTION_PLAYER_NOTIFICATION));
PlaybackService.ACTION_PLAYER_NOTIFICATION));
activity.registerReceiver(shutdownReceiver, new IntentFilter(
PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE));
@ -239,7 +234,7 @@ public abstract class PlaybackController {
return null;
}
public abstract void setupGUI();
private void setupPositionObserver() {
if ((positionObserverFuture != null && positionObserverFuture
@ -263,8 +258,6 @@ public abstract class PlaybackController {
}
}
public abstract void onPositionObserverUpdate();
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
playbackService = ((PlaybackService.LocalBinder) service)
@ -367,26 +360,31 @@ public abstract class PlaybackController {
}
};
public abstract void onPlaybackSpeedChange();
public void setupGUI() {};
public abstract void onShutdownNotification();
public void onPositionObserverUpdate() {};
public void onPlaybackSpeedChange() {};
public void onShutdownNotification() {};
/**
* Called when the currently displayed information should be refreshed.
*/
public abstract void onReloadNotification(int code);
public void onReloadNotification(int code) {};
public abstract void onBufferStart();
public void onBufferStart() {};
public abstract void onBufferEnd();
public void onBufferEnd() {};
public abstract void onBufferUpdate(float progress);
public void onBufferUpdate(float progress) {};
public abstract void onSleepTimerUpdate();
public void onSleepTimerUpdate() {};
public abstract void handleError(int code);
public void handleError(int code) {};
public abstract void onPlaybackEnd();
public void onPlaybackEnd() {};
public void repeatHandleStatus() {
if (status != null && playbackService != null) {
@ -484,15 +482,19 @@ public abstract class PlaybackController {
}
}
public abstract ImageButton getPlayButton();
public ImageButton getPlayButton() {
return null;
};
public abstract void postStatusMsg(int msg);
public void postStatusMsg(int msg) {};
public abstract void clearStatusMsg();
public void clearStatusMsg() {};
public abstract boolean loadMediaInfo();
public boolean loadMediaInfo() {
return false;
};
public abstract void onAwaitingVideoSurface();
public void onAwaitingVideoSurface() {};
/**
* Called when connection to playback service has been established or
@ -526,7 +528,7 @@ public abstract class PlaybackController {
}
}
public abstract void onServiceQueried();
public void onServiceQueried() {};
/**
* Should be used by classes which implement the OnSeekBarChanged interface.
@ -691,6 +693,12 @@ public abstract class PlaybackController {
}
}
public void setVolume(float leftVolume, float rightVolume) {
if (playbackService != null) {
playbackService.setVolume(leftVolume, rightVolume);
}
}
public float getCurrentPlaybackSpeedMultiplier() {
if (canSetPlaybackSpeed()) {
return playbackService.getCurrentPlaybackSpeed();
@ -699,6 +707,16 @@ public abstract class PlaybackController {
}
}
public boolean canDownmix() {
return playbackService != null && playbackService.canDownmix();
}
public void setDownmix(boolean enable) {
if(playbackService != null) {
playbackService.setDownmix(enable);
}
}
public boolean isPlayingVideo() {
if (playbackService != null) {
return PlaybackService.getCurrentMediaType() == MediaType.VIDEO;

View File

@ -88,7 +88,7 @@ public class Timeline {
}
// replace ASCII line breaks with HTML ones if shownotes don't contain HTML line breaks already
if(!LINE_BREAK_REGEX.matcher(shownotes).find()) {
if(!LINE_BREAK_REGEX.matcher(shownotes).find() && !shownotes.contains("<p>")) {
shownotes = shownotes.replace("\n", "<br />");
}

View File

@ -16,6 +16,11 @@ public class VideoPlayer extends MediaPlayer implements IPlayer {
return false;
}
@Override
public boolean canDownmix() {
return false;
}
@Override
public float getCurrentPitchStepsAdjustment() {
return 1;
@ -60,6 +65,12 @@ public class VideoPlayer extends MediaPlayer implements IPlayer {
throw new UnsupportedOperationException("Setting playback speed unsupported in video player");
}
@Override
public void setDownmix(boolean b) {
Log.e(TAG, "Setting downmix unsupported in video player");
throw new UnsupportedOperationException("Setting downmix unsupported in video player");
}
@Override
public void setVideoScalingMode(int mode) {
super.setVideoScalingMode(mode);

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 659 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 431 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 637 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 637 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 472 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 410 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 410 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 494 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 769 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 828 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 821 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 593 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 533 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 543 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 737 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 664 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 877 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 870 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 737 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 749 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 955 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 882 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 647 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -75,15 +75,15 @@
</string-array>
<string-array name="playback_speed_values">
<item>0.5</item>
<item>0.6</item>
<item>0.7</item>
<item>0.50</item>
<item>0.60</item>
<item>0.70</item>
<item>0.75</item>
<item>0.8</item>
<item>0.80</item>
<item>0.85</item>
<item>0.9</item>
<item>0.90</item>
<item>0.95</item>
<item>1.0</item>
<item>1.00</item>
<item>1.05</item>
<item>1.10</item>
<item>1.15</item>

View File

@ -13,7 +13,6 @@
<attr name="content_discard" format="reference"/>
<attr name="content_new" format="reference"/>
<attr name="feed" format="reference"/>
<attr name="device_access_time" format="reference"/>
<attr name="location_web_site" format="reference"/>
<attr name="navigation_accept" format="reference"/>
<attr name="navigation_cancel" format="reference"/>
@ -37,15 +36,20 @@
<attr name="av_pause_big" format="reference"/>
<attr name="av_ff_big" format="reference"/>
<attr name="av_rew_big" format="reference"/>
<attr name="av_skip_big" format="reference"/>
<attr name="ic_settings" format="reference"/>
<attr name="ic_lock_open" format="reference"/>
<attr name="ic_lock_closed" format="reference"/>
<attr name="ic_filter" format="reference"/>
<attr name="progressBarTheme" format="reference"/>
<attr name="ic_fav" format="reference"/>
<attr name="ic_unfav" format="reference"/>
<attr name="ic_sleep" format="reference"/>
<attr name="ic_sleep_off" format="reference"/>
<!-- Used in itemdescription -->
<attr name="non_transparent_background" format="reference"/>
<attr name="overlay_background" format="color"/>
<attr name="nav_drawer_background" format="color"/>
</resources>
</resources>

View File

@ -33,6 +33,6 @@
<dimen name="listitem_icon_leftpadding">16dp</dimen>
<dimen name="listitem_icon_rightpadding">16dp</dimen>
<dimen name="audioplayer_playercontrols_length">64dp</dimen>
<dimen name="audioplayer_playercontrols_length">48dp</dimen>
</resources>
</resources>

View File

@ -148,7 +148,9 @@
<string name="added_to_queue_label">Added to Queue</string>
<string name="remove_from_queue_label">Remove from Queue</string>
<string name="add_to_favorite_label">Add to Favorites</string>
<string name="added_to_favorites">Added to Favorites</string>
<string name="remove_from_favorite_label">Remove from Favorites</string>
<string name="removed_from_favorites">Removed from Favorites</string>
<string name="visit_website_label">Visit Website</string>
<string name="support_label">Flattr this</string>
<string name="enqueue_all_new">Enqueue all</string>
@ -268,6 +270,7 @@
<!-- Empty list labels -->
<string name="no_items_label">There are no items in this list.</string>
<string name="no_feeds_label">You haven\'t subscribed to any feeds yet.</string>
<string name="no_chapters_label">This episode has no chapters.</string>
<!-- Preferences -->
<string name="other_pref">Other</string>
@ -555,4 +558,14 @@
<string name="rating_later_label">Remind me later</string>
<string name="rating_now_label">Sure, let\'s do this!</string>
<!-- Audio controls -->
<string name="audio_controls">Audio controls</string>
<string name="playback_speed">Playback Speed</string>
<string name="volume">Volume</string>
<string name="left_short">L</string>
<string name="right_short">R</string>
<string name="audio_effects">Audio Effects</string>
<string name="stereo_to_mono">Downmix: Stereo to mono</string>
<string name="sonic_only">Sonic only</string>
</resources>

View File

@ -18,7 +18,6 @@
<item name="attr/av_rewind">@drawable/ic_fast_rewind_grey600_24dp</item>
<item name="attr/content_discard">@drawable/ic_delete_grey600_24dp</item>
<item name="attr/content_new">@drawable/ic_add_grey600_24dp</item>
<item name="attr/device_access_time">@drawable/ic_timer_grey600_24dp</item>
<item name="attr/feed">@drawable/ic_feed_grey600_24dp</item>
<item name="attr/location_web_site">@drawable/ic_web_grey600_24dp</item>
<item name="attr/navigation_accept">@drawable/ic_done_grey600_24dp</item>
@ -45,10 +44,16 @@
<item name="attr/av_pause_big">@drawable/ic_pause_grey600_36dp</item>
<item name="attr/av_ff_big">@drawable/ic_fast_forward_grey600_36dp</item>
<item name="attr/av_rew_big">@drawable/ic_fast_rewind_grey600_36dp</item>
<item name="attr/av_skip_big">@drawable/ic_skip_grey600_36dp</item>
<item name="attr/ic_fav">@drawable/ic_star_border_grey600_24dp</item>
<item name="attr/ic_unfav">@drawable/ic_star_grey600_24dp</item>
<item name="attr/ic_settings">@drawable/ic_settings_grey600_24dp</item>
<item name="attr/ic_lock_open">@drawable/ic_lock_open_grey600_24dp</item>
<item name="attr/ic_lock_closed">@drawable/ic_lock_closed_grey600_24dp</item>
<item name="attr/ic_filter">@drawable/ic_filter_grey600_24dp</item>
<item name="attr/ic_sleep">@drawable/ic_sleep_grey600_24dp</item>
<item name="attr/ic_sleep_off">@drawable/ic_sleep_off_grey600_24dp</item>
</style>
<style name="Theme.AntennaPod.Dark" parent="Theme.AppCompat">
@ -67,7 +72,6 @@
<item name="attr/av_rewind">@drawable/ic_fast_rewind_white_24dp</item>
<item name="attr/content_discard">@drawable/ic_delete_white_24dp</item>
<item name="attr/content_new">@drawable/ic_add_white_24dp</item>
<item name="attr/device_access_time">@drawable/ic_timer_white_24dp</item>
<item name="attr/feed">@drawable/ic_feed_white_24dp</item>
<item name="attr/location_web_site">@drawable/ic_web_white_24dp</item>
<item name="attr/navigation_accept">@drawable/ic_done_white_24dp</item>
@ -94,10 +98,15 @@
<item name="attr/av_pause_big">@drawable/ic_pause_white_36dp</item>
<item name="attr/av_ff_big">@drawable/ic_fast_forward_white_36dp</item>
<item name="attr/av_rew_big">@drawable/ic_fast_rewind_white_36dp</item>
<item name="attr/av_skip_big">@drawable/ic_skip_white_36dp</item>
<item name="attr/ic_fav">@drawable/ic_star_border_white_24dp</item>
<item name="attr/ic_unfav">@drawable/ic_star_white_24dp</item>
<item name="attr/ic_settings">@drawable/ic_settings_white_24dp</item>
<item name="attr/ic_lock_open">@drawable/ic_lock_open_white_24dp</item>
<item name="attr/ic_lock_closed">@drawable/ic_lock_closed_white_24dp</item>
<item name="attr/ic_filter">@drawable/ic_filter_white_24dp</item>
<item name="attr/ic_sleep">@drawable/ic_sleep_white_24dp</item>
<item name="attr/ic_sleep_off">@drawable/ic_sleep_off_white_24dp</item>
</style>
<style name="Theme.AntennaPod.Light.NoTitle" parent="Theme.AppCompat.Light.NoActionBar">
@ -118,7 +127,6 @@
<item name="attr/av_rewind">@drawable/ic_fast_rewind_grey600_24dp</item>
<item name="attr/content_discard">@drawable/ic_delete_grey600_24dp</item>
<item name="attr/content_new">@drawable/ic_add_grey600_24dp</item>
<item name="attr/device_access_time">@drawable/ic_timer_grey600_24dp</item>
<item name="attr/feed">@drawable/ic_feed_grey600_24dp</item>
<item name="attr/location_web_site">@drawable/ic_web_grey600_24dp</item>
<item name="attr/navigation_accept">@drawable/ic_done_grey600_24dp</item>
@ -145,10 +153,15 @@
<item name="attr/av_pause_big">@drawable/ic_pause_grey600_36dp</item>
<item name="attr/av_ff_big">@drawable/ic_fast_forward_grey600_36dp</item>
<item name="attr/av_rew_big">@drawable/ic_fast_rewind_grey600_36dp</item>
<item name="attr/av_skip_big">@drawable/ic_skip_grey600_36dp</item>
<item name="attr/ic_fav">@drawable/ic_star_border_grey600_24dp</item>
<item name="attr/ic_unfav">@drawable/ic_star_grey600_24dp</item>
<item name="attr/ic_settings">@drawable/ic_settings_grey600_24dp</item>
<item name="attr/ic_lock_open">@drawable/ic_lock_open_grey600_24dp</item>
<item name="attr/ic_lock_closed">@drawable/ic_lock_closed_grey600_24dp</item>
<item name="attr/ic_filter">@drawable/ic_filter_grey600_24dp</item>
<item name="attr/ic_sleep">@drawable/ic_sleep_grey600_24dp</item>
<item name="attr/ic_sleep_off">@drawable/ic_sleep_off_grey600_24dp</item>
</style>
<style name="Theme.AntennaPod.Dark.NoTitle" parent="Theme.AppCompat.NoActionBar">
@ -168,7 +181,6 @@
<item name="attr/av_rewind">@drawable/ic_fast_rewind_white_24dp</item>
<item name="attr/content_discard">@drawable/ic_delete_white_24dp</item>
<item name="attr/content_new">@drawable/ic_add_white_24dp</item>
<item name="attr/device_access_time">@drawable/ic_timer_white_24dp</item>
<item name="attr/feed">@drawable/ic_feed_white_24dp</item>
<item name="attr/location_web_site">@drawable/ic_web_white_24dp</item>
<item name="attr/navigation_accept">@drawable/ic_done_white_24dp</item>
@ -195,10 +207,15 @@
<item name="attr/av_pause_big">@drawable/ic_pause_white_36dp</item>
<item name="attr/av_ff_big">@drawable/ic_fast_forward_white_36dp</item>
<item name="attr/av_rew_big">@drawable/ic_fast_rewind_white_36dp</item>
<item name="attr/av_skip_big">@drawable/ic_skip_white_36dp</item>
<item name="attr/ic_fav">@drawable/ic_star_border_white_24dp</item>
<item name="attr/ic_unfav">@drawable/ic_star_white_24dp</item>
<item name="attr/ic_settings">@drawable/ic_settings_white_24dp</item>
<item name="attr/ic_lock_open">@drawable/ic_lock_open_white_24dp</item>
<item name="attr/ic_lock_closed">@drawable/ic_lock_closed_white_24dp</item>
<item name="attr/ic_filter">@drawable/ic_filter_white_24dp</item>
<item name="attr/ic_sleep">@drawable/ic_sleep_white_24dp</item>
<item name="attr/ic_sleep_off">@drawable/ic_sleep_off_white_24dp</item>
</style>
<style name="Theme.AntennaPod.VideoPlayer" parent="@style/Theme.AntennaPod.Dark">