Prepare merge.
This commit is contained in:
commit
1bb1f0990f
|
@ -17,6 +17,7 @@ dependencies {
|
|||
compile 'com.squareup.okhttp:okhttp:2.2.0'
|
||||
compile 'com.squareup.okhttp:okhttp-urlconnection:2.2.0'
|
||||
compile 'com.squareup.okio:okio:1.2.0'
|
||||
compile 'de.greenrobot:eventbus:2.4.0'
|
||||
compile project(':core')
|
||||
compile project(':library:drag-sort-listview')
|
||||
}
|
||||
|
|
|
@ -78,4 +78,9 @@
|
|||
public static *** w(...);
|
||||
public static *** d(...);
|
||||
public static *** e(...);
|
||||
}
|
||||
}
|
||||
|
||||
# greenrobot EventBus
|
||||
-keepclassmembers class ** {
|
||||
public void onEvent*(**);
|
||||
}
|
||||
|
|
|
@ -2,12 +2,6 @@ package de.test.antennapod.service.playback;
|
|||
|
||||
import android.content.Context;
|
||||
import android.test.InstrumentationTestCase;
|
||||
import de.danoeh.antennapod.core.feed.EventDistributor;
|
||||
import de.danoeh.antennapod.core.feed.Feed;
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.service.playback.PlaybackServiceTaskManager;
|
||||
import de.danoeh.antennapod.core.storage.PodDBAdapter;
|
||||
import de.danoeh.antennapod.core.util.playback.Playable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
|
@ -15,6 +9,15 @@ import java.util.List;
|
|||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import de.danoeh.antennapod.core.feed.EventDistributor;
|
||||
import de.danoeh.antennapod.core.feed.Feed;
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.feed.QueueEvent;
|
||||
import de.danoeh.antennapod.core.service.playback.PlaybackServiceTaskManager;
|
||||
import de.danoeh.antennapod.core.storage.PodDBAdapter;
|
||||
import de.danoeh.antennapod.core.util.playback.Playable;
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
/**
|
||||
* Test class for PlaybackServiceTaskManager
|
||||
*/
|
||||
|
@ -94,7 +97,7 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
|
|||
};
|
||||
EventDistributor.getInstance().register(queueListener);
|
||||
List<FeedItem> queue = writeTestQueue("a");
|
||||
EventDistributor.getInstance().sendQueueUpdateBroadcast();
|
||||
EventBus.getDefault().post(new QueueEvent(QueueEvent.Action.ADDED_ITEMS, queue));
|
||||
countDownLatch.await(5000, TimeUnit.MILLISECONDS);
|
||||
|
||||
assertNotNull(queue);
|
||||
|
|
|
@ -14,6 +14,7 @@ import de.danoeh.antennapod.core.feed.FeedMedia;
|
|||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.FeedItemStatistics;
|
||||
import de.danoeh.antennapod.core.storage.PodDBAdapter;
|
||||
import de.danoeh.antennapod.core.util.LongList;
|
||||
import de.danoeh.antennapod.core.util.flattr.FlattrStatus;
|
||||
|
||||
import static de.test.antennapod.storage.DBTestUtils.saveFeedlist;
|
||||
|
@ -194,7 +195,7 @@ public class DBReaderTest extends InstrumentationTestCase {
|
|||
final Context context = getInstrumentation().getTargetContext();
|
||||
final int numItems = 10;
|
||||
List<FeedItem> queue = saveQueue(numItems);
|
||||
List<Long> ids = DBReader.getQueueIDList(context);
|
||||
LongList ids = DBReader.getQueueIDList(context);
|
||||
assertNotNull(ids);
|
||||
assertTrue(queue.size() == ids.size());
|
||||
for (int i = 0; i < queue.size(); i++) {
|
||||
|
|
|
@ -5,16 +5,6 @@ import android.database.Cursor;
|
|||
import android.test.InstrumentationTestCase;
|
||||
import android.util.Log;
|
||||
|
||||
import de.danoeh.antennapod.core.feed.Chapter;
|
||||
import de.danoeh.antennapod.core.feed.Feed;
|
||||
import de.danoeh.antennapod.core.feed.FeedImage;
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.feed.SimpleChapter;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.storage.PodDBAdapter;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
@ -25,6 +15,16 @@ import java.util.concurrent.Future;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import de.danoeh.antennapod.core.feed.Chapter;
|
||||
import de.danoeh.antennapod.core.feed.Feed;
|
||||
import de.danoeh.antennapod.core.feed.FeedImage;
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.feed.SimpleChapter;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.storage.PodDBAdapter;
|
||||
|
||||
/**
|
||||
* Test class for DBWriter
|
||||
*/
|
||||
|
@ -674,13 +674,13 @@ public class DBWriterTest extends InstrumentationTestCase {
|
|||
assertTrue(item.getId() != 0);
|
||||
}
|
||||
for (int removeIndex = 0; removeIndex < NUM_ITEMS; removeIndex++) {
|
||||
final long id = feed.getItems().get(removeIndex).getId();
|
||||
final FeedItem item = feed.getItems().get(removeIndex);
|
||||
adapter = new PodDBAdapter(context);
|
||||
adapter.open();
|
||||
adapter.setQueue(feed.getItems());
|
||||
adapter.close();
|
||||
|
||||
DBWriter.removeQueueItem(context, id, false).get(TIMEOUT, TimeUnit.SECONDS);
|
||||
DBWriter.removeQueueItem(context, item, false).get(TIMEOUT, TimeUnit.SECONDS);
|
||||
adapter = new PodDBAdapter(context);
|
||||
adapter.open();
|
||||
Cursor queue = adapter.getQueueIDCursor();
|
||||
|
@ -688,10 +688,10 @@ public class DBWriterTest extends InstrumentationTestCase {
|
|||
for (int i = 0; i < queue.getCount(); i++) {
|
||||
assertTrue(queue.moveToPosition(i));
|
||||
final long queueID = queue.getLong(0);
|
||||
assertTrue(queueID != id); // removed item is no longer in queue
|
||||
assertTrue(queueID != item.getId()); // removed item is no longer in queue
|
||||
boolean idFound = false;
|
||||
for (FeedItem item : feed.getItems()) { // items that were not removed are still in the queue
|
||||
idFound = idFound | (item.getId() == queueID);
|
||||
for (FeedItem other : feed.getItems()) { // items that were not removed are still in the queue
|
||||
idFound = idFound | (other.getId() == queueID);
|
||||
}
|
||||
assertTrue(idFound);
|
||||
}
|
||||
|
|
|
@ -25,7 +25,9 @@ import de.danoeh.antennapod.core.feed.Feed;
|
|||
import de.danoeh.antennapod.core.feed.FeedImage;
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.feed.QueueEvent;
|
||||
import de.danoeh.antennapod.core.storage.PodDBAdapter;
|
||||
import de.greenrobot.event.EventBus;
|
||||
import de.test.antennapod.util.service.download.HTTPBin;
|
||||
import de.test.antennapod.util.syndication.feedgenerator.RSS2Generator;
|
||||
|
||||
|
@ -202,6 +204,6 @@ public class UITestUtils {
|
|||
adapter.setQueue(queue);
|
||||
adapter.close();
|
||||
EventDistributor.getInstance().sendFeedUpdateBroadcast();
|
||||
EventDistributor.getInstance().sendQueueUpdateBroadcast();
|
||||
EventBus.getDefault().post(new QueueEvent(QueueEvent.Action.ADDED_ITEMS, queue));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
package de.test.antennapod.util.syndication.feedgenerator;
|
||||
|
||||
import android.util.Xml;
|
||||
import de.danoeh.antennapod.core.feed.Feed;
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.syndication.util.SyndDateUtils;
|
||||
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import de.danoeh.antennapod.core.feed.Feed;
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.util.DateUtils;
|
||||
|
||||
/**
|
||||
* Creates Atom feeds. See FeedGenerator for more information.
|
||||
*/
|
||||
|
@ -83,9 +85,9 @@ public class AtomGenerator implements FeedGenerator{
|
|||
if (item.getPubDate() != null) {
|
||||
xml.startTag(null, "published");
|
||||
if ((flags & FEATURE_USE_RFC3339LOCAL) != 0) {
|
||||
xml.text(SyndDateUtils.formatRFC3339Local(item.getPubDate()));
|
||||
xml.text(DateUtils.formatRFC3339Local(item.getPubDate()));
|
||||
} else {
|
||||
xml.text(SyndDateUtils.formatRFC3339UTC(item.getPubDate()));
|
||||
xml.text(DateUtils.formatRFC3339UTC(item.getPubDate()));
|
||||
}
|
||||
xml.endTag(null, "published");
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ package de.test.antennapod.util.syndication.feedgenerator;
|
|||
import android.util.Xml;
|
||||
import de.danoeh.antennapod.core.feed.Feed;
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.syndication.util.SyndDateUtils;
|
||||
import de.danoeh.antennapod.core.util.DateUtils;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -79,7 +79,7 @@ public class RSS2Generator implements FeedGenerator{
|
|||
}
|
||||
if (item.getPubDate() != null) {
|
||||
xml.startTag(null, "pubDate");
|
||||
xml.text(SyndDateUtils.formatRFC822Date(item.getPubDate()));
|
||||
xml.text(DateUtils.formatRFC822Date(item.getPubDate()));
|
||||
xml.endTag(null, "pubDate");
|
||||
}
|
||||
if ((flags & FEATURE_WRITE_GUID) != 0) {
|
||||
|
|
|
@ -0,0 +1,202 @@
|
|||
|
||||
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.
|
|
@ -77,5 +77,8 @@ licensed under the Apache 2.0 license <a href="LICENSE_OKIO.txt">(View)</a>
|
|||
<h2>Material Design Icons <a href="https://github.com/google/material-design-icons">(Link)</a></h2>
|
||||
by Google, licensed under an Attribution-ShareAlike 4.0 International license <a href="LICENSE_MATERIAL_DESIGN_ICONS.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>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -29,7 +29,6 @@ import com.squareup.picasso.Picasso;
|
|||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import de.danoeh.antennapod.BuildConfig;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.adapter.ChapterListAdapter;
|
||||
import de.danoeh.antennapod.adapter.NavListAdapter;
|
||||
|
@ -96,30 +95,25 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
|
|||
FragmentTransaction fT = getSupportFragmentManager().beginTransaction();
|
||||
|
||||
if (coverFragment != null) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Removing cover fragment");
|
||||
Log.d(TAG, "Removing cover fragment");
|
||||
fT.remove(coverFragment);
|
||||
}
|
||||
if (descriptionFragment != null) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Removing description fragment");
|
||||
Log.d(TAG, "Removing description fragment");
|
||||
fT.remove(descriptionFragment);
|
||||
}
|
||||
if (chapterFragment != null) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Removing chapter fragment");
|
||||
Log.d(TAG, "Removing chapter fragment");
|
||||
fT.remove(chapterFragment);
|
||||
}
|
||||
if (currentlyShownFragment != null) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Removing currently shown fragment");
|
||||
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) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Removing detached fragment");
|
||||
Log.d(TAG, "Removing detached fragment");
|
||||
fT.remove(f);
|
||||
}
|
||||
}
|
||||
|
@ -135,11 +129,9 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
|
|||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "onStop");
|
||||
Log.d(TAG, "onStop()");
|
||||
cancelLoadTask();
|
||||
EventDistributor.getInstance().unregister(contentUpdate);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -154,8 +146,7 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
|
|||
}
|
||||
|
||||
private void savePreferences() {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Saving preferences");
|
||||
Log.d(TAG, "Saving preferences");
|
||||
SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
if (currentlyShownPosition >= 0 && controller != null
|
||||
|
@ -182,9 +173,7 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
|
|||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
// super.onSaveInstanceState(outState); would cause crash
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "onSaveInstanceState");
|
||||
|
||||
Log.d(TAG, "onSaveInstanceState");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -207,8 +196,7 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
|
|||
* @return true if restoreFromPrefernces changed the activity's state
|
||||
*/
|
||||
private boolean restoreFromPreferences() {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Restoring instance state");
|
||||
Log.d(TAG, "Restoring instance state");
|
||||
SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
|
||||
int savedPosition = prefs.getInt(PREF_KEY_SELECTED_FRAGMENT_POSITION,
|
||||
-1);
|
||||
|
@ -222,15 +210,10 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
|
|||
switchToFragment(savedPosition);
|
||||
return true;
|
||||
} else if (controller == null || controller.getMedia() == null) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG,
|
||||
"Couldn't restore from preferences: controller or media was null");
|
||||
Log.d(TAG, "Couldn't restore from preferences: controller or media was null");
|
||||
} else {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG,
|
||||
"Couldn't restore from preferences: savedPosition was -1 or saved identifier and playable identifier didn't match.\nsavedPosition: "
|
||||
+ savedPosition + ", id: " + playableId
|
||||
);
|
||||
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;
|
||||
|
@ -241,9 +224,7 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
|
|||
super.onResume();
|
||||
if (StringUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) {
|
||||
Intent intent = getIntent();
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Received VIEW intent: "
|
||||
+ intent.getData().getPath());
|
||||
Log.d(TAG, "Received VIEW intent: " + intent.getData().getPath());
|
||||
ExternalMedia media = new ExternalMedia(intent.getData().getPath(),
|
||||
MediaType.AUDIO);
|
||||
Intent launchIntent = new Intent(this, PlaybackService.class);
|
||||
|
@ -271,8 +252,7 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
|
|||
|
||||
@Override
|
||||
protected void onAwaitingVideoSurface() {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "onAwaitingVideoSurface was called in audio player -> switching to video player");
|
||||
Log.d(TAG, "onAwaitingVideoSurface was called in audio player -> switching to video player");
|
||||
startActivity(new Intent(this, VideoplayerActivity.class));
|
||||
}
|
||||
|
||||
|
@ -297,8 +277,7 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
|
|||
* @param pos Must be POS_COVER, POS_DESCR, or POS_CHAPTERS
|
||||
*/
|
||||
private void switchToFragment(int pos) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Switching contentView to position " + pos);
|
||||
Log.d(TAG, "Switching contentView to position " + pos);
|
||||
if (currentlyShownPosition != pos && controller != null) {
|
||||
Playable media = controller.getMedia();
|
||||
if (media != null) {
|
||||
|
@ -356,9 +335,7 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
|
|||
lastShownPosition = currentlyShownPosition;
|
||||
currentlyShownPosition = pos;
|
||||
if (detachedFragments[pos] != null) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Reattaching fragment at position "
|
||||
+ pos);
|
||||
Log.d(TAG, "Reattaching fragment at position " + pos);
|
||||
ft.attach(detachedFragments[pos]);
|
||||
} else {
|
||||
ft.add(R.id.contentView, currentlyShownFragment);
|
||||
|
@ -632,9 +609,7 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
|
|||
@Override
|
||||
protected void onReloadNotification(int notificationCode) {
|
||||
if (notificationCode == PlaybackService.EXTRA_CODE_VIDEO) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG,
|
||||
"ReloadNotification received, switching to Videoplayer now");
|
||||
Log.d(TAG, "ReloadNotification received, switching to Videoplayer now");
|
||||
finish();
|
||||
startActivity(new Intent(this, VideoplayerActivity.class));
|
||||
|
||||
|
@ -731,8 +706,7 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
|
|||
@Override
|
||||
public void update(EventDistributor eventDistributor, Integer arg) {
|
||||
if ((EventDistributor.FEED_LIST_UPDATE & arg) != 0) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Received contentUpdate Intent.");
|
||||
Log.d(TAG, "Received contentUpdate Intent.");
|
||||
loadData();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,11 +25,11 @@ import org.apache.commons.lang3.Validate;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import de.danoeh.antennapod.BuildConfig;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.adapter.NavListAdapter;
|
||||
import de.danoeh.antennapod.core.feed.EventDistributor;
|
||||
import de.danoeh.antennapod.core.feed.Feed;
|
||||
import de.danoeh.antennapod.core.feed.QueueEvent;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.util.StorageUtils;
|
||||
|
@ -43,6 +43,7 @@ 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 de.greenrobot.event.EventBus;
|
||||
|
||||
/**
|
||||
* The activity that is shown when the user launches the app.
|
||||
|
@ -52,8 +53,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
|
|||
private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED
|
||||
| EventDistributor.DOWNLOAD_QUEUED
|
||||
| EventDistributor.FEED_LIST_UPDATE
|
||||
| EventDistributor.UNREAD_ITEMS_UPDATE
|
||||
| EventDistributor.QUEUE_UPDATE;
|
||||
| EventDistributor.UNREAD_ITEMS_UPDATE;
|
||||
|
||||
public static final String PREF_NAME = "MainActivityPrefs";
|
||||
public static final String PREF_IS_FIRST_LAUNCH = "prefMainActivityIsFirstLaunch";
|
||||
|
@ -342,6 +342,13 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
EventDistributor.getInstance().register(contentUpdate);
|
||||
EventBus.getDefault().register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
|
@ -351,7 +358,6 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
|
|||
protected void onResume() {
|
||||
super.onResume();
|
||||
StorageUtils.checkStorageAvailability(this);
|
||||
EventDistributor.getInstance().register(contentUpdate);
|
||||
|
||||
Intent intent = getIntent();
|
||||
if (navDrawerData != null && intent.hasExtra(EXTRA_NAV_INDEX) && intent.hasExtra(EXTRA_NAV_TYPE)) {
|
||||
|
@ -366,6 +372,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
|
|||
super.onStop();
|
||||
cancelLoadTask();
|
||||
EventDistributor.getInstance().unregister(contentUpdate);
|
||||
EventBus.getDefault().unregister(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -454,13 +461,17 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
|
|||
}
|
||||
}
|
||||
|
||||
public void onEvent(QueueEvent event) {
|
||||
Log.d(TAG, "onEvent(" + event + ")");
|
||||
loadData();
|
||||
}
|
||||
|
||||
private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
|
||||
|
||||
@Override
|
||||
public void update(EventDistributor eventDistributor, Integer arg) {
|
||||
if ((EVENTS & arg) != 0) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Received contentUpdate Intent.");
|
||||
Log.d(TAG, "Received contentUpdate Intent.");
|
||||
loadData();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,13 +12,13 @@ import android.util.Log;
|
|||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.Window;
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.SeekBar.OnSeekBarChangeListener;
|
||||
import android.widget.TextView;
|
||||
|
||||
import de.danoeh.antennapod.BuildConfig;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||
|
@ -48,7 +48,9 @@ public abstract class MediaplayerActivity extends ActionBarActivity
|
|||
protected SeekBar sbPosition;
|
||||
protected ImageButton butPlay;
|
||||
protected ImageButton butRev;
|
||||
protected TextView txtvRev;
|
||||
protected ImageButton butFF;
|
||||
protected TextView txtvFF;
|
||||
|
||||
private PlaybackController newPlaybackController() {
|
||||
return new PlaybackController(this, false) {
|
||||
|
@ -167,8 +169,7 @@ public abstract class MediaplayerActivity extends ActionBarActivity
|
|||
chooseTheme();
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Creating Activity");
|
||||
Log.d(TAG, "onCreate()");
|
||||
StorageUtils.checkStorageAvailability(this);
|
||||
setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
||||
|
||||
|
@ -224,8 +225,7 @@ public abstract class MediaplayerActivity extends ActionBarActivity
|
|||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Activity stopped");
|
||||
Log.d(TAG, "onStop()");
|
||||
if (controller != null) {
|
||||
controller.release();
|
||||
}
|
||||
|
@ -234,8 +234,7 @@ public abstract class MediaplayerActivity extends ActionBarActivity
|
|||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Activity destroyed");
|
||||
Log.d(TAG, "onDestroy()");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -358,8 +357,7 @@ public abstract class MediaplayerActivity extends ActionBarActivity
|
|||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Resuming Activity");
|
||||
Log.d(TAG, "onResume()");
|
||||
StorageUtils.checkStorageAvailability(this);
|
||||
controller.init();
|
||||
}
|
||||
|
@ -378,6 +376,8 @@ public abstract class MediaplayerActivity extends ActionBarActivity
|
|||
if (controller != null) {
|
||||
int currentPosition = controller.getPosition();
|
||||
int duration = controller.getDuration();
|
||||
Log.d(TAG, "currentPosition " + Converter
|
||||
.getDurationStringLong(currentPosition));
|
||||
if (currentPosition != PlaybackService.INVALID_TIME
|
||||
&& duration != PlaybackService.INVALID_TIME
|
||||
&& controller.getMedia() != null) {
|
||||
|
@ -386,15 +386,13 @@ public abstract class MediaplayerActivity extends ActionBarActivity
|
|||
txtvLength.setText(Converter.getDurationStringLong(duration));
|
||||
updateProgressbarPosition(currentPosition, duration);
|
||||
} else {
|
||||
Log.w(TAG,
|
||||
"Could not react to position observer update because of invalid time");
|
||||
Log.w(TAG, "Could not react to position observer update because of invalid time");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateProgressbarPosition(int position, int duration) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Updating progressbar info");
|
||||
Log.d(TAG, "updateProgressbarPosition(" + position + ", " + duration +")");
|
||||
float progress = ((float) position) / duration;
|
||||
sbPosition.setProgress((int) (progress * sbPosition.getMax()));
|
||||
}
|
||||
|
@ -406,8 +404,7 @@ public abstract class MediaplayerActivity extends ActionBarActivity
|
|||
* FeedMedia object.
|
||||
*/
|
||||
protected boolean loadMediaInfo() {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Loading media info");
|
||||
Log.d(TAG, "loadMediaInfo()");
|
||||
Playable media = controller.getMedia();
|
||||
if (media != null) {
|
||||
txtvPosition.setText(Converter.getDurationStringLong((media
|
||||
|
@ -433,7 +430,11 @@ public abstract class MediaplayerActivity extends ActionBarActivity
|
|||
txtvLength = (TextView) findViewById(R.id.txtvLength);
|
||||
butPlay = (ImageButton) findViewById(R.id.butPlay);
|
||||
butRev = (ImageButton) findViewById(R.id.butRev);
|
||||
txtvRev = (TextView) findViewById(R.id.txtvRev);
|
||||
txtvRev.setText(String.valueOf(UserPreferences.getRewindSecs()));
|
||||
butFF = (ImageButton) findViewById(R.id.butFF);
|
||||
txtvFF = (TextView) findViewById(R.id.txtvFF);
|
||||
txtvFF.setText(String.valueOf(UserPreferences.getFastFowardSecs()));
|
||||
|
||||
// SEEKBAR SETUP
|
||||
|
||||
|
@ -444,10 +445,100 @@ public abstract class MediaplayerActivity extends ActionBarActivity
|
|||
butPlay.setOnClickListener(controller.newOnPlayButtonClickListener());
|
||||
|
||||
if (butFF != null) {
|
||||
butFF.setOnClickListener(controller.newOnFFButtonClickListener());
|
||||
butFF.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int curr = controller.getPosition();
|
||||
controller.seekTo(curr + UserPreferences.getFastFowardSecs() * 1000);
|
||||
}
|
||||
});
|
||||
butFF.setOnLongClickListener(new View.OnLongClickListener() {
|
||||
|
||||
int choice;
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
int checked = 0;
|
||||
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++) {
|
||||
if (rewindSecs == values[i]) {
|
||||
checked = i;
|
||||
}
|
||||
choices[i] = String.valueOf(values[i]) + " "
|
||||
+ getString(R.string.time_unit_seconds);
|
||||
}
|
||||
choice = values[checked];
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(MediaplayerActivity.this);
|
||||
builder.setTitle(R.string.pref_fast_forward);
|
||||
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.setPrefFastForwardSecs(choice);
|
||||
txtvFF.setText(String.valueOf(choice));
|
||||
}
|
||||
});
|
||||
builder.create().show();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (butRev != null) {
|
||||
butRev.setOnClickListener(controller.newOnRevButtonClickListener());
|
||||
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_unit_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;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
|
|||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
||||
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction;
|
||||
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
|
||||
import de.danoeh.antennapod.core.storage.DBTasks;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequestException;
|
||||
|
@ -61,6 +63,19 @@ public class DefaultActionButtonCallback implements ActionButtonCallback {
|
|||
} else {
|
||||
if (!item.isRead()) {
|
||||
DBWriter.markItemRead(context, item, true, true);
|
||||
|
||||
if(GpodnetPreferences.loggedIn()) {
|
||||
// gpodder: send played action
|
||||
FeedMedia media = item.getMedia();
|
||||
GpodnetEpisodeAction action = new GpodnetEpisodeAction.Builder(item, GpodnetEpisodeAction.Action.PLAY)
|
||||
.currentDeviceId()
|
||||
.currentTimestamp()
|
||||
.started(media.getDuration() / 1000)
|
||||
.position(media.getDuration() / 1000)
|
||||
.total(media.getDuration() / 1000)
|
||||
.build();
|
||||
GpodnetPreferences.enqueueEpisodeAction(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,10 +49,11 @@ import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
|
|||
* Shows unread or recently published episodes
|
||||
*/
|
||||
public class AllEpisodesFragment extends Fragment {
|
||||
|
||||
private static final String TAG = "AllEpisodesFragment";
|
||||
|
||||
private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED |
|
||||
EventDistributor.DOWNLOAD_QUEUED |
|
||||
EventDistributor.QUEUE_UPDATE |
|
||||
EventDistributor.UNREAD_ITEMS_UPDATE |
|
||||
EventDistributor.PLAYER_STATUS_UPDATE;
|
||||
|
||||
|
@ -399,7 +400,7 @@ public class AllEpisodesFragment extends Fragment {
|
|||
|
||||
private ItemLoader itemLoader;
|
||||
|
||||
private void startItemLoader() {
|
||||
protected void startItemLoader() {
|
||||
if (itemLoader != null) {
|
||||
itemLoader.cancel(true);
|
||||
}
|
||||
|
@ -429,9 +430,10 @@ public class AllEpisodesFragment extends Fragment {
|
|||
protected Object[] doInBackground(Void... params) {
|
||||
Context context = activity.get();
|
||||
if (context != null) {
|
||||
return new Object[]{DBReader.getUnreadItemsList(context),
|
||||
return new Object[]{
|
||||
DBReader.getUnreadItemsList(context),
|
||||
DBReader.getRecentlyPublishedEpisodes(context, RECENT_EPISODES_LIMIT),
|
||||
QueueAccess.IDListAccess(DBReader.getQueueIDList(context))};
|
||||
DBReader.getQueueIDList(context)};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import android.support.v4.app.ListFragment;
|
|||
import android.view.View;
|
||||
import android.widget.ListView;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import de.danoeh.antennapod.R;
|
||||
|
@ -17,7 +18,6 @@ import de.danoeh.antennapod.core.feed.EventDistributor;
|
|||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.util.QueueAccess;
|
||||
|
||||
/**
|
||||
* Displays all running downloads and provides a button to delete them
|
||||
|
@ -26,11 +26,9 @@ public class CompletedDownloadsFragment extends ListFragment {
|
|||
private static final int EVENTS =
|
||||
EventDistributor.DOWNLOAD_HANDLED |
|
||||
EventDistributor.DOWNLOADLOG_UPDATE |
|
||||
EventDistributor.QUEUE_UPDATE |
|
||||
EventDistributor.UNREAD_ITEMS_UPDATE;
|
||||
|
||||
private List<FeedItem> items;
|
||||
private QueueAccess queue;
|
||||
private DownloadedEpisodesListAdapter listAdapter;
|
||||
|
||||
private boolean viewCreated = false;
|
||||
|
@ -155,7 +153,7 @@ public class CompletedDownloadsFragment extends ListFragment {
|
|||
}
|
||||
}
|
||||
|
||||
private class ItemLoader extends AsyncTask<Void, Void, Object[]> {
|
||||
private class ItemLoader extends AsyncTask<Void, Void, List<FeedItem>> {
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
|
@ -166,11 +164,10 @@ public class CompletedDownloadsFragment extends ListFragment {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Object[] results) {
|
||||
protected void onPostExecute(List<FeedItem> results) {
|
||||
super.onPostExecute(results);
|
||||
if (results != null) {
|
||||
items = (List<FeedItem>) results[0];
|
||||
queue = (QueueAccess) results[1];
|
||||
items = results;
|
||||
itemsLoaded = true;
|
||||
if (viewCreated && getActivity() != null) {
|
||||
onFragmentLoaded();
|
||||
|
@ -179,13 +176,12 @@ public class CompletedDownloadsFragment extends ListFragment {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Object[] doInBackground(Void... params) {
|
||||
protected List<FeedItem> doInBackground(Void... params) {
|
||||
Context context = getActivity();
|
||||
if (context != null) {
|
||||
return new Object[]{DBReader.getDownloadedItems(context),
|
||||
QueueAccess.IDListAccess(DBReader.getQueueIDList(context))};
|
||||
return DBReader.getDownloadedItems(context);
|
||||
}
|
||||
return null;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ import android.widget.TextView;
|
|||
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
import de.danoeh.antennapod.BuildConfig;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
||||
import de.danoeh.antennapod.core.util.Converter;
|
||||
|
@ -54,8 +53,7 @@ public class ExternalPlayerFragment extends Fragment {
|
|||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "layoutInfo was clicked");
|
||||
Log.d(TAG, "layoutInfo was clicked");
|
||||
|
||||
if (controller.getMedia() != null) {
|
||||
startActivity(PlaybackService.getPlayerActivityIntent(
|
||||
|
@ -90,14 +88,10 @@ public class ExternalPlayerFragment extends Fragment {
|
|||
|
||||
@Override
|
||||
public void onBufferStart() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBufferEnd() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -153,7 +147,6 @@ public class ExternalPlayerFragment extends Fragment {
|
|||
butPlay.setOnClickListener(controller
|
||||
.newOnPlayButtonClickListener());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -170,8 +163,6 @@ public class ExternalPlayerFragment extends Fragment {
|
|||
|
||||
@Override
|
||||
public void onPlaybackSpeedChange() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -185,8 +176,7 @@ public class ExternalPlayerFragment extends Fragment {
|
|||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Fragment is about to be destroyed");
|
||||
Log.d(TAG, "Fragment is about to be destroyed");
|
||||
if (controller != null) {
|
||||
controller.release();
|
||||
}
|
||||
|
@ -201,8 +191,7 @@ public class ExternalPlayerFragment extends Fragment {
|
|||
}
|
||||
|
||||
private boolean loadMediaInfo() {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Loading media info");
|
||||
Log.d(TAG, "Loading media info");
|
||||
if (controller.serviceAvailable()) {
|
||||
Playable media = controller.getMedia();
|
||||
if (media != null) {
|
||||
|
@ -221,13 +210,11 @@ public class ExternalPlayerFragment extends Fragment {
|
|||
}
|
||||
return true;
|
||||
} else {
|
||||
Log.w(TAG,
|
||||
"loadMediaInfo was called while the media object of playbackService was null!");
|
||||
Log.w(TAG, "loadMediaInfo was called while the media object of playbackService was null!");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG,
|
||||
"loadMediaInfo was called while playbackService was null!");
|
||||
Log.w(TAG, "loadMediaInfo was called while playbackService was null!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import android.support.v7.widget.PopupMenu;
|
|||
import android.support.v7.widget.Toolbar;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
@ -43,6 +44,7 @@ import de.danoeh.antennapod.core.asynctask.DownloadObserver;
|
|||
import de.danoeh.antennapod.core.feed.EventDistributor;
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.feed.QueueEvent;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.service.download.Downloader;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
|
@ -51,18 +53,20 @@ import de.danoeh.antennapod.core.storage.DBWriter;
|
|||
import de.danoeh.antennapod.core.storage.DownloadRequestException;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
import de.danoeh.antennapod.core.util.Converter;
|
||||
import de.danoeh.antennapod.core.util.QueueAccess;
|
||||
import de.danoeh.antennapod.core.util.LongList;
|
||||
import de.danoeh.antennapod.core.util.playback.Timeline;
|
||||
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
/**
|
||||
* Displays information about a FeedItem and actions.
|
||||
*/
|
||||
public class ItemFragment extends Fragment implements LoaderManager.LoaderCallbacks<Pair<FeedItem, QueueAccess>> {
|
||||
public class ItemFragment extends Fragment implements LoaderManager.LoaderCallbacks<Pair<FeedItem, LongList>> {
|
||||
|
||||
private static final String TAG = "ItemFragment";
|
||||
|
||||
private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED |
|
||||
EventDistributor.DOWNLOAD_QUEUED |
|
||||
EventDistributor.QUEUE_UPDATE |
|
||||
EventDistributor.UNREAD_ITEMS_UPDATE;
|
||||
|
||||
private static final String ARG_FEEDITEM = "feeditem";
|
||||
|
@ -84,7 +88,7 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba
|
|||
private boolean itemsLoaded = false;
|
||||
private long itemID;
|
||||
private FeedItem item;
|
||||
private QueueAccess queue;
|
||||
private LongList queue;
|
||||
private String webviewData;
|
||||
private DownloadObserver downloadObserver;
|
||||
private List<Downloader> downloaderList;
|
||||
|
@ -124,6 +128,7 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba
|
|||
public void onStart() {
|
||||
super.onStart();
|
||||
EventDistributor.getInstance().register(contentUpdate);
|
||||
EventBus.getDefault().register(this);
|
||||
if (downloadObserver != null) {
|
||||
downloadObserver.setActivity(getActivity());
|
||||
downloadObserver.onResume();
|
||||
|
@ -138,6 +143,7 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba
|
|||
public void onStop() {
|
||||
super.onStop();
|
||||
EventDistributor.getInstance().unregister(contentUpdate);
|
||||
EventBus.getDefault().unregister(this);
|
||||
}
|
||||
|
||||
private void resetViewState() {
|
||||
|
@ -387,25 +393,29 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba
|
|||
}
|
||||
}
|
||||
|
||||
public void onEvent(QueueEvent event) {
|
||||
Log.d(TAG, "onEvent(" + event + ")");
|
||||
getLoaderManager().restartLoader(0, null, ItemFragment.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loader<Pair<FeedItem, QueueAccess>> onCreateLoader(int id, Bundle args) {
|
||||
return new DBTaskLoader<Pair<FeedItem, QueueAccess>>(getActivity()) {
|
||||
public Loader<Pair<FeedItem,LongList>> onCreateLoader(int id, Bundle args) {
|
||||
return new DBTaskLoader<Pair<FeedItem,LongList>>(getActivity()) {
|
||||
@Override
|
||||
public Pair<FeedItem, QueueAccess> loadInBackground() {
|
||||
public Pair<FeedItem,LongList> loadInBackground() {
|
||||
FeedItem data1 = DBReader.getFeedItem(getContext(), itemID);
|
||||
if (data1 != null) {
|
||||
Timeline t = new Timeline(getActivity(), data1);
|
||||
webviewData = t.processShownotes(false);
|
||||
}
|
||||
QueueAccess data2 = QueueAccess.IDListAccess(DBReader.getQueueIDList(getContext()));
|
||||
LongList data2 = DBReader.getQueueIDList(getContext());
|
||||
return Pair.create(data1, data2);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Pair<FeedItem, QueueAccess>> loader, Pair<FeedItem, QueueAccess> data) {
|
||||
public void onLoadFinished(Loader<Pair<FeedItem,LongList>> loader, Pair<FeedItem,LongList> data) {
|
||||
|
||||
if (data != null) {
|
||||
item = data.first;
|
||||
|
@ -420,8 +430,7 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<Pair<FeedItem, QueueAccess>> loader) {
|
||||
|
||||
public void onLoaderReset(Loader<Pair<FeedItem,LongList>> loader) {
|
||||
}
|
||||
|
||||
private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
|
||||
|
|
|
@ -9,6 +9,7 @@ import android.os.Build;
|
|||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v4.app.ListFragment;
|
||||
import android.support.v4.util.Pair;
|
||||
import android.support.v7.app.ActionBarActivity;
|
||||
import android.support.v7.widget.SearchView;
|
||||
import android.util.Log;
|
||||
|
@ -29,7 +30,6 @@ import org.apache.commons.lang3.Validate;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import de.danoeh.antennapod.BuildConfig;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.activity.FeedInfoActivity;
|
||||
import de.danoeh.antennapod.activity.MainActivity;
|
||||
|
@ -44,17 +44,19 @@ import de.danoeh.antennapod.core.feed.EventDistributor;
|
|||
import de.danoeh.antennapod.core.feed.Feed;
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.feed.QueueEvent;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.core.service.download.Downloader;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DBTasks;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequestException;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
import de.danoeh.antennapod.core.util.QueueAccess;
|
||||
import de.danoeh.antennapod.core.util.LongList;
|
||||
import de.danoeh.antennapod.core.util.gui.MoreContentListFooterUtil;
|
||||
import de.danoeh.antennapod.menuhandler.FeedMenuHandler;
|
||||
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
|
||||
import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
/**
|
||||
* Displays a list of FeedItems.
|
||||
|
@ -65,7 +67,6 @@ public class ItemlistFragment extends ListFragment {
|
|||
|
||||
private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED
|
||||
| EventDistributor.DOWNLOAD_QUEUED
|
||||
| EventDistributor.QUEUE_UPDATE
|
||||
| EventDistributor.UNREAD_ITEMS_UPDATE
|
||||
| EventDistributor.PLAYER_STATUS_UPDATE;
|
||||
|
||||
|
@ -76,7 +77,7 @@ public class ItemlistFragment extends ListFragment {
|
|||
|
||||
private long feedID;
|
||||
private Feed feed;
|
||||
protected QueueAccess queue;
|
||||
private LongList queue;
|
||||
|
||||
private boolean itemsLoaded = false;
|
||||
private boolean viewsCreated = false;
|
||||
|
@ -118,6 +119,7 @@ public class ItemlistFragment extends ListFragment {
|
|||
public void onStart() {
|
||||
super.onStart();
|
||||
EventDistributor.getInstance().register(contentUpdate);
|
||||
EventBus.getDefault().register(this);
|
||||
if (downloadObserver != null) {
|
||||
downloadObserver.setActivity(getActivity());
|
||||
downloadObserver.onResume();
|
||||
|
@ -131,6 +133,7 @@ public class ItemlistFragment extends ListFragment {
|
|||
public void onStop() {
|
||||
super.onStop();
|
||||
EventDistributor.getInstance().unregister(contentUpdate);
|
||||
EventBus.getDefault().unregister(this);
|
||||
stopItemLoader();
|
||||
}
|
||||
|
||||
|
@ -283,13 +286,17 @@ public class ItemlistFragment extends ListFragment {
|
|||
}
|
||||
}
|
||||
|
||||
public void onEvent(QueueEvent event) {
|
||||
Log.d(TAG, "onEvent(" + event + ")");
|
||||
startItemLoader();
|
||||
}
|
||||
|
||||
private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
|
||||
|
||||
@Override
|
||||
public void update(EventDistributor eventDistributor, Integer arg) {
|
||||
if ((EVENTS & arg) != 0) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Received contentUpdate Intent.");
|
||||
Log.d(TAG, "Received contentUpdate Intent.");
|
||||
if ((EventDistributor.DOWNLOAD_QUEUED & arg) != 0) {
|
||||
updateProgressBarVisibility();
|
||||
} else {
|
||||
|
@ -474,25 +481,26 @@ public class ItemlistFragment extends ListFragment {
|
|||
}
|
||||
}
|
||||
|
||||
private class ItemLoader extends AsyncTask<Long, Void, Object[]> {
|
||||
private class ItemLoader extends AsyncTask<Long, Void, Pair<Feed,LongList>> {
|
||||
@Override
|
||||
protected Object[] doInBackground(Long... params) {
|
||||
protected Pair<Feed,LongList> doInBackground(Long... params) {
|
||||
long feedID = params[0];
|
||||
Context context = getActivity();
|
||||
if (context != null) {
|
||||
return new Object[]{DBReader.getFeed(context, feedID),
|
||||
QueueAccess.IDListAccess(DBReader.getQueueIDList(context))};
|
||||
Feed feed = DBReader.getFeed(context, feedID);
|
||||
LongList queue = DBReader.getQueueIDList(context);
|
||||
return Pair.create(feed, queue);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Object[] res) {
|
||||
protected void onPostExecute(Pair<Feed,LongList> res) {
|
||||
super.onPostExecute(res);
|
||||
if (res != null) {
|
||||
feed = (Feed) res[0];
|
||||
queue = (QueueAccess) res[1];
|
||||
feed = res.first;
|
||||
queue = res.second;
|
||||
itemsLoaded = true;
|
||||
if (viewsCreated) {
|
||||
onFragmentLoaded();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package de.danoeh.antennapod.fragment;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
@ -11,17 +11,25 @@ import com.mobeta.android.dslv.DragSortListView;
|
|||
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.feed.QueueEvent;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.util.gui.FeedItemUndoToken;
|
||||
import de.danoeh.antennapod.core.util.gui.UndoBarController;
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
|
||||
/**
|
||||
* Like 'EpisodesFragment' except that it only shows new episodes and
|
||||
* supports swiping to mark as read.
|
||||
*/
|
||||
|
||||
public class NewEpisodesFragment extends AllEpisodesFragment {
|
||||
|
||||
private static final String TAG = "NewEpisodesFragment";
|
||||
|
||||
private static final String PREF_NAME = "PrefNewEpisodesFragment";
|
||||
|
||||
private UndoBarController undoBarController;
|
||||
|
@ -30,6 +38,25 @@ public class NewEpisodesFragment extends AllEpisodesFragment {
|
|||
super(true, PREF_NAME);
|
||||
}
|
||||
|
||||
public void onEvent(QueueEvent event) {
|
||||
Log.d(TAG, "onEvent(" + event + ")");
|
||||
if(event.action == QueueEvent.Action.ADDED) {
|
||||
startItemLoader();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
EventBus.getDefault().register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
EventBus.getDefault().unregister(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetViewState() {
|
||||
super.resetViewState();
|
||||
|
@ -55,18 +82,30 @@ public class NewEpisodesFragment extends AllEpisodesFragment {
|
|||
}
|
||||
});
|
||||
|
||||
undoBarController = new UndoBarController(root.findViewById(R.id.undobar), new UndoBarController.UndoListener() {
|
||||
undoBarController = new UndoBarController<FeedItemUndoToken>(root.findViewById(R.id.undobar), new UndoBarController.UndoListener<FeedItemUndoToken>() {
|
||||
|
||||
private final Context context = getActivity();
|
||||
|
||||
@Override
|
||||
public void onUndo(Parcelable token) {
|
||||
// Perform the undo
|
||||
FeedItemUndoToken undoToken = (FeedItemUndoToken) token;
|
||||
public void onUndo(FeedItemUndoToken token) {
|
||||
if (token != null) {
|
||||
long itemId = undoToken.getFeedItemId();
|
||||
int position = undoToken.getPosition();
|
||||
DBWriter.markItemRead(getActivity(), itemId, false);
|
||||
long itemId = token.getFeedItemId();
|
||||
DBWriter.markItemRead(context, itemId, false);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onHide(FeedItemUndoToken token) {
|
||||
if (token != null && context != null) {
|
||||
long itemId = token.getFeedItemId();
|
||||
FeedItem item = DBReader.getFeedItem(context, itemId);
|
||||
FeedMedia media = item.getMedia();
|
||||
if(media != null && media.hasAlmostEnded() && UserPreferences.isAutoDelete()) {
|
||||
DBWriter.deleteFeedMediaOfItem(context, media.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return root;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,7 +7,9 @@ import android.os.AsyncTask;
|
|||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v4.app.ListFragment;
|
||||
import android.support.v4.util.Pair;
|
||||
import android.support.v4.view.MenuItemCompat;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
|
@ -25,12 +27,14 @@ import de.danoeh.antennapod.core.asynctask.DownloadObserver;
|
|||
import de.danoeh.antennapod.core.feed.EventDistributor;
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.feed.QueueEvent;
|
||||
import de.danoeh.antennapod.core.service.download.Downloader;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.util.QueueAccess;
|
||||
import de.danoeh.antennapod.core.util.LongList;
|
||||
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
|
||||
import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
public class PlaybackHistoryFragment extends ListFragment {
|
||||
private static final String TAG = "PlaybackHistoryFragment";
|
||||
|
@ -38,7 +42,7 @@ public class PlaybackHistoryFragment extends ListFragment {
|
|||
EventDistributor.PLAYER_STATUS_UPDATE;
|
||||
|
||||
private List<FeedItem> playbackHistory;
|
||||
private QueueAccess queue;
|
||||
private LongList queue;
|
||||
private FeedItemlistAdapter adapter;
|
||||
|
||||
private boolean itemsLoaded = false;
|
||||
|
@ -66,12 +70,14 @@ public class PlaybackHistoryFragment extends ListFragment {
|
|||
public void onStart() {
|
||||
super.onStart();
|
||||
EventDistributor.getInstance().register(contentUpdate);
|
||||
EventBus.getDefault().register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
EventDistributor.getInstance().unregister(contentUpdate);
|
||||
EventBus.getDefault().unregister(this);
|
||||
stopItemLoader();
|
||||
}
|
||||
|
||||
|
@ -165,6 +171,11 @@ public class PlaybackHistoryFragment extends ListFragment {
|
|||
}
|
||||
}
|
||||
|
||||
public void onEvent(QueueEvent event) {
|
||||
Log.d(TAG, "onEvent(" + event + ")");
|
||||
startItemLoader();
|
||||
}
|
||||
|
||||
private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
|
||||
|
||||
@Override
|
||||
|
@ -251,27 +262,27 @@ public class PlaybackHistoryFragment extends ListFragment {
|
|||
}
|
||||
}
|
||||
|
||||
private class ItemLoader extends AsyncTask<Void, Void, Object[]> {
|
||||
private class ItemLoader extends AsyncTask<Void, Void, Pair<List<FeedItem>,LongList>> {
|
||||
|
||||
@Override
|
||||
protected Object[] doInBackground(Void... params) {
|
||||
protected Pair<List<FeedItem>,LongList> doInBackground(Void... params) {
|
||||
Context context = activity.get();
|
||||
if (context != null) {
|
||||
List<FeedItem> ph = DBReader.getPlaybackHistory(context);
|
||||
DBReader.loadFeedDataOfFeedItemlist(context, ph);
|
||||
return new Object[]{ph,
|
||||
QueueAccess.IDListAccess(DBReader.getQueueIDList(context))};
|
||||
List<FeedItem> history = DBReader.getPlaybackHistory(context);
|
||||
LongList queue = DBReader.getQueueIDList(context);
|
||||
DBReader.loadFeedDataOfFeedItemlist(context, history);
|
||||
return Pair.create(history, queue);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Object[] res) {
|
||||
protected void onPostExecute(Pair<List<FeedItem>,LongList> res) {
|
||||
super.onPostExecute(res);
|
||||
if (res != null) {
|
||||
playbackHistory = (List<FeedItem>) res[0];
|
||||
queue = (QueueAccess) res[1];
|
||||
playbackHistory = res.first;
|
||||
queue = res.second;
|
||||
itemsLoaded = true;
|
||||
if (viewsCreated) {
|
||||
onFragmentLoaded();
|
||||
|
|
|
@ -7,7 +7,6 @@ import android.content.SharedPreferences;
|
|||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Parcelable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v7.widget.SearchView;
|
||||
import android.util.Log;
|
||||
|
@ -37,6 +36,8 @@ import de.danoeh.antennapod.core.feed.EventDistributor;
|
|||
import de.danoeh.antennapod.core.feed.Feed;
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.feed.QueueEvent;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||
import de.danoeh.antennapod.core.service.download.Downloader;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
|
@ -48,6 +49,7 @@ import de.danoeh.antennapod.core.util.gui.FeedItemUndoToken;
|
|||
import de.danoeh.antennapod.core.util.gui.UndoBarController;
|
||||
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
|
||||
import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
/**
|
||||
* Shows all items in the queue
|
||||
|
@ -56,7 +58,6 @@ public class QueueFragment extends Fragment {
|
|||
private static final String TAG = "QueueFragment";
|
||||
private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED |
|
||||
EventDistributor.DOWNLOAD_QUEUED |
|
||||
EventDistributor.QUEUE_UPDATE |
|
||||
EventDistributor.PLAYER_STATUS_UPDATE;
|
||||
|
||||
private DragSortListView listView;
|
||||
|
@ -64,7 +65,7 @@ public class QueueFragment extends Fragment {
|
|||
private TextView txtvEmpty;
|
||||
private ProgressBar progLoading;
|
||||
|
||||
private UndoBarController undoBarController;
|
||||
private UndoBarController<FeedItemUndoToken> undoBarController;
|
||||
|
||||
private List<FeedItem> queue;
|
||||
private List<Downloader> downloaderList;
|
||||
|
@ -104,6 +105,7 @@ public class QueueFragment extends Fragment {
|
|||
public void onStart() {
|
||||
super.onStart();
|
||||
EventDistributor.getInstance().register(contentUpdate);
|
||||
EventBus.getDefault().register(this);
|
||||
this.activity.set((MainActivity) getActivity());
|
||||
if (downloadObserver != null) {
|
||||
downloadObserver.setActivity(getActivity());
|
||||
|
@ -124,7 +126,11 @@ public class QueueFragment extends Fragment {
|
|||
public void onStop() {
|
||||
super.onStop();
|
||||
EventDistributor.getInstance().unregister(contentUpdate);
|
||||
EventBus.getDefault().unregister(this);
|
||||
stopItemLoader();
|
||||
if(undoBarController.isShowing()) {
|
||||
undoBarController.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -133,6 +139,15 @@ public class QueueFragment extends Fragment {
|
|||
this.activity.set((MainActivity) activity);
|
||||
}
|
||||
|
||||
public void onEventMainThread(QueueEvent event) {
|
||||
Log.d(TAG, "onEvent(" + event + ")");
|
||||
if(event.action == QueueEvent.Action.REMOVED) {
|
||||
undoBarController.showUndoBar(false, getString(R.string.removed_from_queue),
|
||||
new FeedItemUndoToken(event.item, event.position));
|
||||
}
|
||||
startItemLoader();
|
||||
}
|
||||
|
||||
private void saveScrollPosition() {
|
||||
SharedPreferences prefs = getActivity().getSharedPreferences(PREFS, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
|
@ -295,7 +310,7 @@ public class QueueFragment extends Fragment {
|
|||
DBWriter.moveQueueItemToBottom(getActivity(), selectedItem.getId(), true);
|
||||
return true;
|
||||
case R.id.remove_from_queue_item:
|
||||
DBWriter.removeQueueItem(getActivity(), selectedItem.getId(), false);
|
||||
DBWriter.removeQueueItem(getActivity(), selectedItem, false);
|
||||
return true;
|
||||
default:
|
||||
return super.onContextItemSelected(item);
|
||||
|
@ -343,29 +358,42 @@ public class QueueFragment extends Fragment {
|
|||
|
||||
@Override
|
||||
public void remove(int which) {
|
||||
Log.d(TAG, "remove("+which+")");
|
||||
Log.d(TAG, "remove(" + which + ")");
|
||||
stopItemLoader();
|
||||
FeedItem item = (FeedItem) listView.getAdapter().getItem(which);
|
||||
DBWriter.removeQueueItem(getActivity(), item.getId(), true);
|
||||
undoBarController.showUndoBar(false,
|
||||
getString(R.string.removed_from_queue), new FeedItemUndoToken(item,
|
||||
which)
|
||||
);
|
||||
DBWriter.markItemRead(getActivity(), item.getId(), true);
|
||||
DBWriter.removeQueueItem(getActivity(), item, true);
|
||||
}
|
||||
});
|
||||
|
||||
undoBarController = new UndoBarController(root.findViewById(R.id.undobar), new UndoBarController.UndoListener() {
|
||||
@Override
|
||||
public void onUndo(Parcelable token) {
|
||||
// Perform the undo
|
||||
FeedItemUndoToken undoToken = (FeedItemUndoToken) token;
|
||||
if (token != null) {
|
||||
long itemId = undoToken.getFeedItemId();
|
||||
int position = undoToken.getPosition();
|
||||
DBWriter.addQueueItemAt(getActivity(), itemId, position, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
undoBarController = new UndoBarController<FeedItemUndoToken>(root.findViewById(R.id.undobar),
|
||||
new UndoBarController.UndoListener<FeedItemUndoToken>() {
|
||||
|
||||
private final Context context = getActivity();
|
||||
|
||||
@Override
|
||||
public void onUndo(FeedItemUndoToken token) {
|
||||
if (token != null) {
|
||||
long itemId = token.getFeedItemId();
|
||||
int position = token.getPosition();
|
||||
DBWriter.markItemRead(context, itemId, false);
|
||||
DBWriter.addQueueItemAt(context, itemId, position, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHide(FeedItemUndoToken token) {
|
||||
if (token != null && context != null) {
|
||||
long itemId = token.getFeedItemId();
|
||||
FeedItem item = DBReader.getFeedItem(context, itemId);
|
||||
FeedMedia media = item.getMedia();
|
||||
if(media != null && media.hasAlmostEnded() && UserPreferences.isAutoDelete()) {
|
||||
DBWriter.deleteFeedMediaOfItem(context, media.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
registerForContextMenu(listView);
|
||||
|
|
|
@ -13,6 +13,7 @@ import android.view.MenuItem;
|
|||
import android.view.View;
|
||||
import android.widget.ListView;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import de.danoeh.antennapod.R;
|
||||
|
@ -23,9 +24,7 @@ import de.danoeh.antennapod.core.feed.Feed;
|
|||
import de.danoeh.antennapod.core.feed.FeedComponent;
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.feed.SearchResult;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.FeedSearcher;
|
||||
import de.danoeh.antennapod.core.util.QueueAccess;
|
||||
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
|
||||
import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
|
||||
|
||||
|
@ -44,8 +43,6 @@ public class SearchFragment extends ListFragment {
|
|||
private boolean viewCreated = false;
|
||||
private boolean itemsLoaded = false;
|
||||
|
||||
private QueueAccess queue;
|
||||
|
||||
/**
|
||||
* Create a new SearchFragment that searches all feeds.
|
||||
*/
|
||||
|
@ -165,8 +162,7 @@ public class SearchFragment extends ListFragment {
|
|||
@Override
|
||||
public void update(EventDistributor eventDistributor, Integer arg) {
|
||||
if ((arg & (EventDistributor.UNREAD_ITEMS_UPDATE
|
||||
| EventDistributor.DOWNLOAD_HANDLED
|
||||
| EventDistributor.QUEUE_UPDATE)) != 0) {
|
||||
| EventDistributor.DOWNLOAD_HANDLED)) != 0) {
|
||||
startSearchTask();
|
||||
}
|
||||
}
|
||||
|
@ -209,17 +205,16 @@ public class SearchFragment extends ListFragment {
|
|||
}
|
||||
}
|
||||
|
||||
private class SearchTask extends AsyncTask<Bundle, Void, Object[]> {
|
||||
private class SearchTask extends AsyncTask<Bundle, Void, List<SearchResult>> {
|
||||
@Override
|
||||
protected Object[] doInBackground(Bundle... params) {
|
||||
protected List<SearchResult> doInBackground(Bundle... params) {
|
||||
String query = params[0].getString(ARG_QUERY);
|
||||
long feed = params[0].getLong(ARG_FEED);
|
||||
Context context = getActivity();
|
||||
if (context != null) {
|
||||
return new Object[]{FeedSearcher.performSearch(context, query, feed),
|
||||
QueueAccess.IDListAccess(DBReader.getQueueIDList(context))};
|
||||
return FeedSearcher.performSearch(context, query, feed);
|
||||
} else {
|
||||
return null;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -232,12 +227,11 @@ public class SearchFragment extends ListFragment {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Object[] results) {
|
||||
protected void onPostExecute(List<SearchResult> results) {
|
||||
super.onPostExecute(results);
|
||||
if (results != null) {
|
||||
itemsLoaded = true;
|
||||
searchResults = (List<SearchResult>) results[0];
|
||||
queue = (QueueAccess) results[1];
|
||||
searchResults = results;
|
||||
if (viewCreated) {
|
||||
onFragmentLoaded();
|
||||
}
|
||||
|
|
|
@ -4,15 +4,19 @@ import android.content.Context;
|
|||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
|
||||
import de.danoeh.antennapod.core.BuildConfig;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.core.BuildConfig;
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction;
|
||||
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction.Action;
|
||||
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
|
||||
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
||||
import de.danoeh.antennapod.core.storage.DBTasks;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequestException;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
import de.danoeh.antennapod.core.util.QueueAccess;
|
||||
import de.danoeh.antennapod.core.util.LongList;
|
||||
import de.danoeh.antennapod.core.util.ShareUtils;
|
||||
|
||||
/**
|
||||
|
@ -52,7 +56,7 @@ public class FeedItemMenuHandler {
|
|||
* @return Returns true if selectedItem is not null.
|
||||
*/
|
||||
public static boolean onPrepareMenu(MenuInterface mi,
|
||||
FeedItem selectedItem, boolean showExtendedMenu, QueueAccess queueAccess) {
|
||||
FeedItem selectedItem, boolean showExtendedMenu, LongList queueAccess) {
|
||||
if (selectedItem == null) {
|
||||
return false;
|
||||
}
|
||||
|
@ -122,7 +126,7 @@ public class FeedItemMenuHandler {
|
|||
* @return true if selectedItem is not null.
|
||||
*/
|
||||
public static boolean onPrepareMenu(MenuInterface mi,
|
||||
FeedItem selectedItem, boolean showExtendedMenu, QueueAccess queueAccess, int... excludeIds) {
|
||||
FeedItem selectedItem, boolean showExtendedMenu, LongList queueAccess, int... excludeIds) {
|
||||
boolean rc = onPrepareMenu(mi, selectedItem, showExtendedMenu, queueAccess);
|
||||
if (rc && excludeIds != null) {
|
||||
for (int id : excludeIds) {
|
||||
|
@ -156,15 +160,33 @@ public class FeedItemMenuHandler {
|
|||
break;
|
||||
case R.id.mark_read_item:
|
||||
DBWriter.markItemRead(context, selectedItem, true, true);
|
||||
if(GpodnetPreferences.loggedIn()) {
|
||||
FeedMedia media = selectedItem.getMedia();
|
||||
GpodnetEpisodeAction actionPlay = new GpodnetEpisodeAction.Builder(selectedItem, Action.PLAY)
|
||||
.currentDeviceId()
|
||||
.currentTimestamp()
|
||||
.started(media.getDuration() / 1000)
|
||||
.position(media.getDuration() / 1000)
|
||||
.total(media.getDuration() / 1000)
|
||||
.build();
|
||||
GpodnetPreferences.enqueueEpisodeAction(actionPlay);
|
||||
}
|
||||
break;
|
||||
case R.id.mark_unread_item:
|
||||
DBWriter.markItemRead(context, selectedItem, false, true);
|
||||
if(GpodnetPreferences.loggedIn()) {
|
||||
GpodnetEpisodeAction actionNew = new GpodnetEpisodeAction.Builder(selectedItem, Action.NEW)
|
||||
.currentDeviceId()
|
||||
.currentTimestamp()
|
||||
.build();
|
||||
GpodnetPreferences.enqueueEpisodeAction(actionNew);
|
||||
}
|
||||
break;
|
||||
case R.id.add_to_queue_item:
|
||||
DBWriter.addQueueItem(context, selectedItem.getId());
|
||||
break;
|
||||
case R.id.remove_from_queue_item:
|
||||
DBWriter.removeQueueItem(context, selectedItem.getId(), true);
|
||||
DBWriter.removeQueueItem(context, selectedItem, true);
|
||||
break;
|
||||
case R.id.stream_item:
|
||||
DBTasks.playMedia(context, selectedItem.getMedia(), true, true,
|
||||
|
|
|
@ -343,6 +343,7 @@ public class PreferenceController {
|
|||
}
|
||||
});
|
||||
buildUpdateIntervalPreference();
|
||||
buildSmartMarkAsPlayedPreference();
|
||||
buildAutodownloadSelectedNetworsPreference();
|
||||
setSelectedNetworksEnabled(UserPreferences
|
||||
.isEnableAutodownloadWifiFilter());
|
||||
|
@ -403,6 +404,24 @@ public class PreferenceController {
|
|||
|
||||
}
|
||||
|
||||
private void buildSmartMarkAsPlayedPreference() {
|
||||
final Resources res = ui.getActivity().getResources();
|
||||
|
||||
ListPreference pref = (ListPreference) ui.findPreference(UserPreferences.PREF_SMART_MARK_AS_PLAYED_SECS);
|
||||
String[] values = res.getStringArray(
|
||||
R.array.smart_mark_as_played_values);
|
||||
String[] entries = new String[values.length];
|
||||
for (int x = 0; x < values.length; x++) {
|
||||
if(x == 0) {
|
||||
entries[x] = res.getString(R.string.pref_smart_mark_as_played_disabled);
|
||||
} else {
|
||||
Integer v = Integer.parseInt(values[x]);
|
||||
entries[x] = v + " " + res.getString(R.string.time_unit_seconds);
|
||||
}
|
||||
}
|
||||
pref.setEntries(entries);
|
||||
}
|
||||
|
||||
private void setSelectedNetworksEnabled(boolean b) {
|
||||
if (selectedNetworks != null) {
|
||||
for (Preference p : selectedNetworks) {
|
||||
|
@ -430,7 +449,6 @@ public class PreferenceController {
|
|||
.setEnabled(UserPreferences.isEnableAutodownload());
|
||||
}
|
||||
|
||||
|
||||
private void setParallelDownloadsText(int downloads) {
|
||||
final Resources res = ui.getActivity().getResources();
|
||||
|
||||
|
|
|
@ -12,14 +12,18 @@ import android.util.Log;
|
|||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.widget.RemoteViews;
|
||||
import de.danoeh.antennapod.core.BuildConfig;
|
||||
|
||||
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.receiver.MediaButtonReceiver;
|
||||
import de.danoeh.antennapod.receiver.PlayerWidget;
|
||||
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
||||
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.util.Converter;
|
||||
import de.danoeh.antennapod.core.util.playback.Playable;
|
||||
import de.danoeh.antennapod.receiver.PlayerWidget;
|
||||
|
||||
/** Updates the state of the player widget */
|
||||
public class PlayerWidgetService extends Service {
|
||||
|
@ -39,8 +43,7 @@ public class PlayerWidgetService extends Service {
|
|||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Service created");
|
||||
Log.d(TAG, "Service created");
|
||||
isUpdating = false;
|
||||
psLock = new Object();
|
||||
}
|
||||
|
@ -48,8 +51,24 @@ public class PlayerWidgetService extends Service {
|
|||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Service is about to be destroyed");
|
||||
Log.d(TAG, "Service is about to be destroyed");
|
||||
|
||||
Playable playable = playbackService.getPlayable();
|
||||
if(playable != null && playable instanceof FeedMedia) {
|
||||
FeedMedia media = (FeedMedia) playable;
|
||||
if(media.hasAlmostEnded()) {
|
||||
Log.d(TAG, "smart mark as read");
|
||||
FeedItem item = media.getItem();
|
||||
DBWriter.markItemRead(this, item, true, false);
|
||||
DBWriter.removeQueueItem(this, item, false);
|
||||
DBWriter.addItemToPlaybackHistory(this, media);
|
||||
if (UserPreferences.isAutoDelete()) {
|
||||
Log.d(TAG, "Delete " + media.toString());
|
||||
DBWriter.deleteFeedMediaOfItem(this, media.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
unbindService(mConnection);
|
||||
} catch (IllegalArgumentException e) {
|
||||
|
@ -72,9 +91,7 @@ public class PlayerWidgetService extends Service {
|
|||
startViewUpdaterIfNotRunning();
|
||||
}
|
||||
} else {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG,
|
||||
"Service was called while updating. Ignoring update request");
|
||||
Log.d(TAG, "Service was called while updating. Ignoring update request");
|
||||
}
|
||||
return Service.START_NOT_STICKY;
|
||||
}
|
||||
|
@ -153,8 +170,7 @@ public class PlayerWidgetService extends Service {
|
|||
|
||||
private ServiceConnection mConnection = new ServiceConnection() {
|
||||
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Connection to service established");
|
||||
Log.d(TAG, "Connection to service established");
|
||||
synchronized (psLock) {
|
||||
playbackService = ((PlaybackService.LocalBinder) service)
|
||||
.getService();
|
||||
|
@ -166,8 +182,7 @@ public class PlayerWidgetService extends Service {
|
|||
public void onServiceDisconnected(ComponentName name) {
|
||||
synchronized (psLock) {
|
||||
playbackService = null;
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Disconnected from service");
|
||||
Log.d(TAG, "Disconnected from service");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -95,6 +95,18 @@
|
|||
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_alignLeft="@id/butRev"
|
||||
android:layout_alignRight="@id/butRev"
|
||||
android:gravity="center"
|
||||
android:text="30"
|
||||
android:textSize="8dp"
|
||||
android:clickable="false"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/butFF"
|
||||
android:layout_width="@dimen/audioplayer_playercontrols_length"
|
||||
|
@ -106,6 +118,18 @@
|
|||
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_alignLeft="@id/butFF"
|
||||
android:layout_alignRight="@id/butFF"
|
||||
android:gravity="center"
|
||||
android:text="30"
|
||||
android:textSize="8dp"
|
||||
android:clickable="false"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/butPlaybackSpeed"
|
||||
android:layout_width="@dimen/audioplayer_playercontrols_length"
|
||||
|
|
|
@ -59,6 +59,13 @@
|
|||
android:key="prefAutoDelete"
|
||||
android:summary="@string/pref_auto_delete_sum"
|
||||
android:title="@string/pref_auto_delete_title"/>
|
||||
<ListPreference
|
||||
android:defaultValue="30"
|
||||
android:entries="@array/smart_mark_as_played_values"
|
||||
android:entryValues="@array/smart_mark_as_played_values"
|
||||
android:key="prefSmartMarkAsPlayedSecs"
|
||||
android:summary="@string/pref_smart_mark_as_played_sum"
|
||||
android:title="@string/pref_smart_mark_as_played_title"/>
|
||||
<Preference
|
||||
android:key="prefPlaybackSpeedLauncher"
|
||||
android:summary="@string/pref_playback_speed_sum"
|
||||
|
@ -71,14 +78,6 @@
|
|||
android:summary="@string/pref_pausePlaybackForFocusLoss_sum"
|
||||
android:title="@string/pref_pausePlaybackForFocusLoss_title" />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="30"
|
||||
android:entries="@array/seek_delta_values"
|
||||
android:entryValues="@array/seek_delta_values"
|
||||
android:key="prefSeekDeltaSecs"
|
||||
android:summary="@string/pref_seek_delta_sum"
|
||||
android:title="@string/pref_seek_delta_title" />
|
||||
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory android:title="@string/network_pref">
|
||||
<ListPreference
|
||||
|
|
|
@ -45,4 +45,5 @@ dependencies {
|
|||
compile 'com.squareup.okhttp:okhttp-urlconnection:2.2.0'
|
||||
compile 'com.squareup.okio:okio:1.2.0'
|
||||
compile 'com.nineoldandroids:library:2.4.0'
|
||||
compile 'de.greenrobot:eventbus:2.4.0'
|
||||
}
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
package de.danoeh.antennapod.core.util;
|
||||
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class DateUtilsTest extends AndroidTestCase {
|
||||
|
||||
public void testParseDateWithMicroseconds() throws Exception {
|
||||
GregorianCalendar exp = new GregorianCalendar(2015, 2, 28, 13, 31, 4);
|
||||
Date expected = new Date(exp.getTimeInMillis() + 963);
|
||||
Date actual = DateUtils.parse("2015-03-28T13:31:04.963870");
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
public void testParseDateWithCentiseconds() throws Exception {
|
||||
GregorianCalendar exp = new GregorianCalendar(2015, 2, 28, 13, 31, 4);
|
||||
Date expected = new Date(exp.getTimeInMillis() + 960);
|
||||
Date actual = DateUtils.parse("2015-03-28T13:31:04.96");
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
public void testParseDateWithDeciseconds() throws Exception {
|
||||
GregorianCalendar exp = new GregorianCalendar(2015, 2, 28, 13, 31, 4);
|
||||
Date expected = new Date(exp.getTimeInMillis() + 900);
|
||||
Date actual = DateUtils.parse("2015-03-28T13:31:04.9");
|
||||
assertEquals(expected.getTime()/1000, actual.getTime()/1000);
|
||||
assertEquals(900, actual.getTime()%1000);
|
||||
}
|
||||
|
||||
public void testParseDateWithMicrosecondsAndTimezone() throws Exception {
|
||||
GregorianCalendar exp = new GregorianCalendar(2015, 2, 28, 6, 31, 4);
|
||||
exp.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
Date expected = new Date(exp.getTimeInMillis() + 963);
|
||||
Date actual = DateUtils.parse("2015-03-28T13:31:04.963870 +0700");
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
public void testParseDateWithCentisecondsAndTimezone() throws Exception {
|
||||
GregorianCalendar exp = new GregorianCalendar(2015, 2, 28, 6, 31, 4);
|
||||
exp.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
Date expected = new Date(exp.getTimeInMillis() + 960);
|
||||
Date actual = DateUtils.parse("2015-03-28T13:31:04.96 +0700");
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
public void testParseDateWithDecisecondsAndTimezone() throws Exception {
|
||||
GregorianCalendar exp = new GregorianCalendar(2015, 2, 28, 6, 31, 4);
|
||||
exp.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
Date expected = new Date(exp.getTimeInMillis() + 900);
|
||||
Date actual = DateUtils.parse("2015-03-28T13:31:04.9 +0700");
|
||||
assertEquals(expected.getTime()/1000, actual.getTime()/1000);
|
||||
assertEquals(900, actual.getTime()%1000);
|
||||
}
|
||||
|
||||
}
|
|
@ -14,13 +14,13 @@
|
|||
|
||||
package com.aocate.media;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.MediaPlayer;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class AndroidMediaPlayer extends MediaPlayerImpl {
|
||||
private final static String AMP_TAG = "AocateAndroidMediaPlayer";
|
||||
|
||||
|
|
|
@ -5,8 +5,6 @@ import android.util.Log;
|
|||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
import de.danoeh.antennapod.core.BuildConfig;
|
||||
|
||||
import java.util.AbstractQueue;
|
||||
import java.util.Observable;
|
||||
import java.util.Observer;
|
||||
|
@ -26,7 +24,6 @@ public class EventDistributor extends Observable {
|
|||
|
||||
public static final int FEED_LIST_UPDATE = 1;
|
||||
public static final int UNREAD_ITEMS_UPDATE = 2;
|
||||
public static final int QUEUE_UPDATE = 4;
|
||||
public static final int DOWNLOADLOG_UPDATE = 8;
|
||||
public static final int PLAYBACK_HISTORY_UPDATE = 16;
|
||||
public static final int DOWNLOAD_QUEUED = 32;
|
||||
|
@ -71,23 +68,17 @@ public class EventDistributor extends Observable {
|
|||
|
||||
private void processEventQueue() {
|
||||
Integer result = 0;
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG,
|
||||
"Processing event queue. Number of events: "
|
||||
+ events.size());
|
||||
Log.d(TAG, "Processing event queue. Number of events: " + events.size());
|
||||
for (Integer current = events.poll(); current != null; current = events
|
||||
.poll()) {
|
||||
result |= current;
|
||||
}
|
||||
if (result != 0) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Notifying observers. Data: " + result);
|
||||
Log.d(TAG, "Notifying observers. Data: " + result);
|
||||
setChanged();
|
||||
notifyObservers(result);
|
||||
} else {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG,
|
||||
"Event queue didn't contain any new events. Observers will not be notified.");
|
||||
Log.d(TAG, "Event queue didn't contain any new events. Observers will not be notified.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,10 +96,6 @@ public class EventDistributor extends Observable {
|
|||
addEvent(UNREAD_ITEMS_UPDATE);
|
||||
}
|
||||
|
||||
public void sendQueueUpdateBroadcast() {
|
||||
addEvent(QUEUE_UPDATE);
|
||||
}
|
||||
|
||||
public void sendFeedUpdateBroadcast() {
|
||||
addEvent(FEED_LIST_UPDATE);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,9 @@ package de.danoeh.antennapod.core.feed;
|
|||
|
||||
import android.net.Uri;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
|
@ -384,4 +387,9 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
|
|||
public boolean hasChapters() {
|
||||
return hasChapters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.util.concurrent.Callable;
|
|||
|
||||
import de.danoeh.antennapod.core.ClientConfig;
|
||||
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.util.ChapterUtils;
|
||||
|
@ -146,6 +147,11 @@ public class FeedMedia extends FeedFile implements Playable {
|
|||
}
|
||||
|
||||
|
||||
public boolean hasAlmostEnded() {
|
||||
int smartMarkAsPlayedSecs = UserPreferences.getSmartMarkAsPlayedSecs();
|
||||
return this.position >= this.duration - smartMarkAsPlayedSecs * 1000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTypeAsInt() {
|
||||
return FEEDFILETYPE_FEEDMEDIA;
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
package de.danoeh.antennapod.core.feed;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class QueueEvent {
|
||||
|
||||
public enum Action {
|
||||
ADDED, ADDED_ITEMS, REMOVED, CLEARED, DELETED_MEDIA, SORTED
|
||||
}
|
||||
|
||||
public final Action action;
|
||||
public final FeedItem item;
|
||||
public final int position;
|
||||
public final List<FeedItem> items;
|
||||
|
||||
public QueueEvent(Action action) {
|
||||
this(action, null, null, -1);
|
||||
}
|
||||
|
||||
public QueueEvent(Action action, FeedItem item) {
|
||||
this(action, item, null, -1);
|
||||
}
|
||||
|
||||
public QueueEvent(Action action, FeedItem item, int position) {
|
||||
this(action, item, null, position);
|
||||
}
|
||||
|
||||
public QueueEvent(Action action, List<FeedItem> items) {
|
||||
this(action, null, items, -1);
|
||||
}
|
||||
|
||||
private QueueEvent(Action action, FeedItem item, List<FeedItem> items, int position) {
|
||||
this.action = action;
|
||||
this.item = item;
|
||||
this.items = items;
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
|
||||
.append("action", action)
|
||||
.append("item", item)
|
||||
.append("items", items)
|
||||
.append("position", position)
|
||||
.toString();
|
||||
}
|
||||
}
|
|
@ -45,6 +45,9 @@ import javax.security.auth.x500.X500Principal;
|
|||
|
||||
import de.danoeh.antennapod.core.ClientConfig;
|
||||
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetDevice;
|
||||
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction;
|
||||
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeActionGetResponse;
|
||||
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeActionPostResponse;
|
||||
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetPodcast;
|
||||
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetSubscriptionChange;
|
||||
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetTag;
|
||||
|
@ -536,6 +539,85 @@ public class GpodnetService {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the episode actions
|
||||
* <p/>
|
||||
* This method requires authentication.
|
||||
*
|
||||
* @param episodeActions Collection of episode actions.
|
||||
* @return a GpodnetUploadChangesResponse. See {@link de.danoeh.antennapod.core.gpoddernet.model.GpodnetUploadChangesResponse}
|
||||
* for details.
|
||||
* @throws java.lang.IllegalArgumentException if username, deviceId, added or removed is null.
|
||||
* @throws de.danoeh.antennapod.core.gpoddernet.GpodnetServiceException if added or removed contain duplicates or if there
|
||||
* is an authentication error.
|
||||
*/
|
||||
public GpodnetEpisodeActionPostResponse uploadEpisodeActions(Collection<GpodnetEpisodeAction> episodeActions)
|
||||
throws GpodnetServiceException {
|
||||
|
||||
Validate.notNull(episodeActions);
|
||||
|
||||
String username = GpodnetPreferences.getUsername();
|
||||
|
||||
try {
|
||||
URL url = new URI(BASE_SCHEME, BASE_HOST, String.format(
|
||||
"/api/2/episodes/%s.json", username), null).toURL();
|
||||
|
||||
final JSONArray list = new JSONArray();
|
||||
for(GpodnetEpisodeAction episodeAction : episodeActions) {
|
||||
JSONObject obj = episodeAction.writeToJSONObject();
|
||||
if(obj != null) {
|
||||
list.put(obj);
|
||||
}
|
||||
}
|
||||
|
||||
RequestBody body = RequestBody.create(JSON, list.toString());
|
||||
Request.Builder request = new Request.Builder().post(body).url(url);
|
||||
|
||||
final String response = executeRequest(request);
|
||||
return GpodnetEpisodeActionPostResponse.fromJSONObject(response);
|
||||
} catch (JSONException | MalformedURLException | URISyntaxException e) {
|
||||
e.printStackTrace();
|
||||
throw new GpodnetServiceException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all subscription changes of a specific device.
|
||||
* <p/>
|
||||
* This method requires authentication.
|
||||
*
|
||||
* @param timestamp A timestamp that can be used to receive all changes since a
|
||||
* specific point in time.
|
||||
* @throws IllegalArgumentException If username or deviceId is null.
|
||||
* @throws GpodnetServiceAuthenticationException If there is an authentication error.
|
||||
*/
|
||||
public GpodnetEpisodeActionGetResponse getEpisodeChanges(long timestamp) throws GpodnetServiceException {
|
||||
|
||||
String username = GpodnetPreferences.getUsername();
|
||||
|
||||
String params = String.format("since=%d", timestamp);
|
||||
String path = String.format("/api/2/episodes/%s.json",
|
||||
username);
|
||||
try {
|
||||
URL url = new URI(BASE_SCHEME, null, BASE_HOST, -1, path, params,
|
||||
null).toURL();
|
||||
Request.Builder request = new Request.Builder().url(url);
|
||||
|
||||
String response = executeRequest(request);
|
||||
JSONObject json = new JSONObject(response);
|
||||
return readEpisodeActionsFromJSONObject(json);
|
||||
} catch (URISyntaxException e) {
|
||||
e.printStackTrace();
|
||||
throw new IllegalStateException(e);
|
||||
} catch (JSONException | MalformedURLException e) {
|
||||
e.printStackTrace();
|
||||
throw new GpodnetServiceException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Logs in a specific user. This method must be called if any of the methods
|
||||
* that require authentication is used.
|
||||
|
@ -773,4 +855,24 @@ public class GpodnetService {
|
|||
long timestamp = object.getLong("timestamp");
|
||||
return new GpodnetSubscriptionChange(added, removed, timestamp);
|
||||
}
|
||||
|
||||
private GpodnetEpisodeActionGetResponse readEpisodeActionsFromJSONObject(
|
||||
JSONObject object) throws JSONException {
|
||||
Validate.notNull(object);
|
||||
|
||||
List<GpodnetEpisodeAction> episodeActions = new ArrayList<GpodnetEpisodeAction>();
|
||||
|
||||
long timestamp = object.getLong("timestamp");
|
||||
JSONArray jsonActions = object.getJSONArray("actions");
|
||||
for(int i=0; i < jsonActions.length(); i++) {
|
||||
JSONObject jsonAction = jsonActions.getJSONObject(i);
|
||||
GpodnetEpisodeAction episodeAction = GpodnetEpisodeAction.readFromJSONObject(jsonAction);
|
||||
if(episodeAction != null) {
|
||||
episodeActions.add(episodeAction);
|
||||
}
|
||||
}
|
||||
return new GpodnetEpisodeActionGetResponse(episodeActions, timestamp);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,315 @@
|
|||
package de.danoeh.antennapod.core.gpoddernet.model;
|
||||
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
|
||||
import de.danoeh.antennapod.core.util.DateUtils;
|
||||
|
||||
public class GpodnetEpisodeAction {
|
||||
|
||||
private static final String TAG = "GpodnetEpisodeAction";
|
||||
|
||||
public enum Action {
|
||||
NEW, DOWNLOAD, PLAY, DELETE
|
||||
}
|
||||
|
||||
private final String podcast;
|
||||
private final String episode;
|
||||
private final String deviceId;
|
||||
private final Action action;
|
||||
private final Date timestamp;
|
||||
private final int started;
|
||||
private final int position;
|
||||
private final int total;
|
||||
|
||||
private GpodnetEpisodeAction(Builder builder) {
|
||||
this.podcast = builder.podcast;
|
||||
this.episode = builder.episode;
|
||||
this.action = builder.action;
|
||||
this.deviceId = builder.deviceId;
|
||||
this.timestamp = builder.timestamp;
|
||||
this.started = builder.started;
|
||||
this.position = builder.position;
|
||||
this.total = builder.total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an episode action object from a String representation. The representation includes
|
||||
* all mandatory and optional attributes
|
||||
*
|
||||
* @param s String representation (output from {@link #writeToString()})
|
||||
* @return episode action object, or null if s is invalid
|
||||
*/
|
||||
public static GpodnetEpisodeAction readFromString(String s) {
|
||||
String[] fields = s.split("\t");
|
||||
if(fields.length != 8) {
|
||||
return null;
|
||||
}
|
||||
String podcast = fields[0];
|
||||
String episode = fields[1];
|
||||
String deviceId = fields[2];
|
||||
try {
|
||||
Action action = Action.valueOf(fields[3]);
|
||||
GpodnetEpisodeAction result = new Builder(podcast, episode, action)
|
||||
.deviceId(deviceId)
|
||||
.timestamp(new Date(Long.valueOf(fields[4])))
|
||||
.started(Integer.valueOf(fields[5]))
|
||||
.position(Integer.valueOf(fields[6]))
|
||||
.total(Integer.valueOf(fields[7]))
|
||||
.build();
|
||||
return result;
|
||||
} catch(IllegalArgumentException e) {
|
||||
Log.e(TAG, "readFromString(" + s + "): " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an episode action object from JSON representation. Mandatory fields are "podcast",
|
||||
* "episode" and "action".
|
||||
*
|
||||
* @param object JSON representation
|
||||
* @return episode action object, or null if mandatory values are missing
|
||||
*/
|
||||
public static GpodnetEpisodeAction readFromJSONObject(JSONObject object) {
|
||||
String podcast = object.optString("podcast", null);
|
||||
String episode = object.optString("episode", null);
|
||||
String actionString = object.optString("action", null);
|
||||
if(StringUtils.isEmpty(podcast) || StringUtils.isEmpty(episode) || StringUtils.isEmpty(actionString)) {
|
||||
return null;
|
||||
}
|
||||
GpodnetEpisodeAction.Action action = GpodnetEpisodeAction.Action.valueOf(actionString.toUpperCase());
|
||||
String deviceId = object.optString("device", "");
|
||||
GpodnetEpisodeAction.Builder builder = new GpodnetEpisodeAction.Builder(podcast, episode, action)
|
||||
.deviceId(deviceId);
|
||||
String utcTimestamp = object.optString("timestamp", null);
|
||||
if(StringUtils.isNotEmpty(utcTimestamp)) {
|
||||
builder.timestamp(DateUtils.parse(utcTimestamp));
|
||||
}
|
||||
if(action == GpodnetEpisodeAction.Action.PLAY) {
|
||||
int started = object.optInt("started", -1);
|
||||
int position = object.optInt("position", -1);
|
||||
int total = object.optInt("total", -1);
|
||||
if(started >= 0 && position > 0 && total > 0) {
|
||||
builder
|
||||
.started(started)
|
||||
.position(position)
|
||||
.total(total);
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public String getPodcast() {
|
||||
return this.podcast;
|
||||
}
|
||||
|
||||
public String getEpisode() {
|
||||
return this.episode;
|
||||
}
|
||||
|
||||
public String getDeviceId() {
|
||||
return this.deviceId;
|
||||
}
|
||||
|
||||
public Action getAction() {
|
||||
return this.action;
|
||||
}
|
||||
|
||||
public String getActionString() {
|
||||
return this.action.name().toLowerCase();
|
||||
}
|
||||
|
||||
public Date getTimestamp() {
|
||||
return this.timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the position (in seconds) at which the client started playback
|
||||
*
|
||||
* @return start position (in seconds)
|
||||
*/
|
||||
public int getStarted() {
|
||||
return this.started;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the position (in seconds) at which the client stopped playback
|
||||
*
|
||||
* @return stop position (in seconds)
|
||||
*/
|
||||
public int getPosition() {
|
||||
return this.position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total length of the file in seconds.
|
||||
*
|
||||
* @return total length in seconds
|
||||
*/
|
||||
public int getTotal() {
|
||||
return this.total;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if(o == null) return false;
|
||||
if(this == o) return true;
|
||||
if(this.getClass() != o.getClass()) return false;
|
||||
GpodnetEpisodeAction that = (GpodnetEpisodeAction)o;
|
||||
return new EqualsBuilder()
|
||||
.append(this.podcast, that.podcast)
|
||||
.append(this.episode, that.episode)
|
||||
.append(this.deviceId, that.deviceId)
|
||||
.append(this.action, that.action)
|
||||
.append(this.timestamp, that.timestamp)
|
||||
.append(this.started, that.started)
|
||||
.append(this.position, that.position)
|
||||
.append(this.total, that.total)
|
||||
.isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder()
|
||||
.append(this.podcast)
|
||||
.append(this.episode)
|
||||
.append(this.deviceId)
|
||||
.append(this.action)
|
||||
.append(this.timestamp)
|
||||
.append(this.started)
|
||||
.append(this.position)
|
||||
.append(this.total)
|
||||
.toHashCode();
|
||||
}
|
||||
|
||||
public String writeToString() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
result.append(this.podcast).append("\t");
|
||||
result.append(this.episode).append("\t");
|
||||
result.append(this.deviceId).append("\t");
|
||||
result.append(this.action).append("\t");
|
||||
result.append(this.timestamp.getTime()).append("\t");
|
||||
result.append(String.valueOf(this.started)).append("\t");
|
||||
result.append(String.valueOf(this.position)).append("\t");
|
||||
result.append(String.valueOf(this.total));
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JSON object representation of this object
|
||||
*
|
||||
* @return JSON object representation, or null if the object is invalid
|
||||
*/
|
||||
public JSONObject writeToJSONObject() {
|
||||
JSONObject obj = new JSONObject();
|
||||
try {
|
||||
obj.putOpt("podcast", this.podcast);
|
||||
obj.putOpt("episode", this.episode);
|
||||
obj.put("device", this.deviceId);
|
||||
obj.put("action", this.getActionString());
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US);
|
||||
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
obj.put("timestamp",formatter.format(this.timestamp));
|
||||
if (this.getAction() == Action.PLAY) {
|
||||
obj.put("started", this.started);
|
||||
obj.put("position", this.position);
|
||||
obj.put("total", this.total);
|
||||
}
|
||||
} catch(JSONException e) {
|
||||
Log.e(TAG, "writeToJSONObject(): " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
// mandatory
|
||||
private final String podcast;
|
||||
private final String episode;
|
||||
private final Action action;
|
||||
|
||||
// optional
|
||||
private String deviceId = "";
|
||||
private Date timestamp;
|
||||
private int started = -1;
|
||||
private int position = -1;
|
||||
private int total = -1;
|
||||
|
||||
public Builder(FeedItem item, Action action) {
|
||||
this(item.getFeed().getDownload_url(), item.getItemIdentifier(), action);
|
||||
}
|
||||
|
||||
public Builder(String podcast, String episode, Action action) {
|
||||
this.podcast = podcast;
|
||||
this.episode = episode;
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public Builder deviceId(String deviceId) {
|
||||
this.deviceId = deviceId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder currentDeviceId() {
|
||||
return deviceId(GpodnetPreferences.getDeviceID());
|
||||
}
|
||||
|
||||
public Builder timestamp(Date timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder currentTimestamp() {
|
||||
return timestamp(new Date());
|
||||
}
|
||||
|
||||
public Builder started(int seconds) {
|
||||
if(action == Action.PLAY) {
|
||||
this.started = seconds;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder position(int seconds) {
|
||||
if(action == Action.PLAY) {
|
||||
this.position = seconds;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder total(int seconds) {
|
||||
if(action == Action.PLAY) {
|
||||
this.total = seconds;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public GpodnetEpisodeAction build() {
|
||||
return new GpodnetEpisodeAction(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package de.danoeh.antennapod.core.gpoddernet.model;
|
||||
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class GpodnetEpisodeActionGetResponse {
|
||||
|
||||
private final List<GpodnetEpisodeAction> episodeActions;
|
||||
private final long timestamp;
|
||||
|
||||
public GpodnetEpisodeActionGetResponse(List<GpodnetEpisodeAction> episodeActions, long timestamp) {
|
||||
Validate.notNull(episodeActions);
|
||||
this.episodeActions = episodeActions;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public List<GpodnetEpisodeAction> getEpisodeActions() {
|
||||
return this.episodeActions;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return this.timestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
package de.danoeh.antennapod.core.gpoddernet.model;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class GpodnetEpisodeActionPostResponse {
|
||||
|
||||
/**
|
||||
* timestamp/ID that can be used for requesting changes since this upload.
|
||||
*/
|
||||
public final long timestamp;
|
||||
|
||||
/**
|
||||
* URLs that should be updated. The key of the map is the original URL, the value of the map
|
||||
* is the sanitized URL.
|
||||
*/
|
||||
public final Map<String, String> updatedUrls;
|
||||
|
||||
public GpodnetEpisodeActionPostResponse(long timestamp, Map<String, String> updatedUrls) {
|
||||
this.timestamp = timestamp;
|
||||
this.updatedUrls = updatedUrls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GpodnetUploadChangesResponse-object from a JSON object that was
|
||||
* returned by an uploadChanges call.
|
||||
*
|
||||
* @throws org.json.JSONException If the method could not parse the JSONObject.
|
||||
*/
|
||||
public static GpodnetEpisodeActionPostResponse fromJSONObject(String objectString) throws JSONException {
|
||||
final JSONObject object = new JSONObject(objectString);
|
||||
final long timestamp = object.getLong("timestamp");
|
||||
Map<String, String> updatedUrls = new HashMap<String, String>();
|
||||
JSONArray urls = object.getJSONArray("update_urls");
|
||||
for (int i = 0; i < urls.length(); i++) {
|
||||
JSONArray urlPair = urls.getJSONArray(i);
|
||||
updatedUrls.put(urlPair.getString(0), urlPair.getString(1));
|
||||
}
|
||||
return new GpodnetEpisodeActionPostResponse(timestamp, updatedUrls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,14 +4,20 @@ import android.content.Context;
|
|||
import android.content.SharedPreferences;
|
||||
import android.util.Log;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import de.danoeh.antennapod.core.BuildConfig;
|
||||
import de.danoeh.antennapod.core.ClientConfig;
|
||||
import de.danoeh.antennapod.core.gpoddernet.GpodnetService;
|
||||
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction;
|
||||
import de.danoeh.antennapod.core.service.GpodnetSyncService;
|
||||
|
||||
/**
|
||||
|
@ -28,9 +34,11 @@ public class GpodnetPreferences {
|
|||
public static final String PREF_GPODNET_HOSTNAME = "prefGpodnetHostname";
|
||||
|
||||
|
||||
public static final String PREF_LAST_SYNC_TIMESTAMP = "de.danoeh.antennapod.preferences.gpoddernet.last_sync_timestamp";
|
||||
public static final String PREF_LAST_SUBSCRIPTION_SYNC_TIMESTAMP = "de.danoeh.antennapod.preferences.gpoddernet.last_sync_timestamp";
|
||||
public static final String PREF_LAST_EPISODE_ACTIONS_SYNC_TIMESTAMP = "de.danoeh.antennapod.preferences.gpoddernet.last_episode_actions_sync_timestamp";
|
||||
public static final String PREF_SYNC_ADDED = "de.danoeh.antennapod.preferences.gpoddernet.sync_added";
|
||||
public static final String PREF_SYNC_REMOVED = "de.danoeh.antennapod.preferences.gpoddernet.sync_removed";
|
||||
public static final String PREF_SYNC_EPISODE_ACTIONS = "de.danoeh.antennapod.preferences.gpoddernet.sync_queued_episode_actions";
|
||||
|
||||
private static String username;
|
||||
private static String password;
|
||||
|
@ -41,10 +49,14 @@ public class GpodnetPreferences {
|
|||
private static Set<String> addedFeeds;
|
||||
private static Set<String> removedFeeds;
|
||||
|
||||
private static List<GpodnetEpisodeAction> queuedEpisodeActions;
|
||||
|
||||
/**
|
||||
* Last value returned by getSubscriptionChanges call. Will be used for all subsequent calls of getSubscriptionChanges.
|
||||
*/
|
||||
private static long lastSyncTimestamp;
|
||||
private static long lastSubscriptionSyncTimestamp;
|
||||
|
||||
private static long lastEpisodeActionsSyncTimeStamp;
|
||||
|
||||
private static boolean preferencesLoaded = false;
|
||||
|
||||
|
@ -58,9 +70,11 @@ public class GpodnetPreferences {
|
|||
username = prefs.getString(PREF_GPODNET_USERNAME, null);
|
||||
password = prefs.getString(PREF_GPODNET_PASSWORD, null);
|
||||
deviceID = prefs.getString(PREF_GPODNET_DEVICEID, null);
|
||||
lastSyncTimestamp = prefs.getLong(PREF_LAST_SYNC_TIMESTAMP, 0);
|
||||
lastSubscriptionSyncTimestamp = prefs.getLong(PREF_LAST_SUBSCRIPTION_SYNC_TIMESTAMP, 0);
|
||||
lastEpisodeActionsSyncTimeStamp = prefs.getLong(PREF_LAST_EPISODE_ACTIONS_SYNC_TIMESTAMP, 0);
|
||||
addedFeeds = readListFromString(prefs.getString(PREF_SYNC_ADDED, ""));
|
||||
removedFeeds = readListFromString(prefs.getString(PREF_SYNC_REMOVED, ""));
|
||||
queuedEpisodeActions = readEpisodeActionsFromString(prefs.getString(PREF_SYNC_EPISODE_ACTIONS, ""));
|
||||
hostname = checkGpodnetHostname(prefs.getString(PREF_GPODNET_HOSTNAME, GpodnetService.DEFAULT_BASE_HOST));
|
||||
|
||||
preferencesLoaded = true;
|
||||
|
@ -115,14 +129,24 @@ public class GpodnetPreferences {
|
|||
writePreference(PREF_GPODNET_DEVICEID, deviceID);
|
||||
}
|
||||
|
||||
public static long getLastSyncTimestamp() {
|
||||
public static long getLastSubscriptionSyncTimestamp() {
|
||||
ensurePreferencesLoaded();
|
||||
return lastSyncTimestamp;
|
||||
return lastSubscriptionSyncTimestamp;
|
||||
}
|
||||
|
||||
public static void setLastSyncTimestamp(long lastSyncTimestamp) {
|
||||
GpodnetPreferences.lastSyncTimestamp = lastSyncTimestamp;
|
||||
writePreference(PREF_LAST_SYNC_TIMESTAMP, lastSyncTimestamp);
|
||||
public static void setLastSubscriptionSyncTimestamp(long timestamp) {
|
||||
GpodnetPreferences.lastSubscriptionSyncTimestamp = timestamp;
|
||||
writePreference(PREF_LAST_SUBSCRIPTION_SYNC_TIMESTAMP, timestamp);
|
||||
}
|
||||
|
||||
public static long getLastEpisodeActionsSyncTimestamp() {
|
||||
ensurePreferencesLoaded();
|
||||
return lastEpisodeActionsSyncTimeStamp;
|
||||
}
|
||||
|
||||
public static void setLastEpisodeActionsSyncTimestamp(long timestamp) {
|
||||
GpodnetPreferences.lastEpisodeActionsSyncTimeStamp = timestamp;
|
||||
writePreference(PREF_LAST_EPISODE_ACTIONS_SYNC_TIMESTAMP, timestamp);
|
||||
}
|
||||
|
||||
public static String getHostname() {
|
||||
|
@ -195,7 +219,23 @@ public class GpodnetPreferences {
|
|||
ensurePreferencesLoaded();
|
||||
removedFeeds.removeAll(removed);
|
||||
writePreference(PREF_SYNC_REMOVED, removedFeeds);
|
||||
}
|
||||
|
||||
public static synchronized void enqueueEpisodeAction(GpodnetEpisodeAction action) {
|
||||
ensurePreferencesLoaded();
|
||||
queuedEpisodeActions.add(action);
|
||||
writePreference(PREF_SYNC_EPISODE_ACTIONS, writeEpisodeActionsToString(queuedEpisodeActions));
|
||||
}
|
||||
|
||||
public static List<GpodnetEpisodeAction> getQueuedEpisodeActions() {
|
||||
ensurePreferencesLoaded();
|
||||
return Collections.unmodifiableList(queuedEpisodeActions);
|
||||
}
|
||||
|
||||
public static synchronized void removeQueuedEpisodeActions(Collection<GpodnetEpisodeAction> queued) {
|
||||
ensurePreferencesLoaded();
|
||||
queuedEpisodeActions.removeAll(queued);
|
||||
writePreference(PREF_SYNC_EPISODE_ACTIONS, writeEpisodeActionsToString(queuedEpisodeActions));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -215,7 +255,9 @@ public class GpodnetPreferences {
|
|||
writePreference(PREF_SYNC_ADDED, addedFeeds);
|
||||
removedFeeds.clear();
|
||||
writePreference(PREF_SYNC_REMOVED, removedFeeds);
|
||||
setLastSyncTimestamp(0);
|
||||
queuedEpisodeActions.clear();
|
||||
writePreference(PREF_SYNC_EPISODE_ACTIONS, writeEpisodeActionsToString(queuedEpisodeActions));
|
||||
setLastSubscriptionSyncTimestamp(0);
|
||||
}
|
||||
|
||||
private static Set<String> readListFromString(String s) {
|
||||
|
@ -235,6 +277,29 @@ public class GpodnetPreferences {
|
|||
return result.toString().trim();
|
||||
}
|
||||
|
||||
private static List<GpodnetEpisodeAction> readEpisodeActionsFromString(String s) {
|
||||
String[] lines = s.split("\n");
|
||||
List<GpodnetEpisodeAction> result = new ArrayList<GpodnetEpisodeAction>(lines.length);
|
||||
for(String line : lines) {
|
||||
if(StringUtils.isNotBlank(line)) {
|
||||
GpodnetEpisodeAction action = GpodnetEpisodeAction.readFromString(line);
|
||||
if(action != null) {
|
||||
result.add(GpodnetEpisodeAction.readFromString(line));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String writeEpisodeActionsToString(Collection<GpodnetEpisodeAction> c) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
for(GpodnetEpisodeAction item : c) {
|
||||
result.append(item.writeToString());
|
||||
result.append("\n");
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private static String checkGpodnetHostname(String value) {
|
||||
int startIndex = 0;
|
||||
if (value.startsWith("http://")) {
|
||||
|
|
|
@ -45,6 +45,7 @@ public class UserPreferences implements
|
|||
public static final String PREF_MOBILE_UPDATE = "prefMobileUpdate";
|
||||
public static final String PREF_DISPLAY_ONLY_EPISODES = "prefDisplayOnlyEpisodes";
|
||||
public static final String PREF_AUTO_DELETE = "prefAutoDelete";
|
||||
public static final String PREF_SMART_MARK_AS_PLAYED_SECS = "prefSmartMarkAsPlayedSecs";
|
||||
public static final String PREF_AUTO_FLATTR = "pref_auto_flattr";
|
||||
public static final String PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD = "prefAutoFlattrPlayedDurationThreshold";
|
||||
public static final String PREF_THEME = "prefTheme";
|
||||
|
@ -57,7 +58,8 @@ public class UserPreferences implements
|
|||
private static final String PREF_PLAYBACK_SPEED = "prefPlaybackSpeed";
|
||||
private static final String PREF_PLAYBACK_SPEED_ARRAY = "prefPlaybackSpeedArray";
|
||||
public static final String PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS = "prefPauseForFocusLoss";
|
||||
private static final String PREF_SEEK_DELTA_SECS = "prefSeekDeltaSecs";
|
||||
private static final String PREF_FAST_FORWARD_SECS = "prefFastForwardSecs";
|
||||
private static final String PREF_REWIND_SECS = "prefRewindSecs";
|
||||
private static final String PREF_EXPANDED_NOTIFICATION = "prefExpandNotify";
|
||||
private static final String PREF_PERSISTENT_NOTIFICATION = "prefPersistNotify";
|
||||
public static final String PREF_QUEUE_ADD_TO_FRONT = "prefQueueAddToFront";
|
||||
|
@ -79,6 +81,7 @@ public class UserPreferences implements
|
|||
private boolean allowMobileUpdate;
|
||||
private boolean displayOnlyEpisodes;
|
||||
private boolean autoDelete;
|
||||
private int smartMarkAsPlayedSecs;
|
||||
private boolean autoFlattr;
|
||||
private float autoFlattrPlayedDurationThreshold;
|
||||
private int theme;
|
||||
|
@ -91,7 +94,8 @@ public class UserPreferences implements
|
|||
private String playbackSpeed;
|
||||
private String[] playbackSpeedArray;
|
||||
private boolean pauseForFocusLoss;
|
||||
private int seekDeltaSecs;
|
||||
private int fastForwardSecs;
|
||||
private int rewindSecs;
|
||||
private boolean isFreshInstall;
|
||||
private int notifyPriority;
|
||||
private boolean persistNotify;
|
||||
|
@ -107,8 +111,7 @@ public class UserPreferences implements
|
|||
* @throws IllegalArgumentException if context is null
|
||||
*/
|
||||
public static void createInstance(Context context) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Creating new instance of UserPreferences");
|
||||
Log.d(TAG, "Creating new instance of UserPreferences");
|
||||
Validate.notNull(context);
|
||||
|
||||
instance = new UserPreferences(context);
|
||||
|
@ -137,6 +140,7 @@ public class UserPreferences implements
|
|||
allowMobileUpdate = sp.getBoolean(PREF_MOBILE_UPDATE, false);
|
||||
displayOnlyEpisodes = sp.getBoolean(PREF_DISPLAY_ONLY_EPISODES, false);
|
||||
autoDelete = sp.getBoolean(PREF_AUTO_DELETE, false);
|
||||
smartMarkAsPlayedSecs = Integer.valueOf(sp.getString(PREF_SMART_MARK_AS_PLAYED_SECS, "30"));
|
||||
autoFlattr = sp.getBoolean(PREF_AUTO_FLATTR, false);
|
||||
autoFlattrPlayedDurationThreshold = sp.getFloat(PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD,
|
||||
PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD_DEFAULT);
|
||||
|
@ -154,7 +158,8 @@ public class UserPreferences implements
|
|||
playbackSpeedArray = readPlaybackSpeedArray(sp.getString(
|
||||
PREF_PLAYBACK_SPEED_ARRAY, null));
|
||||
pauseForFocusLoss = sp.getBoolean(PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS, false);
|
||||
seekDeltaSecs = Integer.valueOf(sp.getString(PREF_SEEK_DELTA_SECS, "30"));
|
||||
fastForwardSecs = sp.getInt(PREF_FAST_FORWARD_SECS, 30);
|
||||
rewindSecs = sp.getInt(PREF_REWIND_SECS, 30);
|
||||
if (sp.getBoolean(PREF_EXPANDED_NOTIFICATION, false)) {
|
||||
notifyPriority = NotificationCompat.PRIORITY_MAX;
|
||||
}
|
||||
|
@ -267,6 +272,11 @@ public class UserPreferences implements
|
|||
return instance.autoDelete;
|
||||
}
|
||||
|
||||
public static int getSmartMarkAsPlayedSecs() {
|
||||
instanceAvailable();;
|
||||
return instance.smartMarkAsPlayedSecs;
|
||||
}
|
||||
|
||||
public static boolean isAutoFlattr() {
|
||||
instanceAvailable();
|
||||
return instance.autoFlattr;
|
||||
|
@ -335,9 +345,14 @@ public class UserPreferences implements
|
|||
return instance.playbackSpeedArray;
|
||||
}
|
||||
|
||||
public static int getSeekDeltaMs() {
|
||||
public static int getFastFowardSecs() {
|
||||
instanceAvailable();
|
||||
return 1000 * instance.seekDeltaSecs;
|
||||
return instance.fastForwardSecs;
|
||||
}
|
||||
|
||||
public static int getRewindSecs() {
|
||||
instanceAvailable();
|
||||
return instance.rewindSecs;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -372,8 +387,7 @@ public class UserPreferences implements
|
|||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sp, String key) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Registered change of user preferences. Key: " + key);
|
||||
Log.d(TAG, "Registered change of user preferences. Key: " + key);
|
||||
|
||||
if (key.equals(PREF_DOWNLOAD_MEDIA_ON_WIFI_ONLY)) {
|
||||
downloadMediaOnWifiOnly = sp.getBoolean(
|
||||
|
@ -389,10 +403,10 @@ public class UserPreferences implements
|
|||
updateInterval = readUpdateInterval(sp.getString(
|
||||
PREF_UPDATE_INTERVAL, "0"));
|
||||
ClientConfig.applicationCallbacks.setUpdateInterval(updateInterval);
|
||||
|
||||
} else if (key.equals(PREF_AUTO_DELETE)) {
|
||||
autoDelete = sp.getBoolean(PREF_AUTO_DELETE, false);
|
||||
|
||||
} else if (key.equals(PREF_SMART_MARK_AS_PLAYED_SECS)) {
|
||||
smartMarkAsPlayedSecs = Integer.valueOf(sp.getString(PREF_SMART_MARK_AS_PLAYED_SECS, "30"));
|
||||
} else if (key.equals(PREF_AUTO_FLATTR)) {
|
||||
autoFlattr = sp.getBoolean(PREF_AUTO_FLATTR, false);
|
||||
} else if (key.equals(PREF_DISPLAY_ONLY_EPISODES)) {
|
||||
|
@ -422,8 +436,10 @@ public class UserPreferences implements
|
|||
PREF_PLAYBACK_SPEED_ARRAY, null));
|
||||
} else if (key.equals(PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS)) {
|
||||
pauseForFocusLoss = sp.getBoolean(PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS, false);
|
||||
} else if (key.equals(PREF_SEEK_DELTA_SECS)) {
|
||||
seekDeltaSecs = Integer.valueOf(sp.getString(PREF_SEEK_DELTA_SECS, "30"));
|
||||
} else if (key.equals(PREF_FAST_FORWARD_SECS)) {
|
||||
fastForwardSecs = sp.getInt(PREF_FAST_FORWARD_SECS, 30);
|
||||
} else if (key.equals(PREF_REWIND_SECS)) {
|
||||
rewindSecs = sp.getInt(PREF_REWIND_SECS, 30);
|
||||
} else if (key.equals(PREF_PAUSE_ON_HEADSET_DISCONNECT)) {
|
||||
pauseOnHeadsetDisconnect = sp.getBoolean(PREF_PAUSE_ON_HEADSET_DISCONNECT, true);
|
||||
} else if (key.equals(PREF_UNPAUSE_ON_HEADSET_RECONNECT)) {
|
||||
|
@ -443,6 +459,20 @@ public class UserPreferences implements
|
|||
}
|
||||
}
|
||||
|
||||
public static void setPrefFastForwardSecs(int secs) {
|
||||
Log.d(TAG, "setPrefFastForwardSecs(" + secs +")");
|
||||
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(instance.context).edit();
|
||||
editor.putInt(PREF_FAST_FORWARD_SECS, secs);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
public static void setPrefRewindSecs(int secs) {
|
||||
Log.d(TAG, "setPrefRewindSecs(" + secs +")");
|
||||
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(instance.context).edit();
|
||||
editor.putInt(PREF_REWIND_SECS, secs);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
public static void setPlaybackSpeed(String speed) {
|
||||
PreferenceManager.getDefaultSharedPreferences(instance.context).edit()
|
||||
.putString(PREF_PLAYBACK_SPEED, speed).apply();
|
||||
|
@ -517,8 +547,7 @@ public class UserPreferences implements
|
|||
.getDefaultSharedPreferences(context.getApplicationContext());
|
||||
String strDir = prefs.getString(PREF_DATA_FOLDER, null);
|
||||
if (strDir == null) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Using default data folder");
|
||||
Log.d(TAG, "Using default data folder");
|
||||
return context.getExternalFilesDir(type);
|
||||
} else {
|
||||
File dataDir = new File(strDir);
|
||||
|
@ -549,8 +578,7 @@ public class UserPreferences implements
|
|||
if (!typeDir.exists()) {
|
||||
if (dataDir.canWrite()) {
|
||||
if (!typeDir.mkdir()) {
|
||||
Log.e(TAG, "Could not create data folder named "
|
||||
+ type);
|
||||
Log.e(TAG, "Could not create data folder named " + type);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -562,8 +590,7 @@ public class UserPreferences implements
|
|||
}
|
||||
|
||||
public static void setDataFolder(String dir) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Result from DirectoryChooser: " + dir);
|
||||
Log.d(TAG, "Result from DirectoryChooser: " + dir);
|
||||
instanceAvailable();
|
||||
SharedPreferences prefs = PreferenceManager
|
||||
.getDefaultSharedPreferences(instance.context);
|
||||
|
@ -600,16 +627,13 @@ public class UserPreferences implements
|
|||
IMPORT_DIR);
|
||||
if (importDir != null) {
|
||||
if (importDir.exists()) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Import directory already exists");
|
||||
Log.d(TAG, "Import directory already exists");
|
||||
} else {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Creating import directory");
|
||||
Log.d(TAG, "Creating import directory");
|
||||
importDir.mkdir();
|
||||
}
|
||||
} else {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Could not access external storage.");
|
||||
Log.d(TAG, "Could not access external storage.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -618,8 +642,7 @@ public class UserPreferences implements
|
|||
*/
|
||||
public static void restartUpdateAlarm(long triggerAtMillis, long intervalMillis) {
|
||||
instanceAvailable();
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Restarting update alarm.");
|
||||
Log.d(TAG, "Restarting update alarm.");
|
||||
AlarmManager alarmManager = (AlarmManager) instance.context
|
||||
.getSystemService(Context.ALARM_SERVICE);
|
||||
PendingIntent updateIntent = PendingIntent.getBroadcast(
|
||||
|
@ -628,11 +651,9 @@ public class UserPreferences implements
|
|||
if (intervalMillis != 0) {
|
||||
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, triggerAtMillis, intervalMillis,
|
||||
updateIntent);
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Changed alarm to new interval");
|
||||
Log.d(TAG, "Changed alarm to new interval");
|
||||
} else {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Automatic update was deactivated");
|
||||
Log.d(TAG, "Automatic update was deactivated");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,24 +9,31 @@ import android.content.Intent;
|
|||
import android.os.IBinder;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.Map;
|
||||
|
||||
import de.danoeh.antennapod.core.BuildConfig;
|
||||
import de.danoeh.antennapod.core.ClientConfig;
|
||||
import de.danoeh.antennapod.core.R;
|
||||
import de.danoeh.antennapod.core.feed.Feed;
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.gpoddernet.GpodnetService;
|
||||
import de.danoeh.antennapod.core.gpoddernet.GpodnetServiceAuthenticationException;
|
||||
import de.danoeh.antennapod.core.gpoddernet.GpodnetServiceException;
|
||||
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction;
|
||||
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeActionGetResponse;
|
||||
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeActionPostResponse;
|
||||
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetSubscriptionChange;
|
||||
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetUploadChangesResponse;
|
||||
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
|
||||
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.storage.DownloadRequestException;
|
||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||
import de.danoeh.antennapod.core.util.NetworkUtils;
|
||||
|
@ -50,7 +57,7 @@ public class GpodnetSyncService extends Service {
|
|||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
final String action = (intent != null) ? intent.getStringExtra(ARG_ACTION) : null;
|
||||
if (action != null && action.equals(ACTION_SYNC)) {
|
||||
Log.d(TAG, String.format("Waiting %d milliseconds before uploading changes", WAIT_INTERVAL));
|
||||
Log.d(TAG, String.format("Waiting %d milliseconds before uploading changes", WAIT_INTERVAL));
|
||||
syncWaiterThread.restart();
|
||||
} else {
|
||||
Log.e(TAG, "Received invalid intent: action argument is null or invalid");
|
||||
|
@ -61,9 +68,8 @@ public class GpodnetSyncService extends Service {
|
|||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, "onDestroy");
|
||||
Log.d(TAG, "onDestroy");
|
||||
syncWaiterThread.interrupt();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -79,64 +85,94 @@ public class GpodnetSyncService extends Service {
|
|||
return service;
|
||||
}
|
||||
|
||||
private synchronized void syncChanges() {
|
||||
if (GpodnetPreferences.loggedIn() && NetworkUtils.networkAvailable(this)) {
|
||||
final long timestamp = GpodnetPreferences.getLastSyncTimestamp();
|
||||
try {
|
||||
final List<String> localSubscriptions = DBReader.getFeedListDownloadUrls(this);
|
||||
GpodnetService service = tryLogin();
|
||||
|
||||
if (timestamp == 0) {
|
||||
// first sync: download all subscriptions...
|
||||
GpodnetSubscriptionChange changes =
|
||||
service.getSubscriptionChanges(GpodnetPreferences.getUsername(), GpodnetPreferences.getDeviceID(), 0);
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Downloaded subscription changes: " + changes);
|
||||
processSubscriptionChanges(localSubscriptions, changes);
|
||||
|
||||
// ... then upload all local subscriptions
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Uploading subscription list: " + localSubscriptions);
|
||||
GpodnetUploadChangesResponse uploadChangesResponse =
|
||||
service.uploadChanges(GpodnetPreferences.getUsername(), GpodnetPreferences.getDeviceID(), localSubscriptions, new LinkedList<String>());
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Uploading changes response: " + uploadChangesResponse);
|
||||
GpodnetPreferences.removeAddedFeeds(localSubscriptions);
|
||||
GpodnetPreferences.removeRemovedFeeds(GpodnetPreferences.getRemovedFeedsCopy());
|
||||
GpodnetPreferences.setLastSyncTimestamp(uploadChangesResponse.timestamp);
|
||||
} else {
|
||||
Set<String> added = GpodnetPreferences.getAddedFeedsCopy();
|
||||
Set<String> removed = GpodnetPreferences.getRemovedFeedsCopy();
|
||||
|
||||
// download remote changes first...
|
||||
GpodnetSubscriptionChange subscriptionChanges = service.getSubscriptionChanges(GpodnetPreferences.getUsername(), GpodnetPreferences.getDeviceID(), timestamp);
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Downloaded subscription changes: " + subscriptionChanges);
|
||||
processSubscriptionChanges(localSubscriptions, subscriptionChanges);
|
||||
|
||||
// ... then upload changes local changes
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, String.format("Uploading subscriptions, Added: %s\nRemoved: %s",
|
||||
added.toString(), removed));
|
||||
GpodnetUploadChangesResponse uploadChangesResponse = service.uploadChanges(GpodnetPreferences.getUsername(), GpodnetPreferences.getDeviceID(), added, removed);
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Upload subscriptions response: " + uploadChangesResponse);
|
||||
|
||||
GpodnetPreferences.removeAddedFeeds(added);
|
||||
GpodnetPreferences.removeRemovedFeeds(removed);
|
||||
GpodnetPreferences.setLastSyncTimestamp(uploadChangesResponse.timestamp);
|
||||
}
|
||||
clearErrorNotifications();
|
||||
} catch (GpodnetServiceException e) {
|
||||
e.printStackTrace();
|
||||
updateErrorNotification(e);
|
||||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
private synchronized void sync() {
|
||||
if (GpodnetPreferences.loggedIn() == false || NetworkUtils.networkAvailable(this) == false) {
|
||||
stopSelf();
|
||||
return;
|
||||
}
|
||||
syncSubscriptionChanges();
|
||||
syncEpisodeActions();
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
private synchronized void syncSubscriptionChanges() {
|
||||
final long timestamp = GpodnetPreferences.getLastSubscriptionSyncTimestamp();
|
||||
try {
|
||||
final List<String> localSubscriptions = DBReader.getFeedListDownloadUrls(this);
|
||||
GpodnetService service = tryLogin();
|
||||
|
||||
// first sync: download all subscriptions...
|
||||
GpodnetSubscriptionChange subscriptionChanges = service.getSubscriptionChanges(GpodnetPreferences.getUsername(),
|
||||
GpodnetPreferences.getDeviceID(), timestamp);
|
||||
long lastUpdate = subscriptionChanges.getTimestamp();
|
||||
|
||||
Log.d(TAG, "Downloaded subscription changes: " + subscriptionChanges);
|
||||
processSubscriptionChanges(localSubscriptions, subscriptionChanges);
|
||||
|
||||
Collection<String> added;
|
||||
Collection<String> removed;
|
||||
if (timestamp == 0) {
|
||||
added = localSubscriptions;
|
||||
GpodnetPreferences.removeRemovedFeeds(GpodnetPreferences.getRemovedFeedsCopy());
|
||||
removed = Collections.emptyList();
|
||||
} else {
|
||||
added = GpodnetPreferences.getAddedFeedsCopy();
|
||||
removed = GpodnetPreferences.getRemovedFeedsCopy();
|
||||
}
|
||||
if(added.size() > 0 || removed.size() > 0) {
|
||||
Log.d(TAG, String.format("Uploading subscriptions, Added: %s\nRemoved: %s",
|
||||
added, removed));
|
||||
GpodnetUploadChangesResponse uploadResponse = service.uploadChanges(GpodnetPreferences.getUsername(),
|
||||
GpodnetPreferences.getDeviceID(), added, removed);
|
||||
lastUpdate = uploadResponse.timestamp;
|
||||
Log.d(TAG, "Upload changes response: " + uploadResponse);
|
||||
GpodnetPreferences.removeAddedFeeds(added);
|
||||
GpodnetPreferences.removeRemovedFeeds(removed);
|
||||
}
|
||||
GpodnetPreferences.setLastSubscriptionSyncTimestamp(lastUpdate);
|
||||
clearErrorNotifications();
|
||||
} catch (GpodnetServiceException e) {
|
||||
e.printStackTrace();
|
||||
updateErrorNotification(e);
|
||||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void syncEpisodeActions() {
|
||||
final long timestamp = GpodnetPreferences.getLastEpisodeActionsSyncTimestamp();
|
||||
Log.d(TAG, "last episode actions sync timestamp: " + timestamp);
|
||||
try {
|
||||
GpodnetService service = tryLogin();
|
||||
|
||||
// download episode actions
|
||||
GpodnetEpisodeActionGetResponse getResponse = service.getEpisodeChanges(timestamp);
|
||||
long lastUpdate = getResponse.getTimestamp();
|
||||
Log.d(TAG, "Downloaded episode actions: " + getResponse);
|
||||
List<GpodnetEpisodeAction> remoteActions = getResponse.getEpisodeActions();
|
||||
|
||||
List<GpodnetEpisodeAction> localActions = GpodnetPreferences.getQueuedEpisodeActions();
|
||||
processEpisodeActions(localActions, remoteActions);
|
||||
|
||||
// upload local actions
|
||||
if(localActions.size() > 0) {
|
||||
Log.d(TAG, "Uploading episode actions: " + localActions);
|
||||
GpodnetEpisodeActionPostResponse postResponse = service.uploadEpisodeActions(localActions);
|
||||
lastUpdate = postResponse.timestamp;
|
||||
Log.d(TAG, "Upload episode response: " + postResponse);
|
||||
GpodnetPreferences.removeQueuedEpisodeActions(localActions);
|
||||
}
|
||||
GpodnetPreferences.setLastEpisodeActionsSyncTimestamp(lastUpdate);
|
||||
clearErrorNotifications();
|
||||
} catch (GpodnetServiceException e) {
|
||||
e.printStackTrace();
|
||||
updateErrorNotification(e);
|
||||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void processSubscriptionChanges(List<String> localSubscriptions, GpodnetSubscriptionChange changes) throws DownloadRequestException {
|
||||
for (String downloadUrl : changes.getAdded()) {
|
||||
if (!localSubscriptions.contains(downloadUrl)) {
|
||||
|
@ -149,6 +185,64 @@ public class GpodnetSyncService extends Service {
|
|||
}
|
||||
}
|
||||
|
||||
private synchronized void processEpisodeActions(List<GpodnetEpisodeAction> localActions, List<GpodnetEpisodeAction> remoteActions) throws DownloadRequestException {
|
||||
if(remoteActions.size() == 0) {
|
||||
return;
|
||||
}
|
||||
Map<Pair<String, String>, GpodnetEpisodeAction> localMostRecentPlayAction = new HashMap<Pair<String, String>, GpodnetEpisodeAction>();
|
||||
Map<Pair<String, String>, GpodnetEpisodeAction> remoteMostRecentPlayAction = new HashMap<Pair<String, String>, GpodnetEpisodeAction>();
|
||||
// make sure more recent local actions are not overwritten by older remote actions
|
||||
for(GpodnetEpisodeAction action : localActions) {
|
||||
Pair key = new Pair(action.getPodcast(), action.getEpisode());
|
||||
GpodnetEpisodeAction mostRecent = localMostRecentPlayAction.get(key);
|
||||
if (mostRecent == null) {
|
||||
localMostRecentPlayAction.put(key, action);
|
||||
} else if (mostRecent.getTimestamp().before(action.getTimestamp())) {
|
||||
localMostRecentPlayAction.put(key, action);
|
||||
}
|
||||
}
|
||||
for (GpodnetEpisodeAction action : remoteActions) {
|
||||
switch (action.getAction()) {
|
||||
case NEW:
|
||||
FeedItem newItem = DBReader.getFeedItem(this, action.getPodcast(), action.getEpisode());
|
||||
if(newItem != null) {
|
||||
DBWriter.markItemRead(this, newItem, false, true);
|
||||
} else {
|
||||
Log.i(TAG, "Unknown feed item: " + action);
|
||||
}
|
||||
break;
|
||||
case DOWNLOAD:
|
||||
break;
|
||||
case PLAY:
|
||||
Pair key = new Pair(action.getPodcast(), action.getEpisode());
|
||||
GpodnetEpisodeAction localMostRecent = localMostRecentPlayAction.get(key);
|
||||
if(localMostRecent == null ||
|
||||
localMostRecent.getTimestamp().before(action.getTimestamp())) {
|
||||
GpodnetEpisodeAction mostRecent = remoteMostRecentPlayAction.get(key);
|
||||
if (mostRecent == null) {
|
||||
remoteMostRecentPlayAction.put(key, action);
|
||||
} else if (mostRecent.getTimestamp().before(action.getTimestamp())) {
|
||||
remoteMostRecentPlayAction.put(key, action);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DELETE:
|
||||
// NEVER EVER call DBWriter.deleteFeedMediaOfItem() here, leads to an infinite loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (GpodnetEpisodeAction action : remoteMostRecentPlayAction.values()) {
|
||||
FeedItem playItem = DBReader.getFeedItem(this, action.getPodcast(), action.getEpisode());
|
||||
if (playItem != null) {
|
||||
playItem.getMedia().setPosition(action.getPosition() * 1000);
|
||||
if(playItem.getMedia().hasAlmostEnded()) {
|
||||
DBWriter.markItemRead(this, playItem, true, true);
|
||||
DBWriter.addItemToPlaybackHistory(this, playItem.getMedia());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void clearErrorNotifications() {
|
||||
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
||||
nm.cancel(R.id.notification_gpodnet_sync_error);
|
||||
|
@ -156,7 +250,7 @@ public class GpodnetSyncService extends Service {
|
|||
}
|
||||
|
||||
private void updateErrorNotification(GpodnetServiceException exception) {
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, "Posting error notification");
|
||||
Log.d(TAG, "Posting error notification");
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
|
||||
final String title;
|
||||
|
@ -186,7 +280,7 @@ public class GpodnetSyncService extends Service {
|
|||
private WaiterThread syncWaiterThread = new WaiterThread(WAIT_INTERVAL) {
|
||||
@Override
|
||||
public void onWaitCompleted() {
|
||||
syncChanges();
|
||||
sync();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -209,7 +303,7 @@ public class GpodnetSyncService extends Service {
|
|||
|
||||
private void reinit() {
|
||||
if (thread != null && thread.isAlive()) {
|
||||
Log.d(TAG, "Interrupting waiter thread");
|
||||
Log.d(TAG, "Interrupting waiter thread");
|
||||
thread.interrupt();
|
||||
}
|
||||
thread = new Thread() {
|
||||
|
|
|
@ -60,6 +60,9 @@ import de.danoeh.antennapod.core.feed.FeedImage;
|
|||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.feed.FeedPreferences;
|
||||
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction;
|
||||
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction.Action;
|
||||
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.storage.DBTasks;
|
||||
|
@ -800,6 +803,18 @@ public class DownloadService extends Service {
|
|||
|
||||
// queue new media files for automatic download
|
||||
for (FeedItem item : savedFeed.getItems()) {
|
||||
if(item.getPubDate() == null) {
|
||||
Log.d(TAG, item.toString());
|
||||
}
|
||||
if(item.getImage() != null && item.getImage().isDownloaded() == false) {
|
||||
item.getImage().setOwner(item);
|
||||
try {
|
||||
requester.downloadImage(DownloadService.this,
|
||||
item.getImage());
|
||||
} catch (DownloadRequestException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (!item.isRead() && item.hasMedia() && !item.getMedia().isDownloaded()) {
|
||||
newMediaFiles.add(item.getMedia().getId());
|
||||
}
|
||||
|
@ -1166,6 +1181,15 @@ public class DownloadService extends Service {
|
|||
saveDownloadStatus(status);
|
||||
sendDownloadHandledIntent();
|
||||
|
||||
if(GpodnetPreferences.loggedIn()) {
|
||||
FeedItem item = media.getItem();
|
||||
GpodnetEpisodeAction action = new GpodnetEpisodeAction.Builder(item, Action.DOWNLOAD)
|
||||
.currentDeviceId()
|
||||
.currentTimestamp()
|
||||
.build();
|
||||
GpodnetPreferences.enqueueEpisodeAction(action);
|
||||
}
|
||||
|
||||
numberOfDownloads.decrementAndGet();
|
||||
queryDownloadsAsync();
|
||||
}
|
||||
|
|
|
@ -36,13 +36,15 @@ import org.apache.commons.lang3.StringUtils;
|
|||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import de.danoeh.antennapod.core.BuildConfig;
|
||||
import de.danoeh.antennapod.core.ClientConfig;
|
||||
import de.danoeh.antennapod.core.R;
|
||||
import de.danoeh.antennapod.core.feed.Chapter;
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.feed.MediaType;
|
||||
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction;
|
||||
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction.Action;
|
||||
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
|
||||
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.receiver.MediaButtonReceiver;
|
||||
|
@ -167,6 +169,8 @@ public class PlaybackService extends Service {
|
|||
private PlaybackServiceMediaPlayer mediaPlayer;
|
||||
private PlaybackServiceTaskManager taskManager;
|
||||
|
||||
private int startPosition;
|
||||
|
||||
private static volatile MediaType currentMediaType = MediaType.UNKNOWN;
|
||||
|
||||
private final IBinder mBinder = new LocalBinder();
|
||||
|
@ -179,8 +183,7 @@ public class PlaybackService extends Service {
|
|||
|
||||
@Override
|
||||
public boolean onUnbind(Intent intent) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Received onUnbind event");
|
||||
Log.d(TAG, "Received onUnbind event");
|
||||
return super.onUnbind(intent);
|
||||
}
|
||||
|
||||
|
@ -214,8 +217,7 @@ public class PlaybackService extends Service {
|
|||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Service created.");
|
||||
Log.d(TAG, "Service created.");
|
||||
isRunning = true;
|
||||
|
||||
registerReceiver(headsetDisconnected, new IntentFilter(
|
||||
|
@ -242,8 +244,7 @@ public class PlaybackService extends Service {
|
|||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Service is about to be destroyed");
|
||||
Log.d(TAG, "Service is about to be destroyed");
|
||||
isRunning = false;
|
||||
started = false;
|
||||
currentMediaType = MediaType.UNKNOWN;
|
||||
|
@ -259,8 +260,7 @@ public class PlaybackService extends Service {
|
|||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Received onBind event");
|
||||
Log.d(TAG, "Received onBind event");
|
||||
return mBinder;
|
||||
}
|
||||
|
||||
|
@ -268,8 +268,7 @@ public class PlaybackService extends Service {
|
|||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
super.onStartCommand(intent, flags, startId);
|
||||
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "OnStartCommand called");
|
||||
Log.d(TAG, "OnStartCommand called");
|
||||
final int keycode = intent.getIntExtra(MediaButtonReceiver.EXTRA_KEYCODE, -1);
|
||||
final Playable playable = intent.getParcelableExtra(EXTRA_PLAYABLE);
|
||||
if (keycode == -1 && playable == null) {
|
||||
|
@ -278,14 +277,12 @@ public class PlaybackService extends Service {
|
|||
}
|
||||
|
||||
if ((flags & Service.START_FLAG_REDELIVERY) != 0) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "onStartCommand is a redelivered intent, calling stopForeground now.");
|
||||
Log.d(TAG, "onStartCommand is a redelivered intent, calling stopForeground now.");
|
||||
stopForeground(true);
|
||||
} else {
|
||||
|
||||
if (keycode != -1) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Received media button event");
|
||||
Log.d(TAG, "Received media button event");
|
||||
handleKeycode(keycode);
|
||||
} else {
|
||||
started = true;
|
||||
|
@ -305,8 +302,7 @@ public class PlaybackService extends Service {
|
|||
* Handles media button events
|
||||
*/
|
||||
private void handleKeycode(int keycode) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Handling keycode: " + keycode);
|
||||
Log.d(TAG, "Handling keycode: " + keycode);
|
||||
final PlaybackServiceMediaPlayer.PSMPInfo info = mediaPlayer.getPSMPInfo();
|
||||
final PlayerStatus status = info.playerStatus;
|
||||
switch (keycode) {
|
||||
|
@ -348,11 +344,11 @@ public class PlaybackService extends Service {
|
|||
break;
|
||||
case KeyEvent.KEYCODE_MEDIA_NEXT:
|
||||
case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
|
||||
mediaPlayer.seekDelta(UserPreferences.getSeekDeltaMs());
|
||||
mediaPlayer.seekDelta(UserPreferences.getFastFowardSecs() * 1000);
|
||||
break;
|
||||
case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
|
||||
case KeyEvent.KEYCODE_MEDIA_REWIND:
|
||||
mediaPlayer.seekDelta(-UserPreferences.getSeekDeltaMs());
|
||||
mediaPlayer.seekDelta(-UserPreferences.getRewindSecs() * 1000);
|
||||
break;
|
||||
case KeyEvent.KEYCODE_MEDIA_STOP:
|
||||
if (status == PlayerStatus.PLAYING) {
|
||||
|
@ -376,8 +372,7 @@ public class PlaybackService extends Service {
|
|||
* mediaplayer.
|
||||
*/
|
||||
public void setVideoSurface(SurfaceHolder sh) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Setting display");
|
||||
Log.d(TAG, "Setting display");
|
||||
mediaPlayer.setVideoSurface(sh);
|
||||
}
|
||||
|
||||
|
@ -445,6 +440,21 @@ public class PlaybackService extends Service {
|
|||
}
|
||||
writePlayerStatusPlaybackPreferences();
|
||||
|
||||
final Playable playable = mediaPlayer.getPSMPInfo().playable;
|
||||
|
||||
// Gpodder: send play action
|
||||
if(GpodnetPreferences.loggedIn() && playable instanceof FeedMedia) {
|
||||
FeedMedia media = (FeedMedia) playable;
|
||||
FeedItem item = media.getItem();
|
||||
GpodnetEpisodeAction action = new GpodnetEpisodeAction.Builder(item, Action.PLAY)
|
||||
.currentDeviceId()
|
||||
.currentTimestamp()
|
||||
.started(startPosition / 1000)
|
||||
.position(getCurrentPosition() / 1000)
|
||||
.total(getDuration() / 1000)
|
||||
.build();
|
||||
GpodnetPreferences.enqueueEpisodeAction(action);
|
||||
}
|
||||
break;
|
||||
|
||||
case STOPPED:
|
||||
|
@ -453,16 +463,15 @@ public class PlaybackService extends Service {
|
|||
break;
|
||||
|
||||
case PLAYING:
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Audiofocus successfully requested");
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Resuming/Starting playback");
|
||||
Log.d(TAG, "Audiofocus successfully requested");
|
||||
Log.d(TAG, "Resuming/Starting playback");
|
||||
|
||||
taskManager.startPositionSaver();
|
||||
taskManager.startWidgetUpdater();
|
||||
writePlayerStatusPlaybackPreferences();
|
||||
setupNotification(newInfo);
|
||||
started = true;
|
||||
startPosition = mediaPlayer.getPosition();
|
||||
break;
|
||||
|
||||
case ERROR:
|
||||
|
@ -472,9 +481,8 @@ public class PlaybackService extends Service {
|
|||
}
|
||||
|
||||
Intent statusUpdate = new Intent(ACTION_PLAYER_STATUS_CHANGED);
|
||||
statusUpdate.putExtra(EXTRA_NEW_PLAYER_STATUS, newInfo.playerStatus.ordinal());
|
||||
// statusUpdate.putExtra(EXTRA_NEW_PLAYER_STATUS, newInfo.playerStatus.ordinal());
|
||||
sendBroadcast(statusUpdate);
|
||||
sendBroadcast(new Intent(ACTION_PLAYER_STATUS_CHANGED));
|
||||
updateWidget();
|
||||
refreshRemoteControlClientState(newInfo);
|
||||
bluetoothNotifyChange(newInfo, AVRCP_ACTION_PLAYER_STATUS_CHANGED);
|
||||
|
@ -537,11 +545,10 @@ public class PlaybackService extends Service {
|
|||
};
|
||||
|
||||
private void endPlayback(boolean playNextEpisode) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Playback ended");
|
||||
Log.d(TAG, "Playback ended");
|
||||
|
||||
final Playable media = mediaPlayer.getPSMPInfo().playable;
|
||||
if (media == null) {
|
||||
final Playable playable = mediaPlayer.getPSMPInfo().playable;
|
||||
if (playable == null) {
|
||||
Log.e(TAG, "Cannot end playback: media was null");
|
||||
return;
|
||||
}
|
||||
|
@ -551,36 +558,46 @@ public class PlaybackService extends Service {
|
|||
boolean isInQueue = false;
|
||||
FeedItem nextItem = null;
|
||||
|
||||
if (media instanceof FeedMedia) {
|
||||
FeedItem item = ((FeedMedia) media).getItem();
|
||||
if (playable instanceof FeedMedia) {
|
||||
FeedMedia media = (FeedMedia) playable;
|
||||
FeedItem item = media.getItem();
|
||||
DBWriter.markItemRead(PlaybackService.this, item, true, true);
|
||||
|
||||
try {
|
||||
final List<FeedItem> queue = taskManager.getQueue();
|
||||
isInQueue = QueueAccess.ItemListAccess(queue).contains(((FeedMedia) media).getItem().getId());
|
||||
isInQueue = QueueAccess.ItemListAccess(queue).contains(item.getId());
|
||||
nextItem = DBTasks.getQueueSuccessorOfItem(this, item.getId(), queue);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
// isInQueue remains false
|
||||
}
|
||||
if (isInQueue) {
|
||||
DBWriter.removeQueueItem(PlaybackService.this, item.getId(), true);
|
||||
DBWriter.removeQueueItem(PlaybackService.this, item, true);
|
||||
}
|
||||
DBWriter.addItemToPlaybackHistory(PlaybackService.this, (FeedMedia) media);
|
||||
DBWriter.addItemToPlaybackHistory(PlaybackService.this, media);
|
||||
|
||||
// auto-flattr if enabled
|
||||
if (isAutoFlattrable(media) && UserPreferences.getAutoFlattrPlayedDurationThreshold() == 1.0f) {
|
||||
DBTasks.flattrItemIfLoggedIn(PlaybackService.this, item);
|
||||
}
|
||||
|
||||
//Delete episode if enabled
|
||||
// Delete episode if enabled
|
||||
if(UserPreferences.isAutoDelete()) {
|
||||
DBWriter.deleteFeedMediaOfItem(PlaybackService.this, item.getMedia().getId());
|
||||
|
||||
if(BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Episode Deleted");
|
||||
DBWriter.deleteFeedMediaOfItem(PlaybackService.this, media.getId());
|
||||
Log.d(TAG, "Episode Deleted");
|
||||
}
|
||||
|
||||
// gpodder play action
|
||||
if(GpodnetPreferences.loggedIn()) {
|
||||
GpodnetEpisodeAction action = new GpodnetEpisodeAction.Builder(item, Action.PLAY)
|
||||
.currentDeviceId()
|
||||
.currentTimestamp()
|
||||
.started(startPosition / 1000)
|
||||
.position(getDuration() / 1000)
|
||||
.total(getDuration() / 1000)
|
||||
.build();
|
||||
GpodnetPreferences.enqueueEpisodeAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
// Load next episode if previous episode was in the queue and if there
|
||||
|
@ -596,8 +613,7 @@ public class PlaybackService extends Service {
|
|||
UserPreferences.isFollowQueue();
|
||||
|
||||
if (loadNextItem) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Loading next item in queue");
|
||||
Log.d(TAG, "Loading next item in queue");
|
||||
nextMedia = nextItem.getMedia();
|
||||
}
|
||||
final boolean prepareImmediately;
|
||||
|
@ -605,13 +621,10 @@ public class PlaybackService extends Service {
|
|||
final boolean stream;
|
||||
|
||||
if (playNextEpisode) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Playback of next episode will start immediately.");
|
||||
Log.d(TAG, "Playback of next episode will start immediately.");
|
||||
prepareImmediately = startWhenPrepared = true;
|
||||
} else {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "No more episodes available to play");
|
||||
|
||||
Log.d(TAG, "No more episodes available to play");
|
||||
prepareImmediately = startWhenPrepared = false;
|
||||
stopForeground(true);
|
||||
stopWidgetUpdater();
|
||||
|
@ -619,7 +632,7 @@ public class PlaybackService extends Service {
|
|||
|
||||
writePlaybackPreferencesNoMediaPlaying();
|
||||
if (nextMedia != null) {
|
||||
stream = !media.localFileAvailable();
|
||||
stream = !playable.localFileAvailable();
|
||||
mediaPlayer.playMediaObject(nextMedia, stream, startWhenPrepared, prepareImmediately);
|
||||
sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD,
|
||||
(nextMedia.getMediaType() == MediaType.VIDEO) ? EXTRA_CODE_VIDEO : EXTRA_CODE_AUDIO);
|
||||
|
@ -631,8 +644,7 @@ public class PlaybackService extends Service {
|
|||
}
|
||||
|
||||
public void setSleepTimer(long waitingTime) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Setting sleep timer to " + Long.toString(waitingTime)
|
||||
Log.d(TAG, "Setting sleep timer to " + Long.toString(waitingTime)
|
||||
+ " milliseconds");
|
||||
taskManager.setSleepTimer(waitingTime);
|
||||
sendNotificationBroadcast(NOTIFICATION_TYPE_SLEEPTIMER_UPDATE, 0);
|
||||
|
@ -675,8 +687,7 @@ public class PlaybackService extends Service {
|
|||
}
|
||||
|
||||
private void writePlaybackPreferences() {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Writing playback preferences");
|
||||
Log.d(TAG, "Writing playback preferences");
|
||||
|
||||
SharedPreferences.Editor editor = PreferenceManager
|
||||
.getDefaultSharedPreferences(getApplicationContext()).edit();
|
||||
|
@ -727,8 +738,7 @@ public class PlaybackService extends Service {
|
|||
}
|
||||
|
||||
private void writePlayerStatusPlaybackPreferences() {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Writing player status playback preferences");
|
||||
Log.d(TAG, "Writing player status playback preferences");
|
||||
|
||||
SharedPreferences.Editor editor = PreferenceManager
|
||||
.getDefaultSharedPreferences(getApplicationContext()).edit();
|
||||
|
@ -777,8 +787,7 @@ public class PlaybackService extends Service {
|
|||
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Starting background work");
|
||||
Log.d(TAG, "Starting background work");
|
||||
if (android.os.Build.VERSION.SDK_INT >= 11) {
|
||||
if (info.playable != null) {
|
||||
try {
|
||||
|
@ -888,8 +897,7 @@ public class PlaybackService extends Service {
|
|||
notification = notificationBuilder.build();
|
||||
}
|
||||
startForeground(NOTIFICATION_ID, notification);
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Notification set up");
|
||||
Log.d(TAG, "Notification set up");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -915,18 +923,15 @@ public class PlaybackService extends Service {
|
|||
float playbackSpeed = getCurrentPlaybackSpeed();
|
||||
final Playable playable = mediaPlayer.getPSMPInfo().playable;
|
||||
if (position != INVALID_TIME && duration != INVALID_TIME && playable != null) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Saving current position to " + position);
|
||||
Log.d(TAG, "Saving current position to " + position);
|
||||
if (updatePlayedDuration && playable instanceof FeedMedia) {
|
||||
FeedMedia m = (FeedMedia) playable;
|
||||
FeedItem item = m.getItem();
|
||||
m.setPlayedDuration(m.getPlayedDuration() + ((int) (deltaPlayedDuration * playbackSpeed)));
|
||||
FeedMedia media = (FeedMedia) playable;
|
||||
FeedItem item = media.getItem();
|
||||
media.setPlayedDuration(media.getPlayedDuration() + ((int) (deltaPlayedDuration * playbackSpeed)));
|
||||
// Auto flattr
|
||||
if (isAutoFlattrable(m) &&
|
||||
(m.getPlayedDuration() > UserPreferences.getAutoFlattrPlayedDurationThreshold() * duration)) {
|
||||
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "saveCurrentPosition: performing auto flattr since played duration " + Integer.toString(m.getPlayedDuration())
|
||||
if (isAutoFlattrable(media) &&
|
||||
(media.getPlayedDuration() > UserPreferences.getAutoFlattrPlayedDurationThreshold() * duration)) {
|
||||
Log.d(TAG, "saveCurrentPosition: performing auto flattr since played duration " + Integer.toString(media.getPlayedDuration())
|
||||
+ " is " + UserPreferences.getAutoFlattrPlayedDurationThreshold() * 100 + "% of file duration " + Integer.toString(duration));
|
||||
DBTasks.flattrItemIfLoggedIn(this, item);
|
||||
}
|
||||
|
@ -1019,8 +1024,7 @@ public class PlaybackService extends Service {
|
|||
|
||||
editor.apply();
|
||||
}
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "RemoteControlClient state was refreshed");
|
||||
Log.d(TAG, "RemoteControlClient state was refreshed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1063,15 +1067,12 @@ public class PlaybackService extends Service {
|
|||
if (StringUtils.equals(intent.getAction(), Intent.ACTION_HEADSET_PLUG)) {
|
||||
int state = intent.getIntExtra("state", -1);
|
||||
if (state != -1) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Headset plug event. State is " + state);
|
||||
Log.d(TAG, "Headset plug event. State is " + state);
|
||||
if (state == UNPLUGGED) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Headset was unplugged during playback.");
|
||||
Log.d(TAG, "Headset was unplugged during playback.");
|
||||
pauseIfPauseOnDisconnect();
|
||||
} else if (state == PLUGGED) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Headset was plugged in during playback.");
|
||||
Log.d(TAG, "Headset was plugged in during playback.");
|
||||
unpauseIfPauseOnDisconnect();
|
||||
}
|
||||
} else {
|
||||
|
@ -1088,8 +1089,7 @@ public class PlaybackService extends Service {
|
|||
int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1);
|
||||
int prevState = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE, -1);
|
||||
if (state == AudioManager.SCO_AUDIO_STATE_CONNECTED) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Received bluetooth connection intent");
|
||||
Log.d(TAG, "Received bluetooth connection intent");
|
||||
unpauseIfPauseOnDisconnect();
|
||||
}
|
||||
}
|
||||
|
@ -1101,8 +1101,7 @@ public class PlaybackService extends Service {
|
|||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
// sound is about to change, eg. bluetooth -> speaker
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Pausing playback because audio is becoming noisy");
|
||||
Log.d(TAG, "Pausing playback because audio is becoming noisy");
|
||||
pauseIfPauseOnDisconnect();
|
||||
}
|
||||
// android.media.AUDIO_BECOMING_NOISY
|
||||
|
@ -1148,8 +1147,7 @@ public class PlaybackService extends Service {
|
|||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (StringUtils.equals(intent.getAction(), ACTION_SKIP_CURRENT_EPISODE)) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Received SKIP_CURRENT_EPISODE intent");
|
||||
Log.d(TAG, "Received SKIP_CURRENT_EPISODE intent");
|
||||
mediaPlayer.endPlayback();
|
||||
}
|
||||
}
|
||||
|
@ -1159,8 +1157,7 @@ public class PlaybackService extends Service {
|
|||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (StringUtils.equals(intent.getAction(), ACTION_RESUME_PLAY_CURRENT_EPISODE)) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Received RESUME_PLAY_CURRENT_EPISODE intent");
|
||||
Log.d(TAG, "Received RESUME_PLAY_CURRENT_EPISODE intent");
|
||||
mediaPlayer.resume();
|
||||
}
|
||||
}
|
||||
|
@ -1170,8 +1167,7 @@ public class PlaybackService extends Service {
|
|||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (StringUtils.equals(intent.getAction(), ACTION_PAUSE_PLAY_CURRENT_EPISODE)) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Received PAUSE_PLAY_CURRENT_EPISODE intent");
|
||||
Log.d(TAG, "Received PAUSE_PLAY_CURRENT_EPISODE intent");
|
||||
mediaPlayer.pause(false, false);
|
||||
}
|
||||
}
|
||||
|
@ -1231,7 +1227,26 @@ public class PlaybackService extends Service {
|
|||
|
||||
|
||||
public void seekTo(final int t) {
|
||||
if(mediaPlayer.getPlayerStatus() == PlayerStatus.PLAYING
|
||||
&& GpodnetPreferences.loggedIn()) {
|
||||
final Playable playable = mediaPlayer.getPSMPInfo().playable;
|
||||
if (playable instanceof FeedMedia) {
|
||||
FeedMedia media = (FeedMedia) playable;
|
||||
FeedItem item = media.getItem();
|
||||
GpodnetEpisodeAction action = new GpodnetEpisodeAction.Builder(item, Action.PLAY)
|
||||
.currentDeviceId()
|
||||
.currentTimestamp()
|
||||
.started(startPosition / 1000)
|
||||
.position(getCurrentPosition() / 1000)
|
||||
.total(getDuration() / 1000)
|
||||
.build();
|
||||
GpodnetPreferences.enqueueEpisodeAction(action);
|
||||
}
|
||||
}
|
||||
mediaPlayer.seekTo(t);
|
||||
if(mediaPlayer.getPlayerStatus() == PlayerStatus.PLAYING ) {
|
||||
startPosition = t;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1270,10 +1285,9 @@ public class PlaybackService extends Service {
|
|||
return mediaPlayer.getVideoSize();
|
||||
}
|
||||
|
||||
private boolean isAutoFlattrable(Playable p) {
|
||||
if (p != null && p instanceof FeedMedia) {
|
||||
FeedMedia media = (FeedMedia) p;
|
||||
FeedItem item = ((FeedMedia) p).getItem();
|
||||
private boolean isAutoFlattrable(FeedMedia media) {
|
||||
if (media != null) {
|
||||
FeedItem item = media.getItem();
|
||||
return item != null && FlattrUtils.hasToken() && UserPreferences.isAutoFlattr() && item.getPaymentLink() != null && item.getFlattrStatus().getUnflattred();
|
||||
} else {
|
||||
return false;
|
||||
|
|
|
@ -24,11 +24,13 @@ import java.util.concurrent.TimeUnit;
|
|||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import de.danoeh.antennapod.core.BuildConfig;
|
||||
import de.danoeh.antennapod.core.feed.Chapter;
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.feed.MediaType;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.receiver.MediaButtonReceiver;
|
||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||
import de.danoeh.antennapod.core.util.playback.AudioPlayer;
|
||||
import de.danoeh.antennapod.core.util.playback.IPlayer;
|
||||
import de.danoeh.antennapod.core.util.playback.Playable;
|
||||
|
@ -91,7 +93,7 @@ public class PlaybackServiceMediaPlayer {
|
|||
new RejectedExecutionHandler() {
|
||||
@Override
|
||||
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, "Rejected execution of runnable");
|
||||
Log.d(TAG, "Rejected execution of runnable");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -137,7 +139,7 @@ public class PlaybackServiceMediaPlayer {
|
|||
public void playMediaObject(final Playable playable, final boolean stream, final boolean startWhenPrepared, final boolean prepareImmediately) {
|
||||
Validate.notNull(playable);
|
||||
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, "Play media object.");
|
||||
Log.d(TAG, "playMediaObject(...)");
|
||||
executor.submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
@ -164,16 +166,16 @@ public class PlaybackServiceMediaPlayer {
|
|||
*/
|
||||
private void playMediaObject(final Playable playable, final boolean forceReset, final boolean stream, final boolean startWhenPrepared, final boolean prepareImmediately) {
|
||||
Validate.notNull(playable);
|
||||
if (!playerLock.isHeldByCurrentThread())
|
||||
if (!playerLock.isHeldByCurrentThread()) {
|
||||
throw new IllegalStateException("method requires playerLock");
|
||||
}
|
||||
|
||||
|
||||
if (media != null) {
|
||||
if (!forceReset && media.getIdentifier().equals(playable.getIdentifier())
|
||||
&& playerStatus == PlayerStatus.PLAYING) {
|
||||
// episode is already playing -> ignore method call
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Method call to playMediaObject was ignored: media file already playing.");
|
||||
Log.d(TAG, "Method call to playMediaObject was ignored: media file already playing.");
|
||||
return;
|
||||
} else {
|
||||
// stop playback of this episode
|
||||
|
@ -184,6 +186,23 @@ public class PlaybackServiceMediaPlayer {
|
|||
if (playerStatus == PlayerStatus.PLAYING) {
|
||||
setPlayerStatus(PlayerStatus.PAUSED, media);
|
||||
}
|
||||
|
||||
// smart mark as played
|
||||
if(media != null && media instanceof FeedMedia) {
|
||||
FeedMedia oldMedia = (FeedMedia) media;
|
||||
if(oldMedia.hasAlmostEnded()) {
|
||||
Log.d(TAG, "smart mark as read");
|
||||
FeedItem item = oldMedia.getItem();
|
||||
DBWriter.markItemRead(context, item, true, false);
|
||||
DBWriter.removeQueueItem(context, item, false);
|
||||
DBWriter.addItemToPlaybackHistory(context, oldMedia);
|
||||
if (UserPreferences.isAutoDelete()) {
|
||||
Log.d(TAG, "Delete " + oldMedia.toString());
|
||||
DBWriter.deleteFeedMediaOfItem(context, oldMedia.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setPlayerStatus(PlayerStatus.INDETERMINATE, null);
|
||||
}
|
||||
}
|
||||
|
@ -281,11 +300,10 @@ public class PlaybackServiceMediaPlayer {
|
|||
media.onPlaybackStart();
|
||||
|
||||
} else {
|
||||
if (BuildConfig.DEBUG) Log.e(TAG, "Failed to request audio focus");
|
||||
Log.e(TAG, "Failed to request audio focus");
|
||||
}
|
||||
} else {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Call to resume() was ignored because current state of PSMP object is " + playerStatus);
|
||||
Log.d(TAG, "Call to resume() was ignored because current state of PSMP object is " + playerStatus);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -307,8 +325,7 @@ public class PlaybackServiceMediaPlayer {
|
|||
playerLock.lock();
|
||||
releaseWifiLockIfNecessary();
|
||||
if (playerStatus == PlayerStatus.PLAYING) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Pausing playback.");
|
||||
Log.d(TAG, "Pausing playback.");
|
||||
mediaPlayer.pause();
|
||||
setPlayerStatus(PlayerStatus.PAUSED, media);
|
||||
|
||||
|
@ -320,8 +337,7 @@ public class PlaybackServiceMediaPlayer {
|
|||
reinit();
|
||||
}
|
||||
} else {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Ignoring call to pause: Player is in " + playerStatus + " state");
|
||||
Log.d(TAG, "Ignoring call to pause: Player is in " + playerStatus + " state");
|
||||
}
|
||||
|
||||
playerLock.unlock();
|
||||
|
@ -342,8 +358,7 @@ public class PlaybackServiceMediaPlayer {
|
|||
playerLock.lock();
|
||||
|
||||
if (playerStatus == PlayerStatus.INITIALIZED) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Preparing media player");
|
||||
Log.d(TAG, "Preparing media player");
|
||||
setPlayerStatus(PlayerStatus.PREPARING, media);
|
||||
try {
|
||||
mediaPlayer.prepare();
|
||||
|
@ -370,8 +385,7 @@ public class PlaybackServiceMediaPlayer {
|
|||
throw new IllegalStateException("Player is not in PREPARING state");
|
||||
}
|
||||
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Resource prepared");
|
||||
Log.d(TAG, "Resource prepared");
|
||||
|
||||
if (mediaType == MediaType.VIDEO) {
|
||||
VideoPlayer vp = (VideoPlayer) mediaPlayer;
|
||||
|
@ -383,8 +397,7 @@ public class PlaybackServiceMediaPlayer {
|
|||
}
|
||||
|
||||
if (media.getDuration() == 0) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Setting duration of media");
|
||||
Log.d(TAG, "Setting duration of media");
|
||||
media.setDuration(mediaPlayer.getDuration());
|
||||
}
|
||||
setPlayerStatus(PlayerStatus.PREPARED, media);
|
||||
|
@ -412,8 +425,7 @@ public class PlaybackServiceMediaPlayer {
|
|||
} else if (mediaPlayer != null) {
|
||||
mediaPlayer.reset();
|
||||
} else {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Call to reinit was ignored: media and mediaPlayer were null");
|
||||
Log.d(TAG, "Call to reinit was ignored: media and mediaPlayer were null");
|
||||
}
|
||||
playerLock.unlock();
|
||||
}
|
||||
|
@ -437,15 +449,15 @@ public class PlaybackServiceMediaPlayer {
|
|||
if (playerStatus == PlayerStatus.PLAYING
|
||||
|| playerStatus == PlayerStatus.PAUSED
|
||||
|| playerStatus == PlayerStatus.PREPARED) {
|
||||
if (stream) {
|
||||
// statusBeforeSeeking = playerStatus;
|
||||
// setPlayerStatus(PlayerStatus.SEEKING, media);
|
||||
if (!stream) {
|
||||
statusBeforeSeeking = playerStatus;
|
||||
setPlayerStatus(PlayerStatus.SEEKING, media);
|
||||
}
|
||||
mediaPlayer.seekTo(t);
|
||||
|
||||
} else if (playerStatus == PlayerStatus.INITIALIZED) {
|
||||
media.setPosition(t);
|
||||
startWhenPrepared.set(true);
|
||||
startWhenPrepared.set(false);
|
||||
prepare();
|
||||
}
|
||||
playerLock.unlock();
|
||||
|
@ -522,20 +534,20 @@ public class PlaybackServiceMediaPlayer {
|
|||
* Returns the position of the current media object or INVALID_TIME if the position could not be retrieved.
|
||||
*/
|
||||
public int getPosition() {
|
||||
if (!playerLock.tryLock()) {
|
||||
return INVALID_TIME;
|
||||
}
|
||||
playerLock.lock();
|
||||
|
||||
int retVal = INVALID_TIME;
|
||||
if (playerStatus == PlayerStatus.PLAYING
|
||||
|| playerStatus == PlayerStatus.PAUSED
|
||||
|| playerStatus == PlayerStatus.PREPARED) {
|
||||
|| playerStatus == PlayerStatus.PREPARED
|
||||
|| playerStatus == PlayerStatus.SEEKING) {
|
||||
retVal = mediaPlayer.getCurrentPosition();
|
||||
} else if (media != null && media.getPosition() > 0) {
|
||||
retVal = media.getPosition();
|
||||
}
|
||||
|
||||
playerLock.unlock();
|
||||
Log.d(TAG, "getPosition() -> " + retVal);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
@ -567,8 +579,7 @@ public class PlaybackServiceMediaPlayer {
|
|||
if (media != null && media.getMediaType() == MediaType.AUDIO) {
|
||||
if (mediaPlayer.canSetSpeed()) {
|
||||
mediaPlayer.setPlaybackSpeed((float) speed);
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Playback speed was set to " + speed);
|
||||
Log.d(TAG, "Playback speed was set to " + speed);
|
||||
callback.playbackSpeedChanged(speed);
|
||||
}
|
||||
}
|
||||
|
@ -651,8 +662,7 @@ public class PlaybackServiceMediaPlayer {
|
|||
@Override
|
||||
public void run() {
|
||||
playerLock.lock();
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Resetting video surface");
|
||||
Log.d(TAG, "Resetting video surface");
|
||||
mediaPlayer.setDisplay(null);
|
||||
reinit();
|
||||
playerLock.unlock();
|
||||
|
@ -716,7 +726,7 @@ public class PlaybackServiceMediaPlayer {
|
|||
private synchronized void setPlayerStatus(PlayerStatus newStatus, Playable newMedia) {
|
||||
Validate.notNull(newStatus);
|
||||
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, "Setting player status to " + newStatus);
|
||||
Log.d(TAG, "Setting player status to " + newStatus);
|
||||
|
||||
this.playerStatus = newStatus;
|
||||
this.media = newMedia;
|
||||
|
@ -725,6 +735,7 @@ public class PlaybackServiceMediaPlayer {
|
|||
|
||||
int state;
|
||||
if (playerStatus != null) {
|
||||
Log.d(TAG, "playerStatus: " + playerStatus.toString());
|
||||
switch (playerStatus) {
|
||||
case PLAYING:
|
||||
state = PlaybackStateCompat.STATE_PLAYING;
|
||||
|
@ -788,17 +799,15 @@ public class PlaybackServiceMediaPlayer {
|
|||
// If there is an incoming call, playback should be paused permanently
|
||||
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
final int callState = (tm != null) ? tm.getCallState() : 0;
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, "Call state: " + callState);
|
||||
Log.d(TAG, "Call state: " + callState);
|
||||
Log.i(TAG, "Call state:" + callState);
|
||||
|
||||
if (focusChange == AudioManager.AUDIOFOCUS_LOSS || callState != TelephonyManager.CALL_STATE_IDLE) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Lost audio focus");
|
||||
Log.d(TAG, "Lost audio focus");
|
||||
pause(true, false);
|
||||
callback.shouldStop();
|
||||
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Gained audio focus");
|
||||
Log.d(TAG, "Gained audio focus");
|
||||
if (pausedBecauseOfTransientAudiofocusLoss) { // we paused => play now
|
||||
resume();
|
||||
} else { // we ducked => raise audio level back
|
||||
|
@ -808,22 +817,19 @@ public class PlaybackServiceMediaPlayer {
|
|||
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
|
||||
if (playerStatus == PlayerStatus.PLAYING) {
|
||||
if (!UserPreferences.shouldPauseForFocusLoss()) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Lost audio focus temporarily. Ducking...");
|
||||
Log.d(TAG, "Lost audio focus temporarily. Ducking...");
|
||||
audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,
|
||||
AudioManager.ADJUST_LOWER, 0);
|
||||
pausedBecauseOfTransientAudiofocusLoss = false;
|
||||
} else {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Lost audio focus temporarily. Could duck, but won't, pausing...");
|
||||
Log.d(TAG, "Lost audio focus temporarily. Could duck, but won't, pausing...");
|
||||
pause(false, false);
|
||||
pausedBecauseOfTransientAudiofocusLoss = true;
|
||||
}
|
||||
}
|
||||
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
|
||||
if (playerStatus == PlayerStatus.PLAYING) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Lost audio focus temporarily. Pausing...");
|
||||
Log.d(TAG, "Lost audio focus temporarily. Pausing...");
|
||||
pause(false, false);
|
||||
pausedBecauseOfTransientAudiofocusLoss = true;
|
||||
}
|
||||
|
@ -873,8 +879,7 @@ public class PlaybackServiceMediaPlayer {
|
|||
if (playerStatus == PlayerStatus.INDETERMINATE) {
|
||||
setPlayerStatus(PlayerStatus.STOPPED, null);
|
||||
} else {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Ignored call to stop: Current player state is: " + playerStatus);
|
||||
Log.d(TAG, "Ignored call to stop: Current player state is: " + playerStatus);
|
||||
}
|
||||
playerLock.unlock();
|
||||
|
||||
|
@ -1091,13 +1096,13 @@ public class PlaybackServiceMediaPlayer {
|
|||
@Override
|
||||
public void onFastForward() {
|
||||
super.onFastForward();
|
||||
seekDelta(UserPreferences.getSeekDeltaMs());
|
||||
seekDelta(UserPreferences.getFastFowardSecs() * 1000);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRewind() {
|
||||
super.onRewind();
|
||||
seekDelta(-UserPreferences.getSeekDeltaMs());
|
||||
seekDelta(-UserPreferences.getRewindSecs() * 1000);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -5,14 +5,23 @@ import android.util.Log;
|
|||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import de.danoeh.antennapod.core.BuildConfig;
|
||||
import de.danoeh.antennapod.core.feed.EventDistributor;
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.feed.QueueEvent;
|
||||
import de.danoeh.antennapod.core.storage.DBReader;
|
||||
import de.danoeh.antennapod.core.util.playback.Playable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.*;
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
|
||||
/**
|
||||
* Manages the background tasks of PlaybackSerivce, i.e.
|
||||
|
@ -69,18 +78,13 @@ public class PlaybackServiceTaskManager {
|
|||
}
|
||||
});
|
||||
loadQueue();
|
||||
EventDistributor.getInstance().register(eventDistributorListener);
|
||||
EventBus.getDefault().register(this);
|
||||
}
|
||||
|
||||
private final EventDistributor.EventListener eventDistributorListener = new EventDistributor.EventListener() {
|
||||
@Override
|
||||
public void update(EventDistributor eventDistributor, Integer arg) {
|
||||
if ((EventDistributor.QUEUE_UPDATE & arg) != 0) {
|
||||
cancelQueueLoader();
|
||||
loadQueue();
|
||||
}
|
||||
}
|
||||
};
|
||||
public void onEvent(QueueEvent event) {
|
||||
cancelQueueLoader();
|
||||
loadQueue();
|
||||
}
|
||||
|
||||
private synchronized boolean isQueueLoaderActive() {
|
||||
return queueFuture != null && !queueFuture.isDone();
|
||||
|
@ -145,9 +149,9 @@ public class PlaybackServiceTaskManager {
|
|||
positionSaverFuture = schedExecutor.scheduleWithFixedDelay(positionSaver, POSITION_SAVER_WAITING_INTERVAL,
|
||||
POSITION_SAVER_WAITING_INTERVAL, TimeUnit.MILLISECONDS);
|
||||
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, "Started PositionSaver");
|
||||
Log.d(TAG, "Started PositionSaver");
|
||||
} else {
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, "Call to startPositionSaver was ignored.");
|
||||
Log.d(TAG, "Call to startPositionSaver was ignored.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -312,7 +316,7 @@ public class PlaybackServiceTaskManager {
|
|||
* execution of this method.
|
||||
*/
|
||||
public synchronized void shutdown() {
|
||||
EventDistributor.getInstance().unregister(eventDistributorListener);
|
||||
EventBus.getDefault().unregister(this);
|
||||
cancelAllTasks();
|
||||
schedExecutor.shutdown();
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import java.util.concurrent.ExecutionException;
|
|||
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.util.QueueAccess;
|
||||
import de.danoeh.antennapod.core.util.LongList;
|
||||
|
||||
/**
|
||||
* Implementation of the EpisodeCleanupAlgorithm interface used by AntennaPod.
|
||||
|
@ -24,7 +24,7 @@ public class APCleanupAlgorithm implements EpisodeCleanupAlgorithm<Integer> {
|
|||
public int performCleanup(Context context, Integer episodeNumber) {
|
||||
List<FeedItem> candidates = new ArrayList<FeedItem>();
|
||||
List<FeedItem> downloadedItems = DBReader.getDownloadedItems(context);
|
||||
QueueAccess queue = QueueAccess.IDListAccess(DBReader.getQueueIDList(context));
|
||||
LongList queue = DBReader.getQueueIDList(context);
|
||||
List<FeedItem> delete;
|
||||
for (FeedItem item : downloadedItems) {
|
||||
if (item.hasMedia() && item.getMedia().isDownloaded()
|
||||
|
|
|
@ -2,7 +2,6 @@ package de.danoeh.antennapod.core.storage;
|
|||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.SQLException;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -22,6 +21,7 @@ import de.danoeh.antennapod.core.feed.SimpleChapter;
|
|||
import de.danoeh.antennapod.core.feed.VorbisCommentChapter;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadStatus;
|
||||
import de.danoeh.antennapod.core.util.DownloadError;
|
||||
import de.danoeh.antennapod.core.util.LongList;
|
||||
import de.danoeh.antennapod.core.util.comparator.DownloadStatusComparator;
|
||||
import de.danoeh.antennapod.core.util.comparator.FeedItemPubdateComparator;
|
||||
import de.danoeh.antennapod.core.util.comparator.PlaybackCompletionDateComparator;
|
||||
|
@ -339,8 +339,7 @@ public final class DBReader {
|
|||
}
|
||||
|
||||
static List<FeedItem> getQueue(Context context, PodDBAdapter adapter) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Extracting queue");
|
||||
Log.d(TAG, "getQueue()");
|
||||
|
||||
Cursor itemlistCursor = adapter.getQueueCursor();
|
||||
List<FeedItem> items = extractItemlistFromCursor(adapter,
|
||||
|
@ -359,21 +358,21 @@ public final class DBReader {
|
|||
* @return A list of IDs sorted by the same order as the queue. The caller can wrap the returned
|
||||
* list in a {@link de.danoeh.antennapod.core.util.QueueAccess} object for easier access to the queue's properties.
|
||||
*/
|
||||
public static List<Long> getQueueIDList(Context context) {
|
||||
public static LongList getQueueIDList(Context context) {
|
||||
PodDBAdapter adapter = new PodDBAdapter(context);
|
||||
|
||||
adapter.open();
|
||||
List<Long> result = getQueueIDList(adapter);
|
||||
LongList result = getQueueIDList(adapter);
|
||||
adapter.close();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static List<Long> getQueueIDList(PodDBAdapter adapter) {
|
||||
static LongList getQueueIDList(PodDBAdapter adapter) {
|
||||
adapter.open();
|
||||
Cursor queueCursor = adapter.getQueueIDCursor();
|
||||
|
||||
List<Long> queueIds = new ArrayList<Long>(queueCursor.getCount());
|
||||
LongList queueIds = new LongList(queueCursor.getCount());
|
||||
if (queueCursor.moveToFirst()) {
|
||||
do {
|
||||
queueIds.add(queueCursor.getLong(0));
|
||||
|
@ -383,6 +382,22 @@ public final class DBReader {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the size of the queue.
|
||||
*
|
||||
* @param context A context that is used for opening a database connection.
|
||||
* @return Size of the queue.
|
||||
*/
|
||||
public static int getQueueSize(Context context) {
|
||||
Log.d(TAG, "getQueueSize()");
|
||||
|
||||
PodDBAdapter adapter = new PodDBAdapter(context);
|
||||
adapter.open();
|
||||
int size = adapter.getQueueSize();
|
||||
adapter.close();
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a list of the FeedItems in the queue. If the FeedItems of the queue are not used directly, consider using
|
||||
* {@link #getQueueIDList(android.content.Context)} instead.
|
||||
|
@ -392,8 +407,7 @@ public final class DBReader {
|
|||
* list in a {@link de.danoeh.antennapod.core.util.QueueAccess} object for easier access to the queue's properties.
|
||||
*/
|
||||
public static List<FeedItem> getQueue(Context context) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Extracting queue");
|
||||
Log.d(TAG, "getQueue()");
|
||||
|
||||
PodDBAdapter adapter = new PodDBAdapter(context);
|
||||
adapter.open();
|
||||
|
@ -680,6 +694,42 @@ public final class DBReader {
|
|||
|
||||
}
|
||||
|
||||
static FeedItem getFeedItem(final Context context, final String podcastUrl, final String episodeUrl, PodDBAdapter adapter) {
|
||||
Log.d(TAG, "Loading feeditem with podcast url " + podcastUrl + " and episode url " + episodeUrl);
|
||||
FeedItem item = null;
|
||||
Cursor itemCursor = adapter.getFeedItemCursor(podcastUrl, episodeUrl);
|
||||
if (itemCursor.moveToFirst()) {
|
||||
List<FeedItem> list = extractItemlistFromCursor(adapter, itemCursor);
|
||||
if (list.size() > 0) {
|
||||
item = list.get(0);
|
||||
loadFeedDataOfFeedItemlist(context, list);
|
||||
if (item.hasChapters()) {
|
||||
loadChaptersOfFeedItem(adapter, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a specific FeedItem from the database.
|
||||
*
|
||||
* @param context A context that is used for opening a database connection.
|
||||
* @param podcastUrl the corresponding feed's url
|
||||
* @param episodeUrl the feed item's url
|
||||
* @return The FeedItem or null if the FeedItem could not be found. All FeedComponent-attributes
|
||||
* as well as chapter marks of the FeedItem will also be loaded from the database.
|
||||
*/
|
||||
public static FeedItem getFeedItem(final Context context, final String podcastUrl, final String episodeUrl) {
|
||||
Log.d(TAG, "Loading feeditem with podcast url " + podcastUrl + " and episode url " + episodeUrl);
|
||||
|
||||
PodDBAdapter adapter = new PodDBAdapter(context);
|
||||
adapter.open();
|
||||
FeedItem item = getFeedItem(context, podcastUrl, episodeUrl, adapter);
|
||||
adapter.close();
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads additional information about a FeedItem, e.g. shownotes
|
||||
*
|
||||
|
|
|
@ -34,7 +34,7 @@ import de.danoeh.antennapod.core.service.GpodnetSyncService;
|
|||
import de.danoeh.antennapod.core.service.download.DownloadStatus;
|
||||
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
||||
import de.danoeh.antennapod.core.util.DownloadError;
|
||||
import de.danoeh.antennapod.core.util.QueueAccess;
|
||||
import de.danoeh.antennapod.core.util.LongList;
|
||||
import de.danoeh.antennapod.core.util.comparator.FeedItemPubdateComparator;
|
||||
import de.danoeh.antennapod.core.util.exception.MediaFileNotFoundException;
|
||||
import de.danoeh.antennapod.core.util.flattr.FlattrUtils;
|
||||
|
@ -524,8 +524,8 @@ public final class DBTasks {
|
|||
* @param feedItemId ID of the FeedItem
|
||||
*/
|
||||
public static boolean isInQueue(Context context, final long feedItemId) {
|
||||
List<Long> queue = DBReader.getQueueIDList(context);
|
||||
return QueueAccess.IDListAccess(queue).contains(feedItemId);
|
||||
LongList queue = DBReader.getQueueIDList(context);
|
||||
return queue.contains(feedItemId);
|
||||
}
|
||||
|
||||
private static Feed searchFeedByIdentifyingValueOrID(Context context, PodDBAdapter adapter,
|
||||
|
|
|
@ -7,6 +7,7 @@ import android.content.SharedPreferences;
|
|||
import android.database.Cursor;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
|
||||
import org.shredzone.flattr4j.model.Flattr;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -32,15 +33,18 @@ import de.danoeh.antennapod.core.feed.FeedImage;
|
|||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.feed.FeedPreferences;
|
||||
import de.danoeh.antennapod.core.feed.QueueEvent;
|
||||
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction;
|
||||
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
|
||||
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.service.download.DownloadStatus;
|
||||
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
||||
import de.danoeh.antennapod.core.util.QueueAccess;
|
||||
import de.danoeh.antennapod.core.util.LongList;
|
||||
import de.danoeh.antennapod.core.util.flattr.FlattrStatus;
|
||||
import de.danoeh.antennapod.core.util.flattr.FlattrThing;
|
||||
import de.danoeh.antennapod.core.util.flattr.SimpleFlattrThing;
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
/**
|
||||
* Provides methods for writing data to AntennaPod's database.
|
||||
|
@ -120,10 +124,18 @@ public class DBWriter {
|
|||
PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE));
|
||||
}
|
||||
}
|
||||
// Gpodder: queue delete action for synchronization
|
||||
if(GpodnetPreferences.loggedIn()) {
|
||||
FeedItem item = media.getItem();
|
||||
GpodnetEpisodeAction action = new GpodnetEpisodeAction.Builder(item, GpodnetEpisodeAction.Action.DELETE)
|
||||
.currentDeviceId()
|
||||
.currentTimestamp()
|
||||
.build();
|
||||
GpodnetPreferences.enqueueEpisodeAction(action);
|
||||
}
|
||||
}
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Deleting File. Result: " + result);
|
||||
EventDistributor.getInstance().sendQueueUpdateBroadcast();
|
||||
Log.d(TAG, "Deleting File. Result: " + result);
|
||||
EventBus.getDefault().post(new QueueEvent(QueueEvent.Action.DELETED_MEDIA, media.getItem()));
|
||||
EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast();
|
||||
}
|
||||
}
|
||||
|
@ -337,8 +349,7 @@ public class DBWriter {
|
|||
public void run() {
|
||||
final PodDBAdapter adapter = new PodDBAdapter(context);
|
||||
adapter.open();
|
||||
final List<FeedItem> queue = DBReader
|
||||
.getQueue(context, adapter);
|
||||
final List<FeedItem> queue = DBReader.getQueue(context, adapter);
|
||||
FeedItem item = null;
|
||||
|
||||
if (queue != null) {
|
||||
|
@ -358,8 +369,7 @@ public class DBWriter {
|
|||
}
|
||||
if (queueModified) {
|
||||
adapter.setQueue(queue);
|
||||
EventDistributor.getInstance()
|
||||
.sendQueueUpdateBroadcast();
|
||||
EventBus.getDefault().post(new QueueEvent(QueueEvent.Action.ADDED, item, index));
|
||||
}
|
||||
if (unreadItemsModified && item != null) {
|
||||
adapter.setSingleFeedItem(item);
|
||||
|
@ -427,8 +437,7 @@ public class DBWriter {
|
|||
}
|
||||
if (queueModified) {
|
||||
adapter.setQueue(queue);
|
||||
EventDistributor.getInstance()
|
||||
.sendQueueUpdateBroadcast();
|
||||
EventBus.getDefault().post(new QueueEvent(QueueEvent.Action.ADDED_ITEMS, queue));
|
||||
}
|
||||
if (unreadItemsModified) {
|
||||
adapter.setFeedItemlist(itemsToSave);
|
||||
|
@ -459,7 +468,7 @@ public class DBWriter {
|
|||
adapter.clearQueue();
|
||||
adapter.close();
|
||||
|
||||
EventDistributor.getInstance().sendQueueUpdateBroadcast();
|
||||
EventBus.getDefault().post(new QueueEvent(QueueEvent.Action.CLEARED));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -468,34 +477,25 @@ public class DBWriter {
|
|||
* Removes a FeedItem object from the queue.
|
||||
*
|
||||
* @param context A context that is used for opening a database connection.
|
||||
* @param itemId ID of the FeedItem that should be removed.
|
||||
* @param item FeedItem that should be removed.
|
||||
* @param performAutoDownload true if an auto-download process should be started after the operation.
|
||||
*/
|
||||
public static Future<?> removeQueueItem(final Context context,
|
||||
final long itemId, final boolean performAutoDownload) {
|
||||
final FeedItem item, final boolean performAutoDownload) {
|
||||
return dbExec.submit(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final PodDBAdapter adapter = new PodDBAdapter(context);
|
||||
adapter.open();
|
||||
final List<FeedItem> queue = DBReader
|
||||
.getQueue(context, adapter);
|
||||
FeedItem item = null;
|
||||
final List<FeedItem> queue = DBReader.getQueue(context, adapter);
|
||||
|
||||
if (queue != null) {
|
||||
boolean queueModified = false;
|
||||
QueueAccess queueAccess = QueueAccess.ItemListAccess(queue);
|
||||
if (queueAccess.contains(itemId)) {
|
||||
item = DBReader.getFeedItem(context, itemId);
|
||||
if (item != null) {
|
||||
queueModified = queueAccess.remove(itemId);
|
||||
}
|
||||
}
|
||||
if (queueModified) {
|
||||
int position = queue.indexOf(item);
|
||||
if(position >= 0) {
|
||||
queue.remove(position);
|
||||
adapter.setQueue(queue);
|
||||
EventDistributor.getInstance()
|
||||
.sendQueueUpdateBroadcast();
|
||||
EventBus.getDefault().post(new QueueEvent(QueueEvent.Action.REMOVED, item, position));
|
||||
} else {
|
||||
Log.w(TAG, "Queue was not modified by call to removeQueueItem");
|
||||
}
|
||||
|
@ -523,16 +523,13 @@ public class DBWriter {
|
|||
return dbExec.submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
List<Long> queueIdList = DBReader.getQueueIDList(context);
|
||||
int currentLocation = 0;
|
||||
for (long id : queueIdList) {
|
||||
if (id == itemId) {
|
||||
moveQueueItemHelper(context, currentLocation, 0, broadcastUpdate);
|
||||
return;
|
||||
}
|
||||
currentLocation++;
|
||||
LongList queueIdList = DBReader.getQueueIDList(context);
|
||||
int index = queueIdList.indexOf(itemId);
|
||||
if (index >=0) {
|
||||
moveQueueItemHelper(context, index, 0, broadcastUpdate);
|
||||
} else {
|
||||
Log.e(TAG, "moveQueueItemToTop: item not found");
|
||||
}
|
||||
Log.e(TAG, "moveQueueItemToTop: item not found");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -550,17 +547,14 @@ public class DBWriter {
|
|||
return dbExec.submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
List<Long> queueIdList = DBReader.getQueueIDList(context);
|
||||
int currentLocation = 0;
|
||||
for (long id : queueIdList) {
|
||||
if (id == itemId) {
|
||||
moveQueueItemHelper(context, currentLocation, queueIdList.size() - 1,
|
||||
broadcastUpdate);
|
||||
return;
|
||||
}
|
||||
currentLocation++;
|
||||
LongList queueIdList = DBReader.getQueueIDList(context);
|
||||
int index = queueIdList.indexOf(itemId);
|
||||
if(index >= 0) {
|
||||
moveQueueItemHelper(context, index, queueIdList.size() - 1,
|
||||
broadcastUpdate);
|
||||
} else {
|
||||
Log.e(TAG, "moveQueueItemToBottom: item not found");
|
||||
}
|
||||
Log.e(TAG, "moveQueueItemToBottom: item not found");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -614,8 +608,8 @@ public class DBWriter {
|
|||
|
||||
adapter.setQueue(queue);
|
||||
if (broadcastUpdate) {
|
||||
EventDistributor.getInstance()
|
||||
.sendQueueUpdateBroadcast();
|
||||
EventBus.getDefault().post(new QueueEvent(QueueEvent.Action.REMOVED, item));
|
||||
EventBus.getDefault().post(new QueueEvent(QueueEvent.Action.ADDED, item, to));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -625,6 +619,19 @@ public class DBWriter {
|
|||
adapter.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the 'read'-attribute of a FeedItem to the specified value.
|
||||
*
|
||||
* @param context A context that is used for opening a database connection.
|
||||
* @param itemId ID of the FeedItem
|
||||
* @param read New value of the 'read'-attribute
|
||||
*/
|
||||
public static Future<?> markItemRead(final Context context, final long itemId,
|
||||
final boolean read) {
|
||||
return markItemRead(context, itemId, read, 0, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the 'read'-attribute of a FeedItem to the specified value.
|
||||
*
|
||||
|
@ -639,18 +646,6 @@ public class DBWriter {
|
|||
return markItemRead(context, item.getId(), read, mediaId, resetMediaPosition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the 'read'-attribute of a FeedItem to the specified value.
|
||||
*
|
||||
* @param context A context that is used for opening a database connection.
|
||||
* @param itemId ID of the FeedItem
|
||||
* @param read New value of the 'read'-attribute
|
||||
*/
|
||||
public static Future<?> markItemRead(final Context context, final long itemId,
|
||||
final boolean read) {
|
||||
return markItemRead(context, itemId, read, 0, false);
|
||||
}
|
||||
|
||||
private static Future<?> markItemRead(final Context context, final long itemId,
|
||||
final boolean read, final long mediaId,
|
||||
final boolean resetMediaPosition) {
|
||||
|
@ -1036,8 +1031,7 @@ public class DBWriter {
|
|||
Collections.sort(queue, comparator);
|
||||
adapter.setQueue(queue);
|
||||
if (broadcastUpdate) {
|
||||
EventDistributor.getInstance()
|
||||
.sendQueueUpdateBroadcast();
|
||||
EventBus.getDefault().post(new QueueEvent(QueueEvent.Action.SORTED));
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "sortQueue: Could not load queue");
|
||||
|
|
|
@ -1120,7 +1120,11 @@ public class PodDBAdapter {
|
|||
return c;
|
||||
}
|
||||
|
||||
public final Cursor getFeedItemCursor(final String... ids) {
|
||||
public final Cursor getFeedItemCursor(final String id) {
|
||||
return getFeedItemCursor(new String[] { id });
|
||||
}
|
||||
|
||||
public final Cursor getFeedItemCursor(final String[] ids) {
|
||||
if (ids.length > IN_OPERATOR_MAXIMUM) {
|
||||
throw new IllegalArgumentException(
|
||||
"number of IDs must not be larger than "
|
||||
|
@ -1133,6 +1137,15 @@ public class PodDBAdapter {
|
|||
|
||||
}
|
||||
|
||||
public final Cursor getFeedItemCursor(final String podcastUrl, final String episodeUrl) {
|
||||
final String query = "SELECT " + SEL_FI_SMALL_STR + " FROM " + TABLE_NAME_FEED_ITEMS
|
||||
+ " INNER JOIN " +
|
||||
TABLE_NAME_FEEDS + " ON " + TABLE_NAME_FEED_ITEMS + "." + KEY_FEED + "=" +
|
||||
TABLE_NAME_FEEDS + "." + KEY_ID + " WHERE " + TABLE_NAME_FEED_ITEMS + "." + KEY_ITEM_IDENTIFIER + "='" +
|
||||
episodeUrl + "' AND " + TABLE_NAME_FEEDS + "." + KEY_DOWNLOAD_URL + "='" + podcastUrl + "'";
|
||||
return db.rawQuery(query, null);
|
||||
}
|
||||
|
||||
public int getQueueSize() {
|
||||
final String query = String.format("SELECT COUNT(%s) FROM %s", KEY_ID, TABLE_NAME_QUEUE);
|
||||
Cursor c = db.rawQuery(query, null);
|
||||
|
|
|
@ -3,7 +3,7 @@ package de.danoeh.antennapod.core.syndication.namespace;
|
|||
import org.xml.sax.Attributes;
|
||||
|
||||
import de.danoeh.antennapod.core.syndication.handler.HandlerState;
|
||||
import de.danoeh.antennapod.core.syndication.util.SyndDateUtils;
|
||||
import de.danoeh.antennapod.core.util.DateUtils;
|
||||
|
||||
public class NSDublinCore extends Namespace {
|
||||
private static final String TAG = "NSDublinCore";
|
||||
|
@ -30,7 +30,7 @@ public class NSDublinCore extends Namespace {
|
|||
String second = secondElement.getName();
|
||||
if (top.equals(DATE) && second.equals(ITEM)) {
|
||||
state.getCurrentItem().setPubDate(
|
||||
SyndDateUtils.parseISO8601Date(content));
|
||||
DateUtils.parse(content));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
package de.danoeh.antennapod.core.syndication.namespace;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.xml.sax.Attributes;
|
||||
|
||||
import de.danoeh.antennapod.core.BuildConfig;
|
||||
import de.danoeh.antennapod.core.feed.FeedImage;
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.syndication.handler.HandlerState;
|
||||
import de.danoeh.antennapod.core.syndication.util.SyndDateUtils;
|
||||
import de.danoeh.antennapod.core.syndication.util.SyndTypeUtils;
|
||||
import org.xml.sax.Attributes;
|
||||
import de.danoeh.antennapod.core.util.DateUtils;
|
||||
|
||||
/**
|
||||
* SAX-Parser for reading RSS-Feeds
|
||||
|
@ -129,7 +131,7 @@ public class NSRSS20 extends Namespace {
|
|||
}
|
||||
} else if (top.equals(PUBDATE) && second.equals(ITEM)) {
|
||||
state.getCurrentItem().setPubDate(
|
||||
SyndDateUtils.parseRFC822Date(content));
|
||||
DateUtils.parse(content));
|
||||
} else if (top.equals(URL) && second.equals(IMAGE) && third != null
|
||||
&& third.equals(CHANNEL)) {
|
||||
state.getFeed().getImage().setDownload_url(content);
|
||||
|
|
|
@ -10,7 +10,7 @@ import de.danoeh.antennapod.core.BuildConfig;
|
|||
import de.danoeh.antennapod.core.feed.Chapter;
|
||||
import de.danoeh.antennapod.core.feed.SimpleChapter;
|
||||
import de.danoeh.antennapod.core.syndication.handler.HandlerState;
|
||||
import de.danoeh.antennapod.core.syndication.util.SyndDateUtils;
|
||||
import de.danoeh.antennapod.core.util.DateUtils;
|
||||
|
||||
public class NSSimpleChapters extends Namespace {
|
||||
private static final String TAG = "NSSimpleChapters";
|
||||
|
@ -33,7 +33,7 @@ public class NSSimpleChapters extends Namespace {
|
|||
try {
|
||||
state.getCurrentItem()
|
||||
.getChapters()
|
||||
.add(new SimpleChapter(SyndDateUtils
|
||||
.add(new SimpleChapter(DateUtils
|
||||
.parseTimeString(attributes.getValue(START)),
|
||||
attributes.getValue(TITLE), state.getCurrentItem(),
|
||||
attributes.getValue(HREF)));
|
||||
|
|
|
@ -13,8 +13,8 @@ import de.danoeh.antennapod.core.syndication.namespace.NSITunes;
|
|||
import de.danoeh.antennapod.core.syndication.namespace.NSRSS20;
|
||||
import de.danoeh.antennapod.core.syndication.namespace.Namespace;
|
||||
import de.danoeh.antennapod.core.syndication.namespace.SyndElement;
|
||||
import de.danoeh.antennapod.core.syndication.util.SyndDateUtils;
|
||||
import de.danoeh.antennapod.core.syndication.util.SyndTypeUtils;
|
||||
import de.danoeh.antennapod.core.util.DateUtils;
|
||||
|
||||
public class NSAtom extends Namespace {
|
||||
private static final String TAG = "NSAtom";
|
||||
|
@ -191,12 +191,12 @@ public class NSAtom extends Namespace {
|
|||
if (second.equals(ENTRY)
|
||||
&& state.getCurrentItem().getPubDate() == null) {
|
||||
state.getCurrentItem().setPubDate(
|
||||
SyndDateUtils.parseRFC3339Date(content));
|
||||
DateUtils.parse(content));
|
||||
}
|
||||
} else if (top.equals(PUBLISHED)) {
|
||||
if (second.equals(ENTRY)) {
|
||||
state.getCurrentItem().setPubDate(
|
||||
SyndDateUtils.parseRFC3339Date(content));
|
||||
DateUtils.parse(content));
|
||||
}
|
||||
} else if (top.equals(IMAGE)) {
|
||||
state.getFeed().setImage(new FeedImage(state.getFeed(), content, null));
|
||||
|
|
|
@ -1,194 +0,0 @@
|
|||
package de.danoeh.antennapod.core.syndication.util;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
import de.danoeh.antennapod.core.BuildConfig;
|
||||
|
||||
/**
|
||||
* Parses several date formats.
|
||||
*/
|
||||
public class SyndDateUtils {
|
||||
private static final String TAG = "DateUtils";
|
||||
|
||||
private static final String[] RFC822DATES = {"dd MMM yy HH:mm:ss Z",
|
||||
"dd MMM yy HH:mm Z"};
|
||||
|
||||
/**
|
||||
* RFC 3339 date format for UTC dates.
|
||||
*/
|
||||
public static final String RFC3339UTC = "yyyy-MM-dd'T'HH:mm:ss'Z'";
|
||||
|
||||
/**
|
||||
* RFC 3339 date format for localtime dates with offset.
|
||||
*/
|
||||
public static final String RFC3339LOCAL = "yyyy-MM-dd'T'HH:mm:ssZ";
|
||||
|
||||
public static final String ISO8601_SHORT = "yyyy-MM-dd";
|
||||
|
||||
private static ThreadLocal<SimpleDateFormat> RFC822Formatter = new ThreadLocal<SimpleDateFormat>() {
|
||||
@Override
|
||||
protected SimpleDateFormat initialValue() {
|
||||
return new SimpleDateFormat(RFC822DATES[0], Locale.US);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private static ThreadLocal<SimpleDateFormat> RFC3339Formatter = new ThreadLocal<SimpleDateFormat>() {
|
||||
@Override
|
||||
protected SimpleDateFormat initialValue() {
|
||||
return new SimpleDateFormat(RFC3339UTC, Locale.US);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private static ThreadLocal<SimpleDateFormat> ISO8601ShortFormatter = new ThreadLocal<SimpleDateFormat>() {
|
||||
@Override
|
||||
protected SimpleDateFormat initialValue() {
|
||||
return new SimpleDateFormat(ISO8601_SHORT, Locale.US);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public static Date parseRFC822Date(String date) {
|
||||
Date result = null;
|
||||
if (date.contains("PDT")) {
|
||||
date = date.replace("PDT", "PST8PDT");
|
||||
}
|
||||
if (date.contains(",")) {
|
||||
// Remove day of the week
|
||||
date = date.substring(date.indexOf(",") + 1).trim();
|
||||
}
|
||||
SimpleDateFormat format = RFC822Formatter.get();
|
||||
|
||||
for (String RFC822DATE : RFC822DATES) {
|
||||
try {
|
||||
format.applyPattern(RFC822DATE);
|
||||
result = format.parse(date);
|
||||
break;
|
||||
} catch (ParseException e) {
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, "ParserException", e);
|
||||
}
|
||||
}
|
||||
if (result == null) {
|
||||
Log.e(TAG, "Unable to parse feed date correctly:" + date);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Date parseRFC3339Date(String date) {
|
||||
Date result = null;
|
||||
SimpleDateFormat format = RFC3339Formatter.get();
|
||||
boolean isLocal = date.endsWith("Z");
|
||||
if (date.contains(".")) {
|
||||
// remove secfrac
|
||||
int fracIndex = date.indexOf(".");
|
||||
String first = date.substring(0, fracIndex);
|
||||
String second = null;
|
||||
if (isLocal) {
|
||||
second = date.substring(date.length() - 1);
|
||||
} else {
|
||||
if (date.contains("+")) {
|
||||
second = date.substring(date.indexOf("+"));
|
||||
} else {
|
||||
second = date.substring(date.indexOf("-"));
|
||||
}
|
||||
}
|
||||
|
||||
date = first + second;
|
||||
}
|
||||
if (isLocal) {
|
||||
try {
|
||||
result = format.parse(date);
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
format.applyPattern(RFC3339LOCAL);
|
||||
// remove last colon
|
||||
StringBuffer buf = new StringBuffer(date.length() - 1);
|
||||
int colonIdx = date.lastIndexOf(':');
|
||||
for (int x = 0; x < date.length(); x++) {
|
||||
if (x != colonIdx)
|
||||
buf.append(date.charAt(x));
|
||||
}
|
||||
String bufStr = buf.toString();
|
||||
try {
|
||||
result = format.parse(bufStr);
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "Unable to parse date");
|
||||
} finally {
|
||||
format.applyPattern(RFC3339UTC);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
public static Date parseISO8601Date(String date) {
|
||||
if(date.length() > ISO8601_SHORT.length()) {
|
||||
return parseRFC3339Date(date);
|
||||
}
|
||||
Date result = null;
|
||||
if(date.length() == "YYYYMMDD".length()) {
|
||||
date = date.substring(0, 4) + "-" + date.substring(4, 6) + "-" + date.substring(6,8);
|
||||
}
|
||||
SimpleDateFormat format = ISO8601ShortFormatter.get();
|
||||
try {
|
||||
result = format.parse(date);
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a string of the form [HH:]MM:SS[.mmm] and converts it to
|
||||
* milliseconds.
|
||||
*
|
||||
* @throws java.lang.NumberFormatException if the number segments contain invalid numbers.
|
||||
*/
|
||||
public static long parseTimeString(final String time) {
|
||||
String[] parts = time.split(":");
|
||||
long result = 0;
|
||||
int idx = 0;
|
||||
if (parts.length == 3) {
|
||||
// string has hours
|
||||
result += Integer.valueOf(parts[idx]) * 3600000L;
|
||||
idx++;
|
||||
}
|
||||
if (parts.length >= 2) {
|
||||
result += Integer.valueOf(parts[idx]) * 60000L;
|
||||
idx++;
|
||||
result += (Float.valueOf(parts[idx])) * 1000L;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String formatRFC822Date(Date date) {
|
||||
SimpleDateFormat format = RFC822Formatter.get();
|
||||
return format.format(date);
|
||||
}
|
||||
|
||||
public static String formatRFC3339Local(Date date) {
|
||||
SimpleDateFormat format = RFC3339Formatter.get();
|
||||
format.applyPattern(RFC3339LOCAL);
|
||||
String result = format.format(date);
|
||||
format.applyPattern(RFC3339UTC);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String formatRFC3339UTC(Date date) {
|
||||
SimpleDateFormat format = RFC3339Formatter.get();
|
||||
format.applyPattern(RFC3339UTC);
|
||||
return format.format(date);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
package de.danoeh.antennapod.core.util;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.text.ParsePosition;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Parses several date formats.
|
||||
*/
|
||||
public class DateUtils {
|
||||
private static final String TAG = "DateUtils";
|
||||
|
||||
private static final String[] RFC822DATES = {"dd MMM yy HH:mm:ss Z",
|
||||
"dd MMM yy HH:mm Z"};
|
||||
|
||||
/**
|
||||
* RFC 3339 date format for UTC dates.
|
||||
*/
|
||||
public static final String RFC3339UTC = "yyyy-MM-dd'T'HH:mm:ss'Z'";
|
||||
|
||||
/**
|
||||
* RFC 3339 date format for localtime dates with offset.
|
||||
*/
|
||||
public static final String RFC3339LOCAL = "yyyy-MM-dd'T'HH:mm:ssZ";
|
||||
|
||||
public static final String ISO8601_SHORT = "yyyy-MM-dd";
|
||||
|
||||
private static ThreadLocal<SimpleDateFormat> RFC822Formatter = new ThreadLocal<SimpleDateFormat>() {
|
||||
@Override
|
||||
protected SimpleDateFormat initialValue() {
|
||||
return new SimpleDateFormat("dd MMM yy HH:mm:ss Z", Locale.US);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private static ThreadLocal<SimpleDateFormat> RFC3339Formatter = new ThreadLocal<SimpleDateFormat>() {
|
||||
@Override
|
||||
protected SimpleDateFormat initialValue() {
|
||||
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public static Date parse(String date) {
|
||||
if(date == null) {
|
||||
throw new IllegalArgumentException("Date most not be null");
|
||||
}
|
||||
date = date.replace('/', ' ');
|
||||
date = date.replace('-', ' ');
|
||||
if(date.contains(".")) {
|
||||
int start = date.indexOf('.');
|
||||
int current = start+1;
|
||||
while(current < date.length() && Character.isDigit(date.charAt(current))) {
|
||||
current++;
|
||||
}
|
||||
if(current - start > 4) {
|
||||
if(current < date.length()-1) {
|
||||
date = date.substring(0, start + 4) + date.substring(current);
|
||||
} else {
|
||||
date = date.substring(0, start + 4);
|
||||
}
|
||||
} else if(current - start < 4) {
|
||||
if(current < date.length()-1) {
|
||||
date = date.substring(0, current) + StringUtils.repeat("0", 4-(current-start)) + date.substring(current);
|
||||
} else {
|
||||
date = date.substring(0, current) + StringUtils.repeat("0", 4-(current-start));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
String[] patterns = {
|
||||
"dd MMM yy HH:mm:ss Z",
|
||||
"dd MMM yy HH:mm Z",
|
||||
"EEE, dd MMM yyyy HH:mm:ss Z",
|
||||
"EEEE, dd MMM yy HH:mm:ss Z",
|
||||
"EEE MMM d HH:mm:ss yyyy",
|
||||
"yyyy MM dd'T'HH:mm:ss",
|
||||
"yyyy MM dd'T'HH:mm:ss.SSS",
|
||||
"yyyy MM dd'T'HH:mm:ss.SSS Z",
|
||||
"yyyy MM dd'T'HH:mm:ssZ",
|
||||
"yyyy MM dd'T'HH:mm:ss'Z'",
|
||||
"yyyy MM ddZ",
|
||||
"yyyy MM dd"
|
||||
};
|
||||
SimpleDateFormat parser = new SimpleDateFormat("", Locale.US);
|
||||
parser.setLenient(false);
|
||||
ParsePosition pos = new ParsePosition(0);
|
||||
for(String pattern : patterns) {
|
||||
parser.applyPattern(pattern);
|
||||
pos.setIndex(0);
|
||||
Date result = parser.parse(date, pos);
|
||||
if(result != null && pos.getIndex() == date.length()) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Takes a string of the form [HH:]MM:SS[.mmm] and converts it to
|
||||
* milliseconds.
|
||||
*
|
||||
* @throws java.lang.NumberFormatException if the number segments contain invalid numbers.
|
||||
*/
|
||||
public static long parseTimeString(final String time) {
|
||||
String[] parts = time.split(":");
|
||||
long result = 0;
|
||||
int idx = 0;
|
||||
if (parts.length == 3) {
|
||||
// string has hours
|
||||
result += Integer.valueOf(parts[idx]) * 3600000L;
|
||||
idx++;
|
||||
}
|
||||
if (parts.length >= 2) {
|
||||
result += Integer.valueOf(parts[idx]) * 60000L;
|
||||
idx++;
|
||||
result += (Float.valueOf(parts[idx])) * 1000L;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String formatRFC822Date(Date date) {
|
||||
SimpleDateFormat format = new SimpleDateFormat("dd MMM yy HH:mm:ss Z", Locale.US);
|
||||
return format.format(date);
|
||||
}
|
||||
|
||||
public static String formatRFC3339Local(Date date) {
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US);
|
||||
return format.format(date);
|
||||
}
|
||||
|
||||
public static String formatRFC3339UTC(Date date) {
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
|
||||
return format.format(date);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,241 @@
|
|||
package de.danoeh.antennapod.core.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Fast and memory efficient long list
|
||||
*/
|
||||
public final class LongList {
|
||||
|
||||
private long[] values;
|
||||
private int size;
|
||||
|
||||
/**
|
||||
* Constructs an empty instance with a default initial capacity.
|
||||
*/
|
||||
public LongList() {
|
||||
this(4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an empty instance.
|
||||
*
|
||||
* @param initialCapacity {@code >= 0;} initial capacity of the list
|
||||
*/
|
||||
public LongList(int initialCapacity) {
|
||||
if(initialCapacity < 0) {
|
||||
throw new IllegalArgumentException("initial capacity must be 0 or higher");
|
||||
}
|
||||
values = new long[initialCapacity];
|
||||
size = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
Arrays.hashCode(values);
|
||||
int hashCode = 1;
|
||||
for (int i = 0; i < size; i++) {
|
||||
long value = values[i];
|
||||
hashCode = 31 * hashCode + (int)(value ^ (value >>> 32));
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other == this) {
|
||||
return true;
|
||||
}
|
||||
if (! (other instanceof LongList)) {
|
||||
return false;
|
||||
}
|
||||
LongList otherList = (LongList) other;
|
||||
if (size != otherList.size) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (values[i] != otherList.values[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer(size * 5 + 10);
|
||||
sb.append("LongList{");
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (i != 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append(values[i]);
|
||||
}
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of elements in this list.
|
||||
*/
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the indicated value.
|
||||
*
|
||||
* @param n {@code >= 0, < size();} which element
|
||||
* @return the indicated element's value
|
||||
*/
|
||||
public long get(int n) {
|
||||
if (n >= size) {
|
||||
throw new IndexOutOfBoundsException("n >= size()");
|
||||
} else if(n < 0) {
|
||||
throw new IndexOutOfBoundsException("n < 0");
|
||||
}
|
||||
return values[n];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value at the given index.
|
||||
*
|
||||
* @param index the index at which to put the specified object.
|
||||
* @param value the object to add.
|
||||
* @return the previous element at the index.
|
||||
*/
|
||||
public long set(int index, long value) {
|
||||
if (index >= size) {
|
||||
throw new IndexOutOfBoundsException("n >= size()");
|
||||
} else if(index < 0) {
|
||||
throw new IndexOutOfBoundsException("n < 0");
|
||||
}
|
||||
long result = values[index];
|
||||
values[index] = value;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an element to the end of the list. This will increase the
|
||||
* list's capacity if necessary.
|
||||
*
|
||||
* @param value the value to add
|
||||
*/
|
||||
public void add(long value) {
|
||||
growIfNeeded();
|
||||
values[size++] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts element into specified index, moving elements at and above
|
||||
* that index up one. May not be used to insert at an index beyond the
|
||||
* current size (that is, insertion as a last element is legal but
|
||||
* no further).
|
||||
*
|
||||
* @param n {@code >= 0, <=size();} index of where to insert
|
||||
* @param value value to insert
|
||||
*/
|
||||
public void insert(int n, int value) {
|
||||
if (n > size) {
|
||||
throw new IndexOutOfBoundsException("n > size()");
|
||||
} else if(n < 0) {
|
||||
throw new IndexOutOfBoundsException("n < 0");
|
||||
}
|
||||
|
||||
growIfNeeded();
|
||||
|
||||
System.arraycopy (values, n, values, n+1, size - n);
|
||||
values[n] = value;
|
||||
size++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes value from this list.
|
||||
*
|
||||
* @param value value to remove
|
||||
* return {@code true} if the value was removed, {@code false} otherwise
|
||||
*/
|
||||
public boolean remove(long value) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (values[i] == value) {
|
||||
size--;
|
||||
System.arraycopy(values, i+1, values, i, size-i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an element at a given index, shifting elements at greater
|
||||
* indicies down one.
|
||||
*
|
||||
* @param index index of element to remove
|
||||
*/
|
||||
public void removeIndex(int index) {
|
||||
if (index >= size) {
|
||||
throw new IndexOutOfBoundsException("n >= size()");
|
||||
} else if(index < 0) {
|
||||
throw new IndexOutOfBoundsException("n < 0");
|
||||
}
|
||||
size--;
|
||||
System.arraycopy (values, index + 1, values, index, size - index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increases size of array if needed
|
||||
*/
|
||||
private void growIfNeeded() {
|
||||
if (size == values.length) {
|
||||
// Resize.
|
||||
long[] newArray = new long[size * 3 / 2 + 10];
|
||||
System.arraycopy(values, 0, newArray, 0, size);
|
||||
values = newArray;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the given value, or -1 if the value does not
|
||||
* appear in the list.
|
||||
*
|
||||
* @param value value to find
|
||||
* @return index of value or -1
|
||||
*/
|
||||
public int indexOf(long value) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (values[i] == value) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all values from this list.
|
||||
*/
|
||||
public void clear() {
|
||||
values = new long[4];
|
||||
size = 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the given value is contained in the list
|
||||
*
|
||||
* @param value value to look for
|
||||
* @return {@code true} if this list contains {@code value}, {@code false} otherwise
|
||||
*/
|
||||
public boolean contains(long value) {
|
||||
return indexOf(value) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with a copy of this list's values
|
||||
*
|
||||
* @return array with a copy of this list's values
|
||||
*/
|
||||
public long[] toArray() {
|
||||
return Arrays.copyOf(values, size);
|
||||
|
||||
}
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
package de.danoeh.antennapod.core.util;
|
||||
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
|
||||
/**
|
||||
* Provides methods for accessing the queue. It is possible to load only a part of the information about the queue that
|
||||
* is stored in the database (e.g. sometimes the user just has to test if a specific item is contained in the List.
|
||||
|
@ -25,23 +25,6 @@ public abstract class QueueAccess {
|
|||
public abstract boolean remove(long id);
|
||||
|
||||
private QueueAccess() {
|
||||
|
||||
}
|
||||
|
||||
public static QueueAccess IDListAccess(final List<Long> ids) {
|
||||
return new QueueAccess() {
|
||||
@Override
|
||||
public boolean contains(long id) {
|
||||
return (ids != null) && ids.contains(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(long id) {
|
||||
return ids.remove(id);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
public static QueueAccess ItemListAccess(final List<FeedItem> items) {
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
package de.danoeh.antennapod.core.util.gui;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Parcelable;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
|
@ -16,23 +13,36 @@ import de.danoeh.antennapod.core.R;
|
|||
|
||||
import static com.nineoldandroids.view.ViewPropertyAnimator.animate;
|
||||
|
||||
public class UndoBarController {
|
||||
public class UndoBarController<T> {
|
||||
private View mBarView;
|
||||
private TextView mMessageView;
|
||||
private ViewPropertyAnimator mBarAnimator;
|
||||
private Handler mHideHandler = new Handler();
|
||||
|
||||
private UndoListener mUndoListener;
|
||||
private UndoListener<T> mUndoListener;
|
||||
|
||||
// State objects
|
||||
private Parcelable mUndoToken;
|
||||
private T mUndoToken;
|
||||
private CharSequence mUndoMessage;
|
||||
|
||||
public interface UndoListener {
|
||||
void onUndo(Parcelable token);
|
||||
public interface UndoListener<T> {
|
||||
/**
|
||||
* This callback function is called when the undo button is pressed
|
||||
*
|
||||
* @param token
|
||||
*/
|
||||
void onUndo(T token);
|
||||
|
||||
/**
|
||||
*
|
||||
* This callback function is called when the bar fades out without button press
|
||||
*
|
||||
* @param token
|
||||
*/
|
||||
void onHide(T token);
|
||||
}
|
||||
|
||||
public UndoBarController(View undoBarView, UndoListener undoListener) {
|
||||
public UndoBarController(View undoBarView, UndoListener<T> undoListener) {
|
||||
mBarView = undoBarView;
|
||||
mBarAnimator = animate(mBarView);
|
||||
mUndoListener = undoListener;
|
||||
|
@ -50,7 +60,7 @@ public class UndoBarController {
|
|||
hideUndoBar(true);
|
||||
}
|
||||
|
||||
public void showUndoBar(boolean immediate, CharSequence message, Parcelable undoToken) {
|
||||
public void showUndoBar(boolean immediate, CharSequence message, T undoToken) {
|
||||
mUndoToken = undoToken;
|
||||
mUndoMessage = message;
|
||||
mMessageView.setText(mUndoMessage);
|
||||
|
@ -73,6 +83,14 @@ public class UndoBarController {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean isShowing() {
|
||||
return mBarView.getVisibility() == View.VISIBLE;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
mHideHandler.post(mHideRunnable);
|
||||
}
|
||||
|
||||
public void hideUndoBar(boolean immediate) {
|
||||
mHideHandler.removeCallbacks(mHideRunnable);
|
||||
if (immediate) {
|
||||
|
@ -96,26 +114,11 @@ public class UndoBarController {
|
|||
}
|
||||
}
|
||||
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
outState.putCharSequence("undo_message", mUndoMessage);
|
||||
outState.putParcelable("undo_token", mUndoToken);
|
||||
}
|
||||
|
||||
public void onRestoreInstanceState(Bundle savedInstanceState) {
|
||||
if (savedInstanceState != null) {
|
||||
mUndoMessage = savedInstanceState.getCharSequence("undo_message");
|
||||
mUndoToken = savedInstanceState.getParcelable("undo_token");
|
||||
|
||||
if (mUndoToken != null || !TextUtils.isEmpty(mUndoMessage)) {
|
||||
showUndoBar(true, mUndoMessage, mUndoToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Runnable mHideRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
hideUndoBar(false);
|
||||
mUndoListener.onHide(mUndoToken);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
package de.danoeh.antennapod.core.util.playback;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.*;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.TypedArray;
|
||||
import android.media.MediaPlayer;
|
||||
import android.os.AsyncTask;
|
||||
|
@ -19,13 +25,18 @@ import android.widget.TextView;
|
|||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
import de.danoeh.antennapod.core.BuildConfig;
|
||||
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;
|
||||
|
||||
import de.danoeh.antennapod.core.R;
|
||||
import de.danoeh.antennapod.core.feed.Chapter;
|
||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||
import de.danoeh.antennapod.core.feed.MediaType;
|
||||
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
|
||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
||||
import de.danoeh.antennapod.core.service.playback.PlaybackServiceMediaPlayer;
|
||||
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
|
||||
|
@ -33,8 +44,6 @@ import de.danoeh.antennapod.core.storage.DBTasks;
|
|||
import de.danoeh.antennapod.core.util.Converter;
|
||||
import de.danoeh.antennapod.core.util.playback.Playable.PlayableUtils;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* Communicates with the playback service. GUI classes should use this class to
|
||||
* control playback instead of communicating with the PlaybackService directly.
|
||||
|
@ -118,8 +127,7 @@ public abstract class PlaybackController {
|
|||
* example in the activity's onStop() method.
|
||||
*/
|
||||
public void release() {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Releasing PlaybackController");
|
||||
Log.d(TAG, "Releasing PlaybackController");
|
||||
|
||||
try {
|
||||
activity.unregisterReceiver(statusUpdate);
|
||||
|
@ -164,8 +172,7 @@ public abstract class PlaybackController {
|
|||
* as the arguments of the launch intent.
|
||||
*/
|
||||
private void bindToService() {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Trying to connect to service");
|
||||
Log.d(TAG, "Trying to connect to service");
|
||||
AsyncTask<Void, Void, Intent> intentLoader = new AsyncTask<Void, Void, Intent>() {
|
||||
@Override
|
||||
protected Intent doInBackground(Void... voids) {
|
||||
|
@ -177,7 +184,7 @@ public abstract class PlaybackController {
|
|||
boolean bound = false;
|
||||
if (!PlaybackService.started) {
|
||||
if (serviceIntent != null) {
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, "Calling start service");
|
||||
Log.d(TAG, "Calling start service");
|
||||
activity.startService(serviceIntent);
|
||||
bound = activity.bindService(serviceIntent, mConnection, 0);
|
||||
} else {
|
||||
|
@ -186,14 +193,11 @@ public abstract class PlaybackController {
|
|||
handleStatus();
|
||||
}
|
||||
} else {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG,
|
||||
"PlaybackService is running, trying to connect without start command.");
|
||||
Log.d(TAG, "PlaybackService is running, trying to connect without start command.");
|
||||
bound = activity.bindService(new Intent(activity,
|
||||
PlaybackService.class), mConnection, 0);
|
||||
}
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Result for service binding: " + bound);
|
||||
Log.d(TAG, "Result for service binding: " + bound);
|
||||
}
|
||||
};
|
||||
intentLoader.execute();
|
||||
|
@ -204,8 +208,7 @@ public abstract class PlaybackController {
|
|||
* played media or null if no last played media could be found.
|
||||
*/
|
||||
private Intent getPlayLastPlayedMediaIntent() {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Trying to restore last played media");
|
||||
Log.d(TAG, "Trying to restore last played media");
|
||||
SharedPreferences prefs = PreferenceManager
|
||||
.getDefaultSharedPreferences(activity.getApplicationContext());
|
||||
long currentlyPlayingMedia = PlaybackPreferences
|
||||
|
@ -233,8 +236,7 @@ public abstract class PlaybackController {
|
|||
return serviceIntent;
|
||||
}
|
||||
}
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "No last played media found");
|
||||
Log.d(TAG, "No last played media found");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -246,8 +248,7 @@ public abstract class PlaybackController {
|
|||
|| (positionObserverFuture != null && positionObserverFuture
|
||||
.isDone()) || positionObserverFuture == null) {
|
||||
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Setting up position observer");
|
||||
Log.d(TAG, "Setting up position observer");
|
||||
positionObserver = new MediaPositionObserver();
|
||||
positionObserverFuture = schedExecutor.scheduleWithFixedDelay(
|
||||
positionObserver, MediaPositionObserver.WAITING_INTERVALL,
|
||||
|
@ -259,8 +260,7 @@ public abstract class PlaybackController {
|
|||
private void cancelPositionObserver() {
|
||||
if (positionObserverFuture != null) {
|
||||
boolean result = positionObserverFuture.cancel(true);
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "PositionObserver cancelled. Result: " + result);
|
||||
Log.d(TAG, "PositionObserver cancelled. Result: " + result);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -272,8 +272,7 @@ public abstract class PlaybackController {
|
|||
.getService();
|
||||
if (!released) {
|
||||
queryService();
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Connection to Service established");
|
||||
Log.d(TAG, "Connection to Service established");
|
||||
} else {
|
||||
Log.i(TAG, "Connection to playback service has been established, but controller has already been released");
|
||||
}
|
||||
|
@ -282,17 +281,14 @@ public abstract class PlaybackController {
|
|||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
playbackService = null;
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Disconnected from Service");
|
||||
|
||||
Log.d(TAG, "Disconnected from Service");
|
||||
}
|
||||
};
|
||||
|
||||
protected BroadcastReceiver statusUpdate = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Received statusUpdate Intent.");
|
||||
Log.d(TAG, "Received statusUpdate Intent.");
|
||||
if (isConnectedToPlaybackService()) {
|
||||
PlaybackServiceMediaPlayer.PSMPInfo info = playbackService.getPSMPInfo();
|
||||
status = info.playerStatus;
|
||||
|
@ -349,8 +345,7 @@ public abstract class PlaybackController {
|
|||
}
|
||||
|
||||
} else {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Bad arguments. Won't handle intent");
|
||||
Log.d(TAG, "Bad arguments. Won't handle intent");
|
||||
}
|
||||
} else {
|
||||
bindToService();
|
||||
|
@ -421,6 +416,7 @@ public abstract class PlaybackController {
|
|||
pauseResource = R.drawable.ic_av_pause_circle_outline_80dp;
|
||||
}
|
||||
|
||||
Log.d(TAG, "status: " + status.toString());
|
||||
switch (status) {
|
||||
|
||||
case ERROR:
|
||||
|
@ -466,6 +462,7 @@ public abstract class PlaybackController {
|
|||
updatePlayButtonAppearance(playResource, playText);
|
||||
break;
|
||||
case SEEKING:
|
||||
onPositionObserverUpdate();
|
||||
postStatusMsg(R.string.player_seeking_msg);
|
||||
break;
|
||||
case INITIALIZED:
|
||||
|
@ -501,8 +498,7 @@ public abstract class PlaybackController {
|
|||
* information has to be refreshed
|
||||
*/
|
||||
void queryService() {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.d(TAG, "Querying service info");
|
||||
Log.d(TAG, "Querying service info");
|
||||
if (playbackService != null) {
|
||||
status = playbackService.getStatus();
|
||||
media = playbackService.getPlayable();
|
||||
|
@ -610,28 +606,6 @@ public abstract class PlaybackController {
|
|||
};
|
||||
}
|
||||
|
||||
public OnClickListener newOnRevButtonClickListener() {
|
||||
return new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (status == PlayerStatus.PLAYING) {
|
||||
playbackService.seekDelta(-UserPreferences.getSeekDeltaMs());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public OnClickListener newOnFFButtonClickListener() {
|
||||
return new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (status == PlayerStatus.PLAYING) {
|
||||
playbackService.seekDelta(UserPreferences.getSeekDeltaMs());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public boolean serviceAvailable() {
|
||||
return playbackService != null;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string-array name="seek_delta_values">
|
||||
<string-array name="smart_mark_as_played_values">
|
||||
<item>0</item>
|
||||
<item>15</item>
|
||||
<item>30</item>
|
||||
<item>45</item>
|
||||
<item>60</item>
|
||||
</string-array>
|
||||
|
||||
|
||||
<integer-array name="seek_delta_values">
|
||||
<item>5</item>
|
||||
<item>10</item>
|
||||
<item>15</item>
|
||||
|
@ -9,7 +18,7 @@
|
|||
<item>30</item>
|
||||
<item>45</item>
|
||||
<item>60</item>
|
||||
</string-array>
|
||||
</integer-array>
|
||||
|
||||
<string-array name="update_intervall_options">
|
||||
<item>Manual</item>
|
||||
|
|
|
@ -98,9 +98,9 @@
|
|||
<string name="stream_label">Stream</string>
|
||||
<string name="remove_label">Remove</string>
|
||||
<string name="remove_episode_lable">Remove episode</string>
|
||||
<string name="mark_read_label">Mark as read</string>
|
||||
<string name="mark_unread_label">Mark as unread</string>
|
||||
<string name="marked_as_read_label">Marked as read</string>
|
||||
<string name="mark_read_label">Mark as played</string>
|
||||
<string name="mark_unread_label">Mark as unplayed</string>
|
||||
<string name="marked_as_read_label">Marked as played</string>
|
||||
<string name="add_to_queue_label">Add to Queue</string>
|
||||
<string name="remove_from_queue_label">Remove from Queue</string>
|
||||
<string name="visit_website_label">Visit Website</string>
|
||||
|
@ -220,6 +220,8 @@
|
|||
<string name="pref_followQueue_sum">Jump to next queue item when playback completes</string>
|
||||
<string name="pref_auto_delete_sum">Delete episode when playback completes</string>
|
||||
<string name="pref_auto_delete_title">Auto Delete</string>
|
||||
<string name="pref_smart_mark_as_played_sum">Mark episodes as played even if less than a certain amount of seconds of playing time is still left</string>
|
||||
<string name="pref_smart_mark_as_played_title">Smart mark as played</string>
|
||||
<string name="playback_pref">Playback</string>
|
||||
<string name="network_pref">Network</string>
|
||||
<string name="pref_autoUpdateIntervall_title">Update interval</string>
|
||||
|
@ -266,8 +268,8 @@
|
|||
<string name="pref_gpodnet_setlogin_information_sum">Change the login information for your gpodder.net account.</string>
|
||||
<string name="pref_playback_speed_title">Playback Speeds</string>
|
||||
<string name="pref_playback_speed_sum">Customize the speeds available for variable speed audio playback</string>
|
||||
<string name="pref_seek_delta_title">Seek time</string>
|
||||
<string name="pref_seek_delta_sum">Seek this many seconds when rewinding or fast-forwarding</string>
|
||||
<string name="pref_fast_forward">Fast forward time</string>
|
||||
<string name="pref_rewind">Rewind time</string>
|
||||
<string name="pref_gpodnet_sethostname_title">Set hostname</string>
|
||||
<string name="pref_gpodnet_sethostname_use_default_host">Use default host</string>
|
||||
<string name="pref_expandNotify_title">Expand Notification</string>
|
||||
|
@ -277,6 +279,8 @@
|
|||
<string name="pref_expand_notify_unsupport_toast">Android versions before 4.1 do not support expanded notifications.</string>
|
||||
<string name="pref_queueAddToFront_sum">Add new episodes to the front of the queue.</string>
|
||||
<string name="pref_queueAddToFront_title">Enqueue at front.</string>
|
||||
<string name="pref_smart_mark_as_played_disabled">Disabled</string>
|
||||
|
||||
|
||||
<!-- Auto-Flattr dialog -->
|
||||
<string name="auto_flattr_enable">Enable automatic flattring</string>
|
||||
|
|
Loading…
Reference in New Issue