summaryrefslogtreecommitdiff
path: root/src/de/danoeh/antennapod
diff options
context:
space:
mode:
authordaniel oeh <daniel.oeh@gmail.com>2014-07-26 14:42:59 +0200
committerdaniel oeh <daniel.oeh@gmail.com>2014-07-26 14:42:59 +0200
commit583b2adaae5769fb8bee4d63e4ef10846d7de1b6 (patch)
tree2dfe8481d75fc7a68cee495c3e91bce0a4e5b0c0 /src/de/danoeh/antennapod
parent1673f6eaf83606a78ce3928b2d7c33d4ff0de862 (diff)
parent460e061d35e45268d3dcfebeba00e7231ce8cfd0 (diff)
downloadAntennaPod-583b2adaae5769fb8bee4d63e4ef10846d7de1b6.zip
Merge branch 'develop'0.9.9.2
Conflicts: submodules/dslv
Diffstat (limited to 'src/de/danoeh/antennapod')
-rw-r--r--src/de/danoeh/antennapod/AppConfig.java2
-rw-r--r--src/de/danoeh/antennapod/activity/AudioplayerActivity.java61
-rw-r--r--src/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java33
-rw-r--r--src/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java6
-rw-r--r--src/de/danoeh/antennapod/activity/MainActivity.java27
-rw-r--r--src/de/danoeh/antennapod/activity/MediaplayerActivity.java49
-rw-r--r--src/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java93
-rw-r--r--src/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java4
-rw-r--r--src/de/danoeh/antennapod/activity/PreferenceActivity.java28
-rw-r--r--src/de/danoeh/antennapod/activity/StorageErrorActivity.java5
-rw-r--r--src/de/danoeh/antennapod/activity/VideoplayerActivity.java14
-rw-r--r--src/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java6
-rw-r--r--src/de/danoeh/antennapod/adapter/ActionButtonUtils.java21
-rw-r--r--src/de/danoeh/antennapod/adapter/ChapterListAdapter.java2
-rw-r--r--src/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java44
-rw-r--r--src/de/danoeh/antennapod/adapter/DownloadLogAdapter.java2
-rw-r--r--src/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java2
-rw-r--r--src/de/danoeh/antennapod/adapter/DownloadlistAdapter.java2
-rw-r--r--src/de/danoeh/antennapod/adapter/ExternalEpisodesListAdapter.java4
-rw-r--r--src/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java2
-rw-r--r--src/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java4
-rw-r--r--src/de/danoeh/antennapod/adapter/NavListAdapter.java6
-rw-r--r--src/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java2
-rw-r--r--src/de/danoeh/antennapod/adapter/QueueListAdapter.java2
-rw-r--r--src/de/danoeh/antennapod/adapter/SearchlistAdapter.java2
-rw-r--r--src/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java2
-rw-r--r--src/de/danoeh/antennapod/asynctask/DownloadObserver.java11
-rw-r--r--src/de/danoeh/antennapod/asynctask/FlattrClickWorker.java376
-rw-r--r--src/de/danoeh/antennapod/asynctask/ImageDiskCache.java10
-rw-r--r--src/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java107
-rw-r--r--src/de/danoeh/antennapod/dialog/FeedItemDialog.java84
-rw-r--r--src/de/danoeh/antennapod/dialog/TimeDialog.java214
-rw-r--r--src/de/danoeh/antennapod/feed/EventDistributor.java8
-rw-r--r--src/de/danoeh/antennapod/feed/FeedMedia.java4
-rw-r--r--src/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java3
-rw-r--r--src/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java134
-rw-r--r--src/de/danoeh/antennapod/fragment/ItemlistFragment.java62
-rw-r--r--src/de/danoeh/antennapod/fragment/NewEpisodesFragment.java43
-rw-r--r--src/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java18
-rw-r--r--src/de/danoeh/antennapod/fragment/QueueFragment.java77
-rw-r--r--src/de/danoeh/antennapod/fragment/SearchFragment.java42
-rw-r--r--src/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java42
-rw-r--r--src/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java47
-rw-r--r--src/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java7
-rw-r--r--src/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java66
-rw-r--r--src/de/danoeh/antennapod/gpoddernet/GpodnetService.java104
-rw-r--r--src/de/danoeh/antennapod/gpoddernet/model/GpodnetDevice.java6
-rw-r--r--src/de/danoeh/antennapod/gpoddernet/model/GpodnetPodcast.java9
-rw-r--r--src/de/danoeh/antennapod/gpoddernet/model/GpodnetSubscriptionChange.java9
-rw-r--r--src/de/danoeh/antennapod/gpoddernet/model/GpodnetTag.java6
-rw-r--r--src/de/danoeh/antennapod/preferences/PlaybackPreferences.java7
-rw-r--r--src/de/danoeh/antennapod/preferences/UserPreferences.java1006
-rw-r--r--src/de/danoeh/antennapod/receiver/AlarmUpdateReceiver.java7
-rw-r--r--src/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java5
-rw-r--r--src/de/danoeh/antennapod/receiver/FeedUpdateReceiver.java5
-rw-r--r--src/de/danoeh/antennapod/receiver/PlayerWidget.java7
-rw-r--r--src/de/danoeh/antennapod/service/download/DownloadRequest.java23
-rw-r--r--src/de/danoeh/antennapod/service/download/DownloadService.java120
-rw-r--r--src/de/danoeh/antennapod/service/download/DownloadStatus.java11
-rw-r--r--src/de/danoeh/antennapod/service/playback/PlaybackService.java59
-rw-r--r--src/de/danoeh/antennapod/service/playback/PlaybackServiceMediaPlayer.java25
-rw-r--r--src/de/danoeh/antennapod/service/playback/PlaybackServiceTaskManager.java15
-rw-r--r--src/de/danoeh/antennapod/spa/SPAUtil.java5
-rw-r--r--src/de/danoeh/antennapod/storage/DBTasks.java20
-rw-r--r--src/de/danoeh/antennapod/storage/DBWriter.java4
-rw-r--r--src/de/danoeh/antennapod/storage/DownloadRequester.java6
-rw-r--r--src/de/danoeh/antennapod/storage/PodDBAdapter.java22
-rw-r--r--src/de/danoeh/antennapod/syndication/handler/TypeGetter.java18
-rw-r--r--src/de/danoeh/antennapod/syndication/handler/UnsupportedFeedtypeException.java19
-rw-r--r--src/de/danoeh/antennapod/util/Converter.java21
-rw-r--r--src/de/danoeh/antennapod/util/DuckType.java4
-rw-r--r--src/de/danoeh/antennapod/util/URLChecker.java7
-rw-r--r--src/de/danoeh/antennapod/util/menuhandler/FeedItemMenuHandler.java20
-rw-r--r--src/de/danoeh/antennapod/util/menuhandler/MenuItemUtils.java11
-rw-r--r--src/de/danoeh/antennapod/util/menuhandler/NavDrawerActivity.java9
-rw-r--r--src/de/danoeh/antennapod/util/playback/Playable.java6
-rw-r--r--src/de/danoeh/antennapod/util/playback/PlaybackController.java22
-rw-r--r--src/de/danoeh/antennapod/util/playback/Timeline.java161
-rw-r--r--src/de/danoeh/antennapod/util/syndication/FeedDiscoverer.java78
79 files changed, 2260 insertions, 1377 deletions
diff --git a/src/de/danoeh/antennapod/AppConfig.java b/src/de/danoeh/antennapod/AppConfig.java
index cac946f84..0e12a350f 100644
--- a/src/de/danoeh/antennapod/AppConfig.java
+++ b/src/de/danoeh/antennapod/AppConfig.java
@@ -2,6 +2,6 @@ package de.danoeh.antennapod;
public final class AppConfig {
/** Should be used when setting User-Agent header for HTTP-requests. */
- public final static String USER_AGENT = "AntennaPod/0.9.9.1";
+ public final static String USER_AGENT = "AntennaPod/0.9.9.2";
}
diff --git a/src/de/danoeh/antennapod/activity/AudioplayerActivity.java b/src/de/danoeh/antennapod/activity/AudioplayerActivity.java
index 090c3f1f5..6373ff240 100644
--- a/src/de/danoeh/antennapod/activity/AudioplayerActivity.java
+++ b/src/de/danoeh/antennapod/activity/AudioplayerActivity.java
@@ -12,32 +12,48 @@ import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.ListFragment;
import android.support.v4.widget.DrawerLayout;
import android.util.Log;
+import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
-import android.view.Window;
-import android.widget.*;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ImageButton;
import android.widget.ImageView.ScaleType;
+import android.widget.ListView;
+import android.widget.TextView;
+
+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;
import de.danoeh.antennapod.asynctask.ImageLoader;
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
-import de.danoeh.antennapod.feed.*;
+import de.danoeh.antennapod.feed.Chapter;
+import de.danoeh.antennapod.feed.EventDistributor;
+import de.danoeh.antennapod.feed.Feed;
+import de.danoeh.antennapod.feed.MediaType;
+import de.danoeh.antennapod.feed.SimpleChapter;
import de.danoeh.antennapod.fragment.CoverFragment;
import de.danoeh.antennapod.fragment.ItemDescriptionFragment;
import de.danoeh.antennapod.preferences.UserPreferences;
import de.danoeh.antennapod.service.playback.PlaybackService;
import de.danoeh.antennapod.storage.DBReader;
+import de.danoeh.antennapod.util.menuhandler.MenuItemUtils;
+import de.danoeh.antennapod.util.menuhandler.NavDrawerActivity;
import de.danoeh.antennapod.util.playback.ExternalMedia;
import de.danoeh.antennapod.util.playback.Playable;
+import de.danoeh.antennapod.util.playback.PlaybackController;
/**
* Activity for playing audio files.
*/
-public class AudioplayerActivity extends MediaplayerActivity {
+public class AudioplayerActivity extends MediaplayerActivity implements ItemDescriptionFragment.ItemDescriptionFragmentCallback,
+ NavDrawerActivity {
private static final int POS_COVER = 0;
private static final int POS_DESCR = 1;
private static final int POS_CHAPTERS = 2;
@@ -123,7 +139,6 @@ public class AudioplayerActivity extends MediaplayerActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
- requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
super.onCreate(savedInstanceState);
getSupportActionBar().setDisplayShowTitleEnabled(false);
detachedFragments = new Fragment[NUM_CONTENT_FRAGMENTS];
@@ -215,8 +230,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
@Override
protected void onResume() {
super.onResume();
- if (getIntent().getAction() != null
- && getIntent().getAction().equals(Intent.ACTION_VIEW)) {
+ if (StringUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) {
Intent intent = getIntent();
if (BuildConfig.DEBUG)
Log.d(TAG, "Received VIEW intent: "
@@ -293,7 +307,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
case POS_DESCR:
if (descriptionFragment == null) {
descriptionFragment = ItemDescriptionFragment
- .newInstance(media, true);
+ .newInstance(media, true, true);
}
currentlyShownFragment = descriptionFragment;
break;
@@ -427,6 +441,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
};
typedArray.recycle();
drawerToggle.setDrawerIndicatorEnabled(false);
+ drawerLayout.setDrawerListener(drawerToggle);
navAdapter = new NavListAdapter(itemAccess, this);
navList.setAdapter(navAdapter);
@@ -603,6 +618,34 @@ public class AudioplayerActivity extends MediaplayerActivity {
clearStatusMsg();
}
+ @Override
+ public PlaybackController getPlaybackController() {
+ return controller;
+ }
+
+ @Override
+ public boolean isDrawerOpen() {
+ return drawerLayout != null && navList != null && drawerLayout.isDrawerOpen(navList);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ if (!MenuItemUtils.isActivityDrawerOpen(this)) {
+ return super.onCreateOptionsMenu(menu);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ if (!MenuItemUtils.isActivityDrawerOpen(this)) {
+ return super.onPrepareOptionsMenu(menu);
+ } else {
+ return false;
+ }
+ }
+
public interface AudioplayerContentFragment {
public void onDataSetChanged(Playable media);
}
@@ -615,7 +658,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- if (drawerToggle.onOptionsItemSelected(item)) {
+ if (drawerToggle != null && drawerToggle.onOptionsItemSelected(item)) {
return true;
} else {
return super.onOptionsItemSelected(item);
diff --git a/src/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java b/src/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java
index 597189885..e89f8d05c 100644
--- a/src/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java
+++ b/src/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java
@@ -5,19 +5,26 @@ import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.NavUtils;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.*;
+import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.FeedItemlistDescriptionAdapter;
import de.danoeh.antennapod.asynctask.ImageDiskCache;
import de.danoeh.antennapod.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.feed.EventDistributor;
import de.danoeh.antennapod.feed.Feed;
+import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.storage.DBReader;
import de.danoeh.antennapod.storage.DownloadRequestException;
import de.danoeh.antennapod.storage.DownloadRequester;
+import org.apache.commons.lang3.StringUtils;
+import org.jsoup.Jsoup;
+import org.jsoup.examples.HtmlToPlainText;
+import org.jsoup.nodes.Document;
import java.util.ArrayList;
import java.util.Date;
@@ -25,9 +32,11 @@ import java.util.List;
import java.util.Map;
/**
- * Created by daniel on 24.08.13.
+ * Default implementation of OnlineFeedViewActivity. Shows the downloaded feed's items with their descriptions,
+ * a subscribe button and a spinner for choosing alternate feed URLs.
*/
public class DefaultOnlineFeedViewActivity extends OnlineFeedViewActivity {
+ private static final String TAG = "DefaultOnlineFeedViewActivity";
private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED | EventDistributor.DOWNLOAD_QUEUED | EventDistributor.FEED_LIST_UPDATE;
private volatile List<Feed> feeds;
@@ -64,6 +73,24 @@ public class DefaultOnlineFeedViewActivity extends OnlineFeedViewActivity {
}
@Override
+ protected void beforeShowFeedInformation(Feed feed, Map<String, String> alternateFeedUrls) {
+ super.beforeShowFeedInformation(feed, alternateFeedUrls);
+
+ // remove HTML tags from descriptions
+
+ if (BuildConfig.DEBUG) Log.d(TAG, "Removing HTML from shownotes");
+ if (feed.getItems() != null) {
+ HtmlToPlainText formatter = new HtmlToPlainText();
+ for (FeedItem item : feed.getItems()) {
+ if (item.getDescription() != null) {
+ Document description = Jsoup.parse(item.getDescription());
+ item.setDescription(StringUtils.trim(formatter.getPlainText(description)));
+ }
+ }
+ }
+ }
+
+ @Override
protected void showFeedInformation(final Feed feed, final Map<String, String> alternateFeedUrls) {
super.showFeedInformation(feed, alternateFeedUrls);
setContentView(R.layout.listview_activity);
@@ -74,7 +101,7 @@ public class DefaultOnlineFeedViewActivity extends OnlineFeedViewActivity {
ListView listView = (ListView) findViewById(R.id.listview);
LayoutInflater inflater = (LayoutInflater)
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- View header = inflater.inflate(R.layout.onlinefeedview_header, null);
+ View header = inflater.inflate(R.layout.onlinefeedview_header, listView, false);
listView.addHeaderView(header);
listView.setAdapter(new FeedItemlistDescriptionAdapter(this, 0, feed.getItems()));
@@ -131,7 +158,7 @@ public class DefaultOnlineFeedViewActivity extends OnlineFeedViewActivity {
for (String url : alternateFeedUrls.keySet()) {
alternateUrlsTitleList.add(alternateFeedUrls.get(url));
}
- ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, alternateUrlsTitleList);
+ ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, alternateUrlsTitleList);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spAlternateUrls.setAdapter(adapter);
spAlternateUrls.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
diff --git a/src/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java b/src/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java
index 4b8420e45..c5f25d813 100644
--- a/src/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java
+++ b/src/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java
@@ -9,6 +9,9 @@ import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
+
+import org.apache.commons.lang3.Validate;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.preferences.UserPreferences;
@@ -58,7 +61,8 @@ public class DownloadAuthenticationActivity extends ActionBarActivity {
butCancel = (Button) findViewById(R.id.butCancel);
txtvDescription = (TextView) findViewById(R.id.txtvDescription);
- if (!getIntent().hasExtra(ARG_DOWNLOAD_REQUEST)) throw new IllegalArgumentException("Download request missing");
+ Validate.isTrue(getIntent().hasExtra(ARG_DOWNLOAD_REQUEST), "Download request missing");
+
request = getIntent().getParcelableExtra(ARG_DOWNLOAD_REQUEST);
sendToDownloadRequester = getIntent().getBooleanExtra(ARG_SEND_TO_DOWNLOAD_REQUESTER_BOOL, false);
diff --git a/src/de/danoeh/antennapod/activity/MainActivity.java b/src/de/danoeh/antennapod/activity/MainActivity.java
index 257bea82d..b7014dab2 100644
--- a/src/de/danoeh/antennapod/activity/MainActivity.java
+++ b/src/de/danoeh/antennapod/activity/MainActivity.java
@@ -19,6 +19,9 @@ import android.util.Log;
import android.view.*;
import android.widget.AdapterView;
import android.widget.ListView;
+
+import org.apache.commons.lang3.Validate;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.NavListAdapter;
@@ -28,13 +31,14 @@ import de.danoeh.antennapod.fragment.*;
import de.danoeh.antennapod.preferences.UserPreferences;
import de.danoeh.antennapod.storage.DBReader;
import de.danoeh.antennapod.util.StorageUtils;
+import de.danoeh.antennapod.util.menuhandler.NavDrawerActivity;
import java.util.List;
/**
* The activity that is shown when the user launches the app.
*/
-public class MainActivity extends ActionBarActivity {
+public class MainActivity extends ActionBarActivity implements NavDrawerActivity{
private static final String TAG = "MainActivity";
private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED
| EventDistributor.DOWNLOAD_QUEUED
@@ -61,7 +65,7 @@ public class MainActivity extends ActionBarActivity {
private ListView navList;
private NavListAdapter navAdapter;
- private ActionBarDrawerToggle drawerToogle;
+ private ActionBarDrawerToggle drawerToggle;
private CharSequence drawerTitle;
private CharSequence currentTitle;
@@ -71,7 +75,7 @@ public class MainActivity extends ActionBarActivity {
public void onCreate(Bundle savedInstanceState) {
setTheme(UserPreferences.getTheme());
super.onCreate(savedInstanceState);
- requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+ supportRequestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
StorageUtils.checkStorageAvailability(this);
setContentView(R.layout.main);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
@@ -82,7 +86,7 @@ public class MainActivity extends ActionBarActivity {
navList = (ListView) findViewById(R.id.nav_list);
TypedArray typedArray = obtainStyledAttributes(new int[]{R.attr.nav_drawer_toggle});
- drawerToogle = new ActionBarDrawerToggle(this, drawerLayout, typedArray.getResourceId(0, 0), R.string.drawer_open, R.string.drawer_close) {
+ drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, typedArray.getResourceId(0, 0), R.string.drawer_open, R.string.drawer_close) {
@Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
@@ -101,7 +105,7 @@ public class MainActivity extends ActionBarActivity {
};
typedArray.recycle();
- drawerLayout.setDrawerListener(drawerToogle);
+ drawerLayout.setDrawerListener(drawerToggle);
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction transaction = fm.beginTransaction();
@@ -147,6 +151,10 @@ public class MainActivity extends ActionBarActivity {
return getSupportActionBar();
}
+ public boolean isDrawerOpen() {
+ return drawerLayout != null && navList != null && drawerLayout.isDrawerOpen(navList);
+ }
+
public List<Feed> getFeeds() {
return (navDrawerData != null) ? navDrawerData.feeds : null;
}
@@ -219,7 +227,7 @@ public class MainActivity extends ActionBarActivity {
}
public void loadChildFragment(Fragment fragment) {
- if (fragment == null) throw new IllegalArgumentException("fragment = null");
+ Validate.notNull(fragment);
FragmentManager fm = getSupportFragmentManager();
fm.beginTransaction()
.replace(R.id.main_view, fragment, "main")
@@ -244,7 +252,7 @@ public class MainActivity extends ActionBarActivity {
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
- drawerToogle.syncState();
+ drawerToggle.syncState();
if (savedInstanceState != null) {
currentTitle = savedInstanceState.getString("title");
if (!drawerLayout.isDrawerOpen(navList)) {
@@ -257,7 +265,7 @@ public class MainActivity extends ActionBarActivity {
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- drawerToogle.onConfigurationChanged(newConfig);
+ drawerToggle.onConfigurationChanged(newConfig);
}
@Override
@@ -296,7 +304,7 @@ public class MainActivity extends ActionBarActivity {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- if (drawerToogle.onOptionsItemSelected(item)) {
+ if (drawerToggle.onOptionsItemSelected(item)) {
return true;
}
switch (item.getItemId()) {
@@ -311,7 +319,6 @@ public class MainActivity extends ActionBarActivity {
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
-
return true;
}
diff --git a/src/de/danoeh/antennapod/activity/MediaplayerActivity.java b/src/de/danoeh/antennapod/activity/MediaplayerActivity.java
index fc70f4c05..13e7b8a82 100644
--- a/src/de/danoeh/antennapod/activity/MediaplayerActivity.java
+++ b/src/de/danoeh/antennapod/activity/MediaplayerActivity.java
@@ -3,19 +3,25 @@ package de.danoeh.antennapod.activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.res.TypedArray;
import android.graphics.PixelFormat;
import android.media.AudioManager;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
+import android.view.Window;
import android.widget.ImageButton;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
+import com.doomonafireball.betterpickers.hmspicker.HmsPickerBuilder;
+import com.doomonafireball.betterpickers.hmspicker.HmsPickerDialogFragment;
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.dialog.TimeDialog;
@@ -30,6 +36,7 @@ import de.danoeh.antennapod.util.StorageUtils;
import de.danoeh.antennapod.util.playback.MediaPlayerError;
import de.danoeh.antennapod.util.playback.Playable;
import de.danoeh.antennapod.util.playback.PlaybackController;
+import org.shredzone.flattr4j.model.User;
/**
* Provides general features which are both needed for playing audio and video
@@ -164,6 +171,10 @@ public abstract class MediaplayerActivity extends ActionBarActivity
protected void onCreate(Bundle savedInstanceState) {
chooseTheme();
super.onCreate(savedInstanceState);
+
+ // subclasses might use this feature
+ supportRequestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+
if (BuildConfig.DEBUG)
Log.d(TAG, "Creating Activity");
StorageUtils.checkStorageAvailability(this);
@@ -312,16 +323,34 @@ public abstract class MediaplayerActivity extends ActionBarActivity
break;
case R.id.set_sleeptimer_item:
if (controller.serviceAvailable()) {
- TimeDialog td = new TimeDialog(this,
- R.string.set_sleeptimer_label,
- R.string.set_sleeptimer_label) {
-
- @Override
- public void onTimeEntered(long millis) {
- controller.setSleepTimer(millis);
- }
- };
- td.show();
+ int pickerStyle = (UserPreferences.getTheme() == R.style.Theme_AntennaPod_Light) ?
+ R.style.AntennaPodBetterPickerThemeLight : R.style.AntennaPodBetterPickerThemeDark;
+ if (Build.VERSION.SDK_INT > 10) { // TODO remove this as soon as dialog is shown correctly on 2.3
+ HmsPickerBuilder hpb = new HmsPickerBuilder()
+ .setStyleResId(pickerStyle)
+ .setFragmentManager(getSupportFragmentManager());
+
+ hpb.addHmsPickerDialogHandler(new HmsPickerDialogFragment.HmsPickerDialogHandler() {
+ @Override
+ public void onDialogHmsSet(int ref, int hours, int minutes, int seconds) {
+ if (controller != null && controller.serviceAvailable()) {
+ controller.setSleepTimer((hours * 3600 + minutes * 60 + seconds) * 1000);
+ }
+ }
+ });
+ hpb.show();
+ } else {
+ TimeDialog td = new TimeDialog(this,
+ R.string.set_sleeptimer_label,
+ R.string.set_sleeptimer_label) {
+
+ @Override
+ public void onTimeEntered(long millis) {
+ controller.setSleepTimer(millis);
+ }
+ };
+ td.show();
+ }
break;
}
diff --git a/src/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java b/src/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
index e397ff2ca..2c6d75cd8 100644
--- a/src/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
+++ b/src/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
@@ -9,6 +9,7 @@ import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
+import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
@@ -29,13 +30,16 @@ import de.danoeh.antennapod.util.DownloadError;
import de.danoeh.antennapod.util.FileNameGenerator;
import de.danoeh.antennapod.util.StorageUtils;
import de.danoeh.antennapod.util.URLChecker;
+import de.danoeh.antennapod.util.syndication.FeedDiscoverer;
import org.apache.commons.lang3.StringUtils;
import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.Date;
+import java.util.List;
import java.util.Map;
/**
@@ -127,6 +131,13 @@ public abstract class OnlineFeedViewActivity extends ActionBarActivity {
}
}
+ private void resetIntent(String url, String title) {
+ Intent intent = new Intent();
+ intent.putExtra(ARG_FEEDURL, url);
+ intent.putExtra(ARG_TITLE, title);
+ setIntent(intent);
+ }
+
private void onDownloadCompleted(final Downloader downloader) {
runOnUiThread(new Runnable() {
@@ -244,8 +255,15 @@ public abstract class OnlineFeedViewActivity extends ActionBarActivity {
e.printStackTrace();
reasonDetailed = e.getMessage();
} catch (UnsupportedFeedtypeException e) {
- e.printStackTrace();
- reasonDetailed = e.getMessage();
+ if (BuildConfig.DEBUG) Log.d(TAG, "Unsupported feed type detected");
+ if (StringUtils.equalsIgnoreCase("html", e.getRootElement())) {
+ if (showFeedDiscoveryDialog(new File(feed.getFile_url()), feed.getDownload_url())) {
+ return;
+ }
+ } else {
+ e.printStackTrace();
+ reasonDetailed = e.getMessage();
+ }
} finally {
boolean rc = new File(feed.getFile_url()).delete();
if (BuildConfig.DEBUG)
@@ -253,6 +271,7 @@ public abstract class OnlineFeedViewActivity extends ActionBarActivity {
}
if (successful) {
+ beforeShowFeedInformation(feed, alternateFeedUrls);
runOnUiThread(new Runnable() {
@Override
public void run() {
@@ -285,7 +304,16 @@ public abstract class OnlineFeedViewActivity extends ActionBarActivity {
}
/**
- * Called when feed parsed successfully
+ * Called after the feed has been downloaded and parsed and before showFeedInformation is called.
+ * This method is executed on a background thread
+ */
+ protected void beforeShowFeedInformation(Feed feed, Map<String, String> alternateFeedUrls) {
+
+ }
+
+ /**
+ * Called when feed parsed successfully.
+ * This method is executed on the GUI thread.
*/
protected void showFeedInformation(Feed feed, Map<String, String> alternateFeedUrls) {
@@ -316,9 +344,67 @@ public abstract class OnlineFeedViewActivity extends ActionBarActivity {
finish();
}
});
+ builder.show();
}
}
+ private boolean showFeedDiscoveryDialog(File feedFile, String baseUrl) {
+ FeedDiscoverer fd = new FeedDiscoverer();
+ final Map<String, String> urlsMap;
+ try {
+ urlsMap = fd.findLinks(feedFile, baseUrl);
+ if (urlsMap == null || urlsMap.isEmpty()) {
+ return false;
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ }
+
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if (isPaused || isFinishing()) {
+ return;
+ }
+
+ final List<String> titles = new ArrayList<String>();
+ final List<String> urls = new ArrayList<String>();
+
+ urls.addAll(urlsMap.keySet());
+ for (String url : urls) {
+ titles.add(urlsMap.get(url));
+ }
+
+ final ArrayAdapter<String> adapter = new ArrayAdapter<String>(OnlineFeedViewActivity.this, R.layout.ellipsize_start_listitem, R.id.txtvTitle, titles);
+ DialogInterface.OnClickListener onClickListener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ String selectedUrl = urls.get(which);
+ dialog.dismiss();
+ resetIntent(selectedUrl, titles.get(which));
+ startFeedDownload(selectedUrl, null, null);
+ }
+ };
+
+ AlertDialog.Builder ab = new AlertDialog.Builder(OnlineFeedViewActivity.this)
+ .setTitle(R.string.feeds_label)
+ .setCancelable(true)
+ .setOnCancelListener(new OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ finish();
+ }
+ })
+ .setAdapter(adapter, onClickListener);
+ ab.show();
+ }
+ });
+
+
+ return true;
+ }
+
private class FeedViewAuthenticationDialog extends AuthenticationDialog {
private String feedUrl;
@@ -339,5 +425,4 @@ public abstract class OnlineFeedViewActivity extends ActionBarActivity {
startFeedDownload(feedUrl, username, password);
}
}
-
}
diff --git a/src/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java b/src/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java
index 5f2ea0401..e09941abf 100644
--- a/src/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java
+++ b/src/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java
@@ -103,11 +103,11 @@ public class OpmlFeedChooserActivity extends ActionBarActivity {
super.onCreateOptionsMenu(menu);
MenuItemCompat.setShowAsAction(menu.add(Menu.NONE, R.id.select_all_item, Menu.NONE,
R.string.select_all_label),
- MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
MenuItemCompat.setShowAsAction(menu.add(Menu.NONE, R.id.deselect_all_item, Menu.NONE,
R.string.deselect_all_label),
- MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
return true;
}
diff --git a/src/de/danoeh/antennapod/activity/PreferenceActivity.java b/src/de/danoeh/antennapod/activity/PreferenceActivity.java
index 77ec579ed..a62ad3ae9 100644
--- a/src/de/danoeh/antennapod/activity/PreferenceActivity.java
+++ b/src/de/danoeh/antennapod/activity/PreferenceActivity.java
@@ -15,7 +15,6 @@ import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceScreen;
-import android.support.v4.app.NavUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
@@ -25,6 +24,7 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.asynctask.FlattrClickWorker;
import de.danoeh.antennapod.asynctask.OpmlExportWorker;
import de.danoeh.antennapod.dialog.AuthenticationDialog;
+import de.danoeh.antennapod.dialog.AutoFlattrPreferenceDialog;
import de.danoeh.antennapod.dialog.GpodnetSetHostnameDialog;
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
import de.danoeh.antennapod.preferences.GpodnetPreferences;
@@ -47,7 +47,7 @@ public class PreferenceActivity extends android.preference.PreferenceActivity {
private static final String PREF_FLATTR_THIS_APP = "prefFlattrThisApp";
private static final String PREF_FLATTR_AUTH = "pref_flattr_authenticate";
private static final String PREF_FLATTR_REVOKE = "prefRevokeAccess";
- private static final String PREF_AUTO_FLATTR = "pref_auto_flattr";
+ private static final String PREF_AUTO_FLATTR_PREFS = "prefAutoFlattrPrefs";
private static final String PREF_OPML_EXPORT = "prefOpmlExport";
private static final String PREF_ABOUT = "prefAbout";
private static final String PREF_CHOOSE_DATA_DIR = "prefChooseDataDir";
@@ -69,7 +69,7 @@ public class PreferenceActivity extends android.preference.PreferenceActivity {
super.onCreate(savedInstanceState);
if (android.os.Build.VERSION.SDK_INT >= 11) {
- ActionBar ab = getActionBar();
+ @SuppressLint("AppCompatMethod") ActionBar ab = getActionBar();
if (ab != null) {
ab.setDisplayHomeAsUpEnabled(true);
}
@@ -247,6 +247,26 @@ public class PreferenceActivity extends android.preference.PreferenceActivity {
return true;
}
});
+
+ findPreference(PREF_AUTO_FLATTR_PREFS).setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ AutoFlattrPreferenceDialog.newAutoFlattrPreferenceDialog(PreferenceActivity.this,
+ new AutoFlattrPreferenceDialog.AutoFlattrPreferenceDialogInterface() {
+ @Override
+ public void onCancelled() {
+
+ }
+
+ @Override
+ public void onConfirmed(boolean autoFlattrEnabled, float autoFlattrValue) {
+ UserPreferences.setAutoFlattrSettings(PreferenceActivity.this, autoFlattrEnabled, autoFlattrValue);
+ checkItemVisibility();
+ }
+ });
+ return true;
+ }
+ });
buildUpdateIntervalPreference();
buildAutodownloadSelectedNetworsPreference();
setSelectedNetworksEnabled(UserPreferences
@@ -314,7 +334,7 @@ public class PreferenceActivity extends android.preference.PreferenceActivity {
findPreference(PREF_FLATTR_AUTH).setEnabled(!hasFlattrToken);
findPreference(PREF_FLATTR_REVOKE).setEnabled(hasFlattrToken);
- findPreference(PREF_AUTO_FLATTR).setEnabled(hasFlattrToken);
+ findPreference(PREF_AUTO_FLATTR_PREFS).setEnabled(hasFlattrToken);
findPreference(UserPreferences.PREF_ENABLE_AUTODL_WIFI_FILTER)
.setEnabled(UserPreferences.isEnableAutodownload());
diff --git a/src/de/danoeh/antennapod/activity/StorageErrorActivity.java b/src/de/danoeh/antennapod/activity/StorageErrorActivity.java
index 2cd56ba8b..d8a137eb9 100644
--- a/src/de/danoeh/antennapod/activity/StorageErrorActivity.java
+++ b/src/de/danoeh/antennapod/activity/StorageErrorActivity.java
@@ -7,6 +7,9 @@ import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
+
+import org.apache.commons.lang3.StringUtils;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.preferences.UserPreferences;
@@ -54,7 +57,7 @@ public class StorageErrorActivity extends ActionBarActivity {
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(Intent.ACTION_MEDIA_MOUNTED)) {
+ if (StringUtils.equals(intent.getAction(), Intent.ACTION_MEDIA_MOUNTED)) {
if (intent.getBooleanExtra("read-only", true)) {
if (BuildConfig.DEBUG)
Log.d(TAG, "Media was mounted; Finishing activity");
diff --git a/src/de/danoeh/antennapod/activity/VideoplayerActivity.java b/src/de/danoeh/antennapod/activity/VideoplayerActivity.java
index c45a3d162..46fa98c49 100644
--- a/src/de/danoeh/antennapod/activity/VideoplayerActivity.java
+++ b/src/de/danoeh/antennapod/activity/VideoplayerActivity.java
@@ -6,14 +6,19 @@ import android.graphics.drawable.ColorDrawable;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
+import android.support.v4.view.WindowCompat;
import android.util.Log;
import android.util.Pair;
-import android.view.*;
+import android.view.MotionEvent;
+import android.view.SurfaceHolder;
+import android.view.View;
+import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.SeekBar;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.feed.MediaType;
@@ -48,7 +53,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
if (Build.VERSION.SDK_INT >= 11) {
- requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
+ supportRequestWindowFeature(WindowCompat.FEATURE_ACTION_BAR_OVERLAY);
}
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
super.onCreate(savedInstanceState);
@@ -126,7 +131,8 @@ public class VideoplayerActivity extends MediaplayerActivity {
Pair<Integer, Integer> videoSize = controller.getVideoSize();
if (videoSize != null && videoSize.first > 0 && videoSize.second > 0) {
- if (BuildConfig.DEBUG) Log.d(TAG, "Width,height of video: " + videoSize.first + ", " + videoSize.second);
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Width,height of video: " + videoSize.first + ", " + videoSize.second);
videoview.setVideoSize(videoSize.first, videoSize.second);
} else {
Log.e(TAG, "Could not determine video size");
@@ -304,6 +310,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
progressIndicator.setVisibility(View.INVISIBLE);
}
+ @SuppressLint("NewApi")
private void showVideoControls() {
videoOverlay.setVisibility(View.VISIBLE);
butPlay.setVisibility(View.VISIBLE);
@@ -318,6 +325,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
}
}
+ @SuppressLint("NewApi")
private void hideVideoControls() {
final Animation animation = AnimationUtils.loadAnimation(this,
R.anim.fade_out);
diff --git a/src/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java b/src/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java
index 05048f079..cb6dc41cf 100644
--- a/src/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java
+++ b/src/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java
@@ -67,9 +67,9 @@ public class GpodnetAuthenticationActivity extends ActionBarActivity {
LayoutInflater inflater = (LayoutInflater)
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
views = new View[]{
- inflater.inflate(R.layout.gpodnetauth_credentials, null),
- inflater.inflate(R.layout.gpodnetauth_device, null),
- inflater.inflate(R.layout.gpodnetauth_finish, null)
+ inflater.inflate(R.layout.gpodnetauth_credentials, viewFlipper, false),
+ inflater.inflate(R.layout.gpodnetauth_device, viewFlipper, false),
+ inflater.inflate(R.layout.gpodnetauth_finish, viewFlipper, false)
};
for (View view : views) {
viewFlipper.addView(view);
diff --git a/src/de/danoeh/antennapod/adapter/ActionButtonUtils.java b/src/de/danoeh/antennapod/adapter/ActionButtonUtils.java
index 17c61a86c..1de071a73 100644
--- a/src/de/danoeh/antennapod/adapter/ActionButtonUtils.java
+++ b/src/de/danoeh/antennapod/adapter/ActionButtonUtils.java
@@ -4,6 +4,9 @@ import android.content.Context;
import android.content.res.TypedArray;
import android.view.View;
import android.widget.ImageButton;
+
+import org.apache.commons.lang3.Validate;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.feed.FeedMedia;
@@ -20,11 +23,12 @@ public class ActionButtonUtils {
private final Context context;
public ActionButtonUtils(Context context) {
- if (context == null) throw new IllegalArgumentException("context = null");
+ Validate.notNull(context);
+
this.context = context;
drawables = context.obtainStyledAttributes(new int[]{
- R.attr.av_play, R.attr.navigation_cancel, R.attr.av_download, R.attr.navigation_chapters});
- labels = new int[]{R.string.play_label, R.string.cancel_download_label, R.string.download_label};
+ R.attr.av_play, R.attr.navigation_cancel, R.attr.av_download, R.attr.navigation_chapters, R.attr.navigation_accept});
+ labels = new int[]{R.string.play_label, R.string.cancel_download_label, R.string.download_label, R.string.mark_read_label};
}
/**
@@ -32,7 +36,8 @@ public class ActionButtonUtils {
* action button so that it matches the state of the FeedItem.
*/
public void configureActionButton(ImageButton butSecondary, FeedItem item) {
- if (butSecondary == null || item == null) throw new IllegalArgumentException("butSecondary or item was null");
+ Validate.isTrue(butSecondary != null && item != null, "butSecondary or item was null");
+
final FeedMedia media = item.getMedia();
if (media != null) {
final boolean isDownloadingMedia = DownloadRequester.getInstance().isDownloadingFile(media);
@@ -61,7 +66,13 @@ public class ActionButtonUtils {
butSecondary.setContentDescription(context.getString(labels[0]));
}
} else {
- butSecondary.setVisibility(View.INVISIBLE);
+ if (item.isRead()) {
+ butSecondary.setVisibility(View.INVISIBLE);
+ } else {
+ butSecondary.setVisibility(View.VISIBLE);
+ butSecondary.setImageDrawable(drawables.getDrawable(4));
+ butSecondary.setContentDescription(context.getString(labels[3]));
+ }
}
}
}
diff --git a/src/de/danoeh/antennapod/adapter/ChapterListAdapter.java b/src/de/danoeh/antennapod/adapter/ChapterListAdapter.java
index 72ad6774f..c12de6ebd 100644
--- a/src/de/danoeh/antennapod/adapter/ChapterListAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/ChapterListAdapter.java
@@ -51,7 +51,7 @@ public class ChapterListAdapter extends ArrayAdapter<Chapter> {
LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- convertView = inflater.inflate(R.layout.simplechapter_item, null);
+ convertView = inflater.inflate(R.layout.simplechapter_item, parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
defaultTextColor = holder.title.getTextColors().getDefaultColor();
holder.start = (TextView) convertView.findViewById(R.id.txtvStart);
diff --git a/src/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java b/src/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
index 3acd587af..0c4cbe685 100644
--- a/src/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
+++ b/src/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
@@ -2,11 +2,15 @@ package de.danoeh.antennapod.adapter;
import android.content.Context;
import android.widget.Toast;
+
+import org.apache.commons.lang3.Validate;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.feed.FeedMedia;
import de.danoeh.antennapod.storage.DBTasks;
+import de.danoeh.antennapod.storage.DBWriter;
import de.danoeh.antennapod.storage.DownloadRequestException;
import de.danoeh.antennapod.storage.DownloadRequester;
@@ -19,31 +23,35 @@ public class DefaultActionButtonCallback implements ActionButtonCallback {
private final Context context;
public DefaultActionButtonCallback(Context context) {
- if (context == null) throw new IllegalArgumentException("context = null");
+ Validate.notNull(context);
this.context = context;
}
@Override
public void onActionButtonPressed(final FeedItem item) {
- final FeedMedia media = item.getMedia();
- if (media == null) {
- return;
- }
- boolean isDownloading = DownloadRequester.getInstance().isDownloadingFile(media);
- if (!isDownloading && !media.isDownloaded()) {
- try {
- DBTasks.downloadFeedItems(context, item);
- Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show();
- } catch (DownloadRequestException e) {
- e.printStackTrace();
- DownloadRequestErrorDialogCreator.newRequestErrorDialog(context, e.getMessage());
+
+ if (item.hasMedia()) {
+ final FeedMedia media = item.getMedia();
+ boolean isDownloading = DownloadRequester.getInstance().isDownloadingFile(media);
+ if (!isDownloading && !media.isDownloaded()) {
+ try {
+ DBTasks.downloadFeedItems(context, item);
+ Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show();
+ } catch (DownloadRequestException e) {
+ e.printStackTrace();
+ DownloadRequestErrorDialogCreator.newRequestErrorDialog(context, e.getMessage());
+ }
+ } else if (isDownloading) {
+ DownloadRequester.getInstance().cancelDownload(context, media);
+ Toast.makeText(context, R.string.download_cancelled_msg, Toast.LENGTH_SHORT).show();
+ } else { // media is downloaded
+ DBTasks.playMedia(context, media, true, true, false);
+ }
+ } else {
+ if (!item.isRead()) {
+ DBWriter.markItemRead(context, item, true, true);
}
- } else if (isDownloading) {
- DownloadRequester.getInstance().cancelDownload(context, media);
- Toast.makeText(context, R.string.download_cancelled_msg, Toast.LENGTH_SHORT).show();
- } else { // media is downloaded
- DBTasks.playMedia(context, media, true, true, false);
}
}
}
diff --git a/src/de/danoeh/antennapod/adapter/DownloadLogAdapter.java b/src/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
index e97d69494..2cc216227 100644
--- a/src/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
@@ -34,7 +34,7 @@ public class DownloadLogAdapter extends BaseAdapter {
holder = new Holder();
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- convertView = inflater.inflate(R.layout.downloadlog_item, null);
+ convertView = inflater.inflate(R.layout.downloadlog_item, parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.type = (TextView) convertView.findViewById(R.id.txtvType);
holder.date = (TextView) convertView.findViewById(R.id.txtvDate);
diff --git a/src/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java b/src/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
index 0bf770df2..33b11774f 100644
--- a/src/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
@@ -54,7 +54,7 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.downloaded_episodeslist_item,
- null);
+ parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.pubDate = (TextView) convertView
.findViewById(R.id.txtvPublished);
diff --git a/src/de/danoeh/antennapod/adapter/DownloadlistAdapter.java b/src/de/danoeh/antennapod/adapter/DownloadlistAdapter.java
index fa2e5a0a7..658af9e4e 100644
--- a/src/de/danoeh/antennapod/adapter/DownloadlistAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/DownloadlistAdapter.java
@@ -56,7 +56,7 @@ public class DownloadlistAdapter extends BaseAdapter {
holder = new Holder();
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- convertView = inflater.inflate(R.layout.downloadlist_item, null);
+ convertView = inflater.inflate(R.layout.downloadlist_item, parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.message = (TextView) convertView
.findViewById(R.id.txtvMessage);
diff --git a/src/de/danoeh/antennapod/adapter/ExternalEpisodesListAdapter.java b/src/de/danoeh/antennapod/adapter/ExternalEpisodesListAdapter.java
index 9a7b607aa..5e857c131 100644
--- a/src/de/danoeh/antennapod/adapter/ExternalEpisodesListAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/ExternalEpisodesListAdapter.java
@@ -72,7 +72,7 @@ public class ExternalEpisodesListAdapter extends BaseExpandableListAdapter {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.external_itemlist_item,
- null);
+ parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.feedTitle = (TextView) convertView
.findViewById(R.id.txtvFeedname);
@@ -225,7 +225,7 @@ public class ExternalEpisodesListAdapter extends BaseExpandableListAdapter {
View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- convertView = inflater.inflate(R.layout.feeditemlist_header, null);
+ convertView = inflater.inflate(R.layout.feeditemlist_header, parent, false);
TextView headerTitle = (TextView) convertView
.findViewById(0);
ImageButton actionButton = (ImageButton) convertView
diff --git a/src/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java b/src/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
index c4a16d4db..357b5f8b4 100644
--- a/src/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
@@ -67,7 +67,7 @@ public class FeedItemlistAdapter extends BaseAdapter {
holder = new Holder();
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- convertView = inflater.inflate(R.layout.feeditemlist_item, null);
+ convertView = inflater.inflate(R.layout.feeditemlist_item, parent, false);
holder.title = (TextView) convertView
.findViewById(R.id.txtvItemname);
holder.lenSize = (TextView) convertView
diff --git a/src/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java b/src/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java
index 5fb204b26..c2c2285ac 100644
--- a/src/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java
@@ -12,7 +12,7 @@ import de.danoeh.antennapod.feed.FeedItem;
import java.util.List;
/**
- * Created by daniel on 24.08.13.
+ * List adapter for showing a list of FeedItems with their title and description.
*/
public class FeedItemlistDescriptionAdapter extends ArrayAdapter<FeedItem> {
@@ -31,7 +31,7 @@ public class FeedItemlistDescriptionAdapter extends ArrayAdapter<FeedItem> {
holder = new Holder();
LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- convertView = inflater.inflate(R.layout.itemdescription_listitem, null);
+ convertView = inflater.inflate(R.layout.itemdescription_listitem, parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.description = (TextView) convertView.findViewById(R.id.txtvDescription);
diff --git a/src/de/danoeh/antennapod/adapter/NavListAdapter.java b/src/de/danoeh/antennapod/adapter/NavListAdapter.java
index 536bf80e3..9676372fb 100644
--- a/src/de/danoeh/antennapod/adapter/NavListAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/NavListAdapter.java
@@ -110,7 +110,7 @@ public class NavListAdapter extends BaseAdapter {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- convertView = inflater.inflate(R.layout.nav_listitem, null);
+ convertView = inflater.inflate(R.layout.nav_listitem, parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.count = (TextView) convertView.findViewById(R.id.txtvCount);
@@ -154,7 +154,7 @@ public class NavListAdapter extends BaseAdapter {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- convertView = inflater.inflate(R.layout.nav_section_item, null);
+ convertView = inflater.inflate(R.layout.nav_section_item, parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
convertView.setTag(holder);
@@ -179,7 +179,7 @@ public class NavListAdapter extends BaseAdapter {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- convertView = inflater.inflate(R.layout.nav_feedlistitem, null);
+ convertView = inflater.inflate(R.layout.nav_feedlistitem, parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.image = (ImageView) convertView.findViewById(R.id.imgvCover);
diff --git a/src/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java b/src/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java
index 555a334f6..07fd3e6b1 100644
--- a/src/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java
@@ -62,7 +62,7 @@ public class NewEpisodesListAdapter extends BaseAdapter {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.new_episodes_listitem,
- null);
+ parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.pubDate = (TextView) convertView
.findViewById(R.id.txtvPublished);
diff --git a/src/de/danoeh/antennapod/adapter/QueueListAdapter.java b/src/de/danoeh/antennapod/adapter/QueueListAdapter.java
index ecce1b473..f671ba5c6 100644
--- a/src/de/danoeh/antennapod/adapter/QueueListAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/QueueListAdapter.java
@@ -57,7 +57,7 @@ public class QueueListAdapter extends BaseAdapter {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.queue_listitem,
- null);
+ parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.butSecondary = (ImageButton) convertView
.findViewById(R.id.butSecondaryAction);
diff --git a/src/de/danoeh/antennapod/adapter/SearchlistAdapter.java b/src/de/danoeh/antennapod/adapter/SearchlistAdapter.java
index 5c6af3943..ecfbb4660 100644
--- a/src/de/danoeh/antennapod/adapter/SearchlistAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/SearchlistAdapter.java
@@ -55,7 +55,7 @@ public class SearchlistAdapter extends BaseAdapter {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- convertView = inflater.inflate(R.layout.searchlist_item, null);
+ convertView = inflater.inflate(R.layout.searchlist_item, parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.cover = (ImageView) convertView
.findViewById(R.id.imgvFeedimage);
diff --git a/src/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java b/src/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java
index 795b17917..f20232a6f 100644
--- a/src/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java
+++ b/src/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java
@@ -38,7 +38,7 @@ public class PodcastListAdapter extends ArrayAdapter<GpodnetPodcast> {
LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- convertView = inflater.inflate(R.layout.gpodnet_podcast_listitem, null);
+ convertView = inflater.inflate(R.layout.gpodnet_podcast_listitem, parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.description = (TextView) convertView.findViewById(R.id.txtvDescription);
holder.image = (ImageView) convertView.findViewById(R.id.imgvCover);
diff --git a/src/de/danoeh/antennapod/asynctask/DownloadObserver.java b/src/de/danoeh/antennapod/asynctask/DownloadObserver.java
index 1c5003ab3..21ae5291e 100644
--- a/src/de/danoeh/antennapod/asynctask/DownloadObserver.java
+++ b/src/de/danoeh/antennapod/asynctask/DownloadObserver.java
@@ -5,6 +5,9 @@ import android.content.*;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
+
+import org.apache.commons.lang3.Validate;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.service.download.DownloadService;
import de.danoeh.antennapod.service.download.Downloader;
@@ -45,9 +48,9 @@ public class DownloadObserver {
* @throws java.lang.IllegalArgumentException if one of the arguments is null.
*/
public DownloadObserver(Activity activity, Handler handler, Callback callback) {
- if (activity == null) throw new IllegalArgumentException("activity = null");
- if (handler == null) throw new IllegalArgumentException("handler = null");
- if (callback == null) throw new IllegalArgumentException("callback = null");
+ Validate.notNull(activity);
+ Validate.notNull(handler);
+ Validate.notNull(callback);
this.activity = activity;
this.handler = handler;
@@ -166,7 +169,7 @@ public class DownloadObserver {
}
public void setActivity(Activity activity) {
- if (activity == null) throw new IllegalArgumentException("activity = null");
+ Validate.notNull(activity);
this.activity = activity;
}
diff --git a/src/de/danoeh/antennapod/asynctask/FlattrClickWorker.java b/src/de/danoeh/antennapod/asynctask/FlattrClickWorker.java
index e9aa79ac1..9210ac1d1 100644
--- a/src/de/danoeh/antennapod/asynctask/FlattrClickWorker.java
+++ b/src/de/danoeh/antennapod/asynctask/FlattrClickWorker.java
@@ -1,300 +1,236 @@
package de.danoeh.antennapod.asynctask;
-import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import android.widget.Toast;
+
+import org.apache.commons.lang3.Validate;
+import org.shredzone.flattr4j.exception.FlattrException;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.FlattrAuthActivity;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.storage.DBReader;
import de.danoeh.antennapod.storage.DBWriter;
+import de.danoeh.antennapod.util.NetworkUtils;
import de.danoeh.antennapod.util.flattr.FlattrThing;
import de.danoeh.antennapod.util.flattr.FlattrUtils;
-import java.util.ArrayList;
-import java.util.List;
-
/**
* Performs a click action in a background thread.
+ * <p/>
+ * When started, the flattr click worker will try to flattr every item that is in the flattr queue. If no network
+ * connection is available it will shut down immediately. The FlattrClickWorker can also be given one additional
+ * FlattrThing which will be flattrd immediately.
+ * <p/>
+ * The FlattrClickWorker will display a toast notification for every item that has been flattrd. If the FlattrClickWorker failed
+ * to flattr something, a notification will be displayed.
*/
-
-public class FlattrClickWorker extends AsyncTask<Void, String, Void> {
+public class FlattrClickWorker extends AsyncTask<Void, Integer, FlattrClickWorker.ExitCode> {
protected static final String TAG = "FlattrClickWorker";
- protected Context context;
-
- private final int NOTIFICATION_ID = 4;
- protected String errorMsg;
- protected int exitCode;
- protected ArrayList<String> flattrd;
- protected ArrayList<String> flattr_failed;
+ private static final int NOTIFICATION_ID = 4;
+ private final Context context;
- protected NotificationCompat.Builder notificationCompatBuilder;
- private Notification.BigTextStyle notificationBuilder;
- protected NotificationManager notificationManager;
+ public static enum ExitCode {EXIT_NORMAL, NO_TOKEN, NO_NETWORK, NO_THINGS}
- protected ProgressDialog progDialog;
+ private volatile int countFailed = 0;
+ private volatile int countSuccess = 0;
- protected final static int EXIT_DEFAULT = 0;
- protected final static int NO_TOKEN = 1;
- protected final static int ENQUEUED = 2;
- protected final static int NO_THINGS = 3;
-
- public final static int ENQUEUE_ONLY = 1;
- public final static int FLATTR_TOAST = 2;
- public static final int FLATTR_NOTIFICATION = 3;
-
- private int run_mode = FLATTR_NOTIFICATION;
-
- private FlattrThing extra_flattr_thing; // additional urls to flattr that do *not* originate from the queue
+ private volatile FlattrThing extraFlattrThing;
/**
- * @param context
- * @param run_mode can be one of ENQUEUE_ONLY, FLATTR_TOAST and FLATTR_NOTIFICATION
+ * Only relevant if just one thing is flattrd
*/
- public FlattrClickWorker(Context context, int run_mode) {
- this(context);
- this.run_mode = run_mode;
- }
+ private volatile FlattrException exception;
+ /**
+ * Creates a new FlattrClickWorker which will only flattr all things in the queue.
+ * <p/>
+ * The FlattrClickWorker has to be started by calling executeAsync().
+ *
+ * @param context A context for accessing the database and posting notifications. Must not be null.
+ */
public FlattrClickWorker(Context context) {
- super();
- this.context = context;
- exitCode = EXIT_DEFAULT;
-
- flattrd = new ArrayList<String>();
- flattr_failed = new ArrayList<String>();
-
- errorMsg = "";
+ Validate.notNull(context);
+ this.context = context.getApplicationContext();
}
- /* only used in PreferencesActivity for flattring antennapod itself,
- * can't really enqueue this thing
- */
- public FlattrClickWorker(Context context, FlattrThing thing) {
+ /**
+ * Creates a new FlattrClickWorker which will flattr all things in the queue and one additional
+ * FlattrThing.
+ * <p/>
+ * The FlattrClickWorker has to be started by calling executeAsync().
+ *
+ * @param context A context for accessing the database and posting notifications. Must not be null.
+ * @param extraFlattrThing The additional thing to flattr
+ */
+ public FlattrClickWorker(Context context, FlattrThing extraFlattrThing) {
this(context);
- extra_flattr_thing = thing;
- run_mode = FLATTR_TOAST;
- Log.d(TAG, "Going to flattr special thing that is not in the queue: " + thing.getTitle());
- }
-
- protected void onNoAccessToken() {
- Log.w(TAG, "No access token was available");
- }
-
- protected void onFlattrError() {
- FlattrUtils.showErrorDialog(context, errorMsg);
+ this.extraFlattrThing = extraFlattrThing;
}
- protected void onFlattred() {
- String notificationTitle = context.getString(R.string.flattrd_label);
- String notificationText = "", notificationSubText = "", notificationBigText = "";
-
- // text for successfully flattred items
- if (flattrd.size() == 1)
- notificationText = String.format(context.getString(R.string.flattr_click_success));
- else if (flattrd.size() > 1) // flattred pending items from queue
- notificationText = String.format(context.getString(R.string.flattr_click_success_count, flattrd.size()));
- if (flattrd.size() > 0) {
- String acc = "";
- for (String s : flattrd)
- acc += s + '\n';
- acc = acc.substring(0, acc.length() - 2);
+ @Override
+ protected ExitCode doInBackground(Void... params) {
- notificationBigText = String.format(context.getString(R.string.flattr_click_success_queue), acc);
+ if (!FlattrUtils.hasToken()) {
+ return ExitCode.NO_TOKEN;
}
- // add text for failures
- if (flattr_failed.size() > 0) {
- notificationTitle = context.getString(R.string.flattrd_failed_label);
- notificationText = String.format(context.getString(R.string.flattr_click_failure_count), flattr_failed.size())
- + " " + notificationText;
-
- notificationSubText = flattr_failed.get(0);
+ if (!NetworkUtils.networkAvailable(context)) {
+ return ExitCode.NO_NETWORK;
+ }
- String acc = "";
- for (String s : flattr_failed)
- acc += s + '\n';
- acc = acc.substring(0, acc.length() - 2);
+ final List<FlattrThing> flattrQueue = DBReader.getFlattrQueue(context);
+ if (extraFlattrThing != null) {
+ flattrQueue.add(extraFlattrThing);
+ } else if (flattrQueue.size() == 1) {
+ // if only one item is flattrd, the report can specifically mentioned that this item has failed
+ extraFlattrThing = flattrQueue.get(0);
+ }
- notificationBigText = String.format(context.getString(R.string.flattr_click_failure), acc)
- + "\n" + notificationBigText;
+ if (flattrQueue.isEmpty()) {
+ return ExitCode.NO_THINGS;
}
- Log.d(TAG, "Going to post notification: " + notificationBigText);
-
- notificationManager.cancel(NOTIFICATION_ID);
-
- if (run_mode == FLATTR_NOTIFICATION || flattr_failed.size() > 0) {
- PendingIntent contentIntent = PendingIntent.getActivity(context, 0, new Intent(context, MainActivity.class), 0);
- if (android.os.Build.VERSION.SDK_INT >= 16) {
- notificationBuilder = new Notification.BigTextStyle(
- new Notification.Builder(context)
- .setOngoing(false)
- .setContentTitle(notificationTitle)
- .setContentText(notificationText)
- .setContentIntent(contentIntent)
- .setSubText(notificationSubText)
- .setSmallIcon(R.drawable.stat_notify_sync))
- .bigText(notificationText + "\n" + notificationBigText);
- notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
- } else {
- notificationCompatBuilder = new NotificationCompat.Builder(context) // need new notificationBuilder and cancel/renotify to get rid of progress bar
- .setContentTitle(notificationTitle)
- .setContentText(notificationText)
- .setContentIntent(contentIntent)
- .setSubText(notificationBigText)
- .setTicker(notificationTitle)
- .setSmallIcon(R.drawable.stat_notify_sync)
- .setOngoing(false);
- notificationManager.notify(NOTIFICATION_ID, notificationCompatBuilder.build());
+ List<Future> dbFutures = new LinkedList<Future>();
+ for (FlattrThing thing : flattrQueue) {
+ if (BuildConfig.DEBUG) Log.d(TAG, "Processing " + thing.getTitle());
+
+ try {
+ thing.getFlattrStatus().setUnflattred(); // pop from queue to prevent unflattrable things from getting stuck in flattr queue infinitely
+ FlattrUtils.clickUrl(context, thing.getPaymentLink());
+ thing.getFlattrStatus().setFlattred();
+ publishProgress(R.string.flattr_click_success);
+ countSuccess++;
+
+ } catch (FlattrException e) {
+ e.printStackTrace();
+ countFailed++;
+ if (countFailed == 1) {
+ exception = e;
+ }
}
- } else if (run_mode == FLATTR_TOAST) {
- Toast.makeText(context.getApplicationContext(),
- notificationText,
- Toast.LENGTH_LONG)
- .show();
- }
- }
- protected void onEnqueue() {
- Toast.makeText(context.getApplicationContext(),
- R.string.flattr_click_enqueued,
- Toast.LENGTH_LONG)
- .show();
- }
+ Future<?> f = DBWriter.setFlattredStatus(context, thing, false);
+ if (f != null) {
+ dbFutures.add(f);
+ }
+ }
- protected void onSetupNotification() {
- if (android.os.Build.VERSION.SDK_INT >= 16) {
- notificationBuilder = new Notification.BigTextStyle(
- new Notification.Builder(context)
- .setContentTitle(context.getString(R.string.flattring_label))
- .setAutoCancel(true)
- .setSmallIcon(R.drawable.stat_notify_sync)
- .setProgress(0, 0, true)
- .setOngoing(true));
- } else {
- notificationCompatBuilder = new NotificationCompat.Builder(context)
- .setContentTitle(context.getString(R.string.flattring_label))
- .setAutoCancel(true)
- .setSmallIcon(R.drawable.stat_notify_sync)
- .setProgress(0, 0, true)
- .setOngoing(true);
+ for (Future f : dbFutures) {
+ try {
+ f.get();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ } catch (ExecutionException e) {
+ e.printStackTrace();
+ }
}
- notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ return ExitCode.EXIT_NORMAL;
}
@Override
- protected void onPostExecute(Void result) {
- if (BuildConfig.DEBUG) Log.d(TAG, "Exit code was " + exitCode);
-
+ protected void onPostExecute(ExitCode exitCode) {
+ super.onPostExecute(exitCode);
switch (exitCode) {
- case NO_TOKEN:
- notificationManager.cancel(NOTIFICATION_ID);
- onNoAccessToken();
+ case EXIT_NORMAL:
+ if (countFailed > 0) {
+ postFlattrFailedNotification();
+ }
break;
- case ENQUEUED:
- onEnqueue();
+ case NO_NETWORK:
+ postToastNotification(R.string.flattr_click_enqueued);
break;
- case EXIT_DEFAULT:
- onFlattred();
+ case NO_TOKEN:
+ postNoTokenNotification();
break;
- case NO_THINGS: // FlattrClickWorker called automatically somewhere to empty flattr queue
- notificationManager.cancel(NOTIFICATION_ID);
+ case NO_THINGS: // nothing to notify here
break;
}
}
@Override
- protected void onPreExecute() {
- onSetupNotification();
+ protected void onProgressUpdate(Integer... values) {
+ super.onProgressUpdate(values);
+ postToastNotification(values[0]);
}
- private static boolean haveInternetAccess(Context context) {
- ConnectivityManager cm =
- (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
-
- NetworkInfo networkInfo = cm.getActiveNetworkInfo();
- return (networkInfo != null && networkInfo.isConnectedOrConnecting());
+ private void postToastNotification(int msg) {
+ Toast.makeText(context, context.getString(msg), Toast.LENGTH_LONG).show();
}
- @Override
- protected Void doInBackground(Void... params) {
- if (BuildConfig.DEBUG) Log.d(TAG, "Starting background work");
-
- exitCode = EXIT_DEFAULT;
-
- if (!FlattrUtils.hasToken()) {
- exitCode = NO_TOKEN;
- } else if (DBReader.getFlattrQueueEmpty(context) && extra_flattr_thing == null) {
- exitCode = NO_THINGS;
- } else if (!haveInternetAccess(context) || run_mode == ENQUEUE_ONLY) {
- exitCode = ENQUEUED;
- } else {
- List<FlattrThing> flattrList = DBReader.getFlattrQueue(context);
- Log.d(TAG, "flattrQueue processing list with " + flattrList.size() + " items.");
-
- if (extra_flattr_thing != null)
- flattrList.add(extra_flattr_thing);
-
- flattrd.ensureCapacity(flattrList.size());
-
- for (FlattrThing thing : flattrList) {
- try {
- Log.d(TAG, "flattrQueue processing " + thing.getTitle() + " " + thing.getPaymentLink());
- publishProgress(String.format(context.getString(R.string.flattring_thing), thing.getTitle()));
-
- thing.getFlattrStatus().setUnflattred(); // pop from queue to prevent unflattrable things from getting stuck in flattr queue infinitely
-
- FlattrUtils.clickUrl(context, thing.getPaymentLink());
- flattrd.add(thing.getTitle());
-
- thing.getFlattrStatus().setFlattred();
- } catch (Exception e) {
- Log.d(TAG, "flattrQueue processing exception at item " + thing.getTitle() + " " + e.getMessage());
- flattr_failed.ensureCapacity(flattrList.size());
- flattr_failed.add(thing.getTitle() + ": " + e.getMessage());
- }
- Log.d(TAG, "flattrQueue processing - going to write thing back to db with flattr_status " + Long.toString(thing.getFlattrStatus().toLong()));
- DBWriter.setFlattredStatus(context, thing, false);
- }
+ private void postNoTokenNotification() {
+ PendingIntent contentIntent = PendingIntent.getActivity(context, 0, new Intent(context, FlattrAuthActivity.class), 0);
+
+ Notification notification = new NotificationCompat.Builder(context)
+ .setStyle(new NotificationCompat.BigTextStyle().bigText(context.getString(R.string.no_flattr_token_notification_msg)))
+ .setContentIntent(contentIntent)
+ .setContentTitle(context.getString(R.string.no_flattr_token_title))
+ .setTicker(context.getString(R.string.no_flattr_token_title))
+ .setSmallIcon(R.drawable.stat_notify_sync_error)
+ .setOngoing(false)
+ .setAutoCancel(true)
+ .build();
+ ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE)).notify(NOTIFICATION_ID, notification);
+ }
+ private void postFlattrFailedNotification() {
+ if (countFailed == 0) {
+ return;
}
- return null;
- }
-
- @Override
- protected void onProgressUpdate(String... names) {
PendingIntent contentIntent = PendingIntent.getActivity(context, 0, new Intent(context, MainActivity.class), 0);
- if (android.os.Build.VERSION.SDK_INT >= 16) {
- notificationBuilder.setBigContentTitle(names[0]);
- notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
+ String title;
+ String subtext;
+
+ if (countFailed == 1) {
+ title = context.getString(R.string.flattrd_failed_label);
+ String exceptionMsg = (exception.getMessage() != null) ? exception.getMessage() : "";
+ subtext = context.getString(R.string.flattr_click_failure, extraFlattrThing.getTitle())
+ + "\n" + exceptionMsg;
} else {
- notificationCompatBuilder.setContentText(names[0]);
- notificationCompatBuilder.setContentIntent(contentIntent);
- notificationManager.notify(NOTIFICATION_ID, notificationCompatBuilder.build());
+ title = context.getString(R.string.flattrd_label);
+ subtext = context.getString(R.string.flattr_click_success_count, countSuccess) + "\n"
+ + context.getString(R.string.flattr_click_failure_count, countFailed);
}
+
+ Notification notification = new NotificationCompat.Builder(context)
+ .setStyle(new NotificationCompat.BigTextStyle().bigText(subtext))
+ .setContentIntent(contentIntent)
+ .setContentTitle(title)
+ .setTicker(title)
+ .setSmallIcon(R.drawable.stat_notify_sync_error)
+ .setOngoing(false)
+ .setAutoCancel(true)
+ .build();
+ ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE)).notify(NOTIFICATION_ID, notification);
}
- @SuppressLint("NewApi")
+
+ /**
+ * Starts the FlattrClickWorker as an AsyncTask.
+ */
+ @TargetApi(11)
public void executeAsync() {
- FlattrUtils.hasToken();
if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
- executeOnExecutor(THREAD_POOL_EXECUTOR);
+ executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} else {
execute();
}
diff --git a/src/de/danoeh/antennapod/asynctask/ImageDiskCache.java b/src/de/danoeh/antennapod/asynctask/ImageDiskCache.java
index 1d069daa5..77609f28b 100644
--- a/src/de/danoeh/antennapod/asynctask/ImageDiskCache.java
+++ b/src/de/danoeh/antennapod/asynctask/ImageDiskCache.java
@@ -11,6 +11,7 @@ import de.danoeh.antennapod.service.download.DownloadRequest;
import de.danoeh.antennapod.service.download.HttpDownloader;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Validate;
import java.io.*;
import java.util.ArrayList;
@@ -48,9 +49,8 @@ public class ImageDiskCache {
* Return an instance of an ImageDiskCache that stores images in the specified folder.
*/
public static synchronized ImageDiskCache getInstance(String path, long maxCacheSize) {
- if (path == null) {
- throw new NullPointerException();
- }
+ Validate.notNull(path);
+
if (cacheSingletons.containsKey(path)) {
return cacheSingletons.get(path);
}
@@ -358,9 +358,7 @@ public class ImageDiskCache {
private final long size;
public DiskCacheObject(String fileUrl, long size) {
- if (fileUrl == null) {
- throw new NullPointerException();
- }
+ Validate.notNull(fileUrl);
this.fileUrl = fileUrl;
this.timestamp = System.currentTimeMillis();
this.size = size;
diff --git a/src/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java b/src/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java
new file mode 100644
index 000000000..d1ed795dc
--- /dev/null
+++ b/src/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java
@@ -0,0 +1,107 @@
+package de.danoeh.antennapod.dialog;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import org.apache.commons.lang3.Validate;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.preferences.UserPreferences;
+
+/**
+ * Creates a new AlertDialog that displays preferences for auto-flattring to the user.
+ */
+public class AutoFlattrPreferenceDialog {
+
+ private AutoFlattrPreferenceDialog() {
+ }
+
+ public static void newAutoFlattrPreferenceDialog(final Activity activity, final AutoFlattrPreferenceDialogInterface callback) {
+ Validate.notNull(activity);
+ Validate.notNull(callback);
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+
+ @SuppressLint("InflateParams") View view = activity.getLayoutInflater().inflate(R.layout.autoflattr_preference_dialog, null);
+ final CheckBox chkAutoFlattr = (CheckBox) view.findViewById(R.id.chkAutoFlattr);
+ final SeekBar skbPercent = (SeekBar) view.findViewById(R.id.skbPercent);
+ final TextView txtvStatus = (TextView) view.findViewById(R.id.txtvStatus);
+
+ chkAutoFlattr.setChecked(UserPreferences.isAutoFlattr());
+ skbPercent.setEnabled(chkAutoFlattr.isChecked());
+ txtvStatus.setEnabled(chkAutoFlattr.isChecked());
+
+ final int initialValue = (int) (UserPreferences.getAutoFlattrPlayedDurationThreshold() * 100.0f);
+ setStatusMsgText(activity, txtvStatus, initialValue);
+ skbPercent.setProgress(initialValue);
+
+ chkAutoFlattr.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ skbPercent.setEnabled(chkAutoFlattr.isChecked());
+ txtvStatus.setEnabled(chkAutoFlattr.isChecked());
+ }
+ });
+
+ skbPercent.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ setStatusMsgText(activity, txtvStatus, progress);
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+
+ }
+ });
+
+ builder.setTitle(R.string.pref_auto_flattr_title)
+ .setView(view)
+ .setPositiveButton(R.string.confirm_label, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ float progDouble = ((float) skbPercent.getProgress()) / 100.0f;
+ callback.onConfirmed(chkAutoFlattr.isChecked(), progDouble);
+ dialog.dismiss();
+ }
+ })
+ .setNegativeButton(R.string.cancel_label, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ callback.onCancelled();
+ dialog.dismiss();
+ }
+ })
+ .setCancelable(false).show();
+ }
+
+ private static void setStatusMsgText(Context context, TextView txtvStatus, int progress) {
+ if (progress == 0) {
+ txtvStatus.setText(R.string.auto_flattr_ater_beginning);
+ } else if (progress == 100) {
+ txtvStatus.setText(R.string.auto_flattr_ater_end);
+ } else {
+ txtvStatus.setText(context.getString(R.string.auto_flattr_after_percent, progress));
+ }
+ }
+
+ public static interface AutoFlattrPreferenceDialogInterface {
+ public void onCancelled();
+
+ public void onConfirmed(boolean autoFlattrEnabled, float autoFlattrValue);
+ }
+
+
+}
diff --git a/src/de/danoeh/antennapod/dialog/FeedItemDialog.java b/src/de/danoeh/antennapod/dialog/FeedItemDialog.java
index 4a130af6e..7384463de 100644
--- a/src/de/danoeh/antennapod/dialog/FeedItemDialog.java
+++ b/src/de/danoeh/antennapod/dialog/FeedItemDialog.java
@@ -1,5 +1,6 @@
package de.danoeh.antennapod.dialog;
+import android.annotation.TargetApi;
import android.app.Dialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
@@ -21,6 +22,14 @@ import android.webkit.WebViewClient;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;
+
+import org.apache.commons.lang3.StringEscapeUtils;
+import org.apache.commons.lang3.Validate;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
@@ -34,11 +43,6 @@ import de.danoeh.antennapod.storage.DownloadRequester;
import de.danoeh.antennapod.util.QueueAccess;
import de.danoeh.antennapod.util.ShownotesProvider;
import de.danoeh.antennapod.util.menuhandler.FeedItemMenuHandler;
-import org.apache.commons.lang3.StringEscapeUtils;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.concurrent.Callable;
/**
* Shows information about a specific FeedItem and provides actions like playing, downloading, etc.
@@ -58,7 +62,7 @@ public class FeedItemDialog extends Dialog {
private PopupMenu popupMenu;
public static FeedItemDialog newInstance(Context context, FeedItemDialogSavedInstance savedInstance) {
- if (savedInstance == null) throw new IllegalArgumentException("savedInstance = null");
+ Validate.notNull(savedInstance);
FeedItemDialog dialog = newInstance(context, savedInstance.item, savedInstance.queueAccess);
if (savedInstance.isShowing) {
dialog.show();
@@ -76,8 +80,8 @@ public class FeedItemDialog extends Dialog {
public FeedItemDialog(Context context, int theme, FeedItem item, QueueAccess queue) {
super(context, theme);
- if (item == null) throw new IllegalArgumentException("item = null");
- if (queue == null) throw new IllegalArgumentException("queue = null");
+ Validate.notNull(item);
+ Validate.notNull(queue);
this.item = item;
this.queue = queue;
}
@@ -95,6 +99,7 @@ public class FeedItemDialog extends Dialog {
&& UserPreferences.getTheme() != R.style.Theme_AntennaPod_Dark;
}
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -147,12 +152,9 @@ public class FeedItemDialog extends Dialog {
@Override
public void onClick(View v) {
- FeedMedia media = item.getMedia();
- if (media == null) {
- return;
- }
actionButtonCallback.onActionButtonPressed(item);
- if (media.isDownloaded()) {
+ FeedMedia media = item.getMedia();
+ if (media != null && media.isDownloaded()) {
// playback was started, dialog should close itself
dismiss();
}
@@ -166,16 +168,17 @@ public class FeedItemDialog extends Dialog {
{
@Override
public void onClick(View v) {
- FeedMedia media = item.getMedia();
- if (media == null) {
- return;
- }
-
- if (!media.isDownloaded()) {
- DBTasks.playMedia(getContext(), media, true, true, true);
- dismiss();
- } else {
- DBWriter.deleteFeedMediaOfItem(getContext(), media.getId());
+ if (item.hasMedia()) {
+ FeedMedia media = item.getMedia();
+ if (!media.isDownloaded()) {
+ DBTasks.playMedia(getContext(), media, true, true, true);
+ dismiss();
+ } else {
+ DBWriter.deleteFeedMediaOfItem(getContext(), media.getId());
+ }
+ } else if (item.getLink() != null) {
+ Uri uri = Uri.parse(item.getLink());
+ getContext().startActivity(new Intent(Intent.ACTION_VIEW, uri));
}
}
}
@@ -186,7 +189,13 @@ public class FeedItemDialog extends Dialog {
public void onClick(View v) {
popupMenu.getMenu().clear();
popupMenu.inflate(R.menu.feeditem_dialog);
- FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, queue);
+ if (item.hasMedia()) {
+ FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, queue);
+ } else {
+ // these are already available via button1 and button2
+ FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, queue,
+ R.id.mark_read_item, R.id.visit_website_item);
+ }
popupMenu.show();
}
}
@@ -228,9 +237,26 @@ public class FeedItemDialog extends Dialog {
}
FeedMedia media = item.getMedia();
if (media == null) {
- header.setVisibility(View.GONE);
+ TypedArray drawables = getContext().obtainStyledAttributes(new int[]{R.attr.navigation_accept,
+ R.attr.location_web_site});
+
+ if (!item.isRead()) {
+ butAction1.setImageDrawable(drawables.getDrawable(0));
+ butAction1.setContentDescription(getContext().getString(R.string.mark_read_label));
+ butAction1.setVisibility(View.VISIBLE);
+ } else {
+ butAction1.setVisibility(View.INVISIBLE);
+ }
+
+ if (item.getLink() != null) {
+ butAction2.setImageDrawable(drawables.getDrawable(1));
+ butAction2.setContentDescription(getContext().getString(R.string.visit_website_label));
+ } else {
+ butAction2.setEnabled(false);
+ }
+
+ drawables.recycle();
} else {
- header.setVisibility(View.VISIBLE);
boolean isDownloading = DownloadRequester.getInstance().isDownloadingFile(media);
TypedArray drawables = getContext().obtainStyledAttributes(new int[]{R.attr.av_play,
R.attr.av_download, R.attr.action_stream, R.attr.content_discard, R.attr.navigation_cancel});
@@ -348,7 +374,7 @@ public class FeedItemDialog extends Dialog {
public void setItem(FeedItem item) {
- if (item == null) throw new IllegalArgumentException("item = null");
+ Validate.notNull(item);
this.item = item;
}
@@ -369,7 +395,7 @@ public class FeedItemDialog extends Dialog {
}
public void setQueue(QueueAccess queue) {
- if (queue == null) throw new IllegalArgumentException("queue = null");
+ Validate.notNull(queue);
this.queue = queue;
}
@@ -387,7 +413,7 @@ public class FeedItemDialog extends Dialog {
/**
* Used to save the FeedItemDialog's state across configuration changes
- * */
+ */
public static class FeedItemDialogSavedInstance {
final FeedItem item;
final QueueAccess queueAccess;
diff --git a/src/de/danoeh/antennapod/dialog/TimeDialog.java b/src/de/danoeh/antennapod/dialog/TimeDialog.java
index cb3ebf0ab..bbd514640 100644
--- a/src/de/danoeh/antennapod/dialog/TimeDialog.java
+++ b/src/de/danoeh/antennapod/dialog/TimeDialog.java
@@ -8,6 +8,7 @@ import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.view.Window;
+import android.view.inputmethod.InputMethodManager;
import android.widget.*;
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
@@ -15,114 +16,123 @@ import de.danoeh.antennapod.R;
import java.util.concurrent.TimeUnit;
public abstract class TimeDialog extends Dialog {
- private static final String TAG = "TimeDialog";
+ private static final String TAG = "TimeDialog";
- private static final int DEFAULT_SPINNER_POSITION = 1;
+ private static final int DEFAULT_SPINNER_POSITION = 1;
- private Context context;
+ private Context context;
- private EditText etxtTime;
- private Spinner spTimeUnit;
- private Button butConfirm;
- private Button butCancel;
+ private EditText etxtTime;
+ private Spinner spTimeUnit;
+ private Button butConfirm;
+ private Button butCancel;
- private TimeUnit[] units = { TimeUnit.SECONDS, TimeUnit.MINUTES,
- TimeUnit.HOURS };
+ private TimeUnit[] units = {TimeUnit.SECONDS, TimeUnit.MINUTES,
+ TimeUnit.HOURS};
- public TimeDialog(Context context, int titleTextId, int leftButtonTextId) {
- super(context);
- this.context = context;
- }
+ public TimeDialog(Context context, int titleTextId, int leftButtonTextId) {
+ super(context);
+ this.context = context;
+ }
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- requestWindowFeature(Window.FEATURE_NO_TITLE);
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
String[] spinnerContent = new String[]{context.getString(R.string.time_unit_seconds),
- context.getString(R.string.time_unit_minutes),
- context.getString(R.string.time_unit_hours)};
-
- setContentView(R.layout.time_dialog);
- etxtTime = (EditText) findViewById(R.id.etxtTime);
- spTimeUnit = (Spinner) findViewById(R.id.spTimeUnit);
- butConfirm = (Button) findViewById(R.id.butConfirm);
- butCancel = (Button) findViewById(R.id.butCancel);
-
- butConfirm.setText(R.string.set_sleeptimer_label);
- butCancel.setText(R.string.cancel_label);
- setTitle(R.string.set_sleeptimer_label);
- ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<String>(
- this.getContext(), android.R.layout.simple_spinner_item,
- spinnerContent);
- spinnerAdapter
- .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- spTimeUnit.setAdapter(spinnerAdapter);
- spTimeUnit.setSelection(DEFAULT_SPINNER_POSITION);
- butCancel.setOnClickListener(new View.OnClickListener() {
-
- @Override
- public void onClick(View v) {
- dismiss();
- }
- });
- butConfirm.setOnClickListener(new View.OnClickListener() {
-
- @Override
- public void onClick(View v) {
- try {
- long input = readTimeMillis();
- onTimeEntered(input);
- dismiss();
- } catch (NumberFormatException e) {
- e.printStackTrace();
- Toast toast = Toast.makeText(context,
- R.string.time_dialog_invalid_input,
- Toast.LENGTH_LONG);
- toast.show();
- }
- }
- });
- etxtTime.addTextChangedListener(new TextWatcher() {
-
- @Override
- public void afterTextChanged(Editable s) {
- checkInputLength(s.length());
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count,
- int after) {
-
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before,
- int count) {
-
- }
- });
- checkInputLength(etxtTime.getText().length());
-
- }
-
- private void checkInputLength(int length) {
- if (length > 0) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Length is larger than 0, enabling confirm button");
- butConfirm.setEnabled(true);
- } else {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Length is smaller than 0, disabling confirm button");
- butConfirm.setEnabled(false);
- }
- }
-
- public abstract void onTimeEntered(long millis);
-
- private long readTimeMillis() {
- TimeUnit selectedUnit = units[spTimeUnit.getSelectedItemPosition()];
- long value = Long.valueOf(etxtTime.getText().toString());
- return selectedUnit.toMillis(value);
- }
+ context.getString(R.string.time_unit_minutes),
+ context.getString(R.string.time_unit_hours)};
+
+ setContentView(R.layout.time_dialog);
+ etxtTime = (EditText) findViewById(R.id.etxtTime);
+ spTimeUnit = (Spinner) findViewById(R.id.spTimeUnit);
+ butConfirm = (Button) findViewById(R.id.butConfirm);
+ butCancel = (Button) findViewById(R.id.butCancel);
+
+ butConfirm.setText(R.string.set_sleeptimer_label);
+ butCancel.setText(R.string.cancel_label);
+ setTitle(R.string.set_sleeptimer_label);
+ ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<String>(
+ this.getContext(), android.R.layout.simple_spinner_item,
+ spinnerContent);
+ spinnerAdapter
+ .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ spTimeUnit.setAdapter(spinnerAdapter);
+ spTimeUnit.setSelection(DEFAULT_SPINNER_POSITION);
+ butCancel.setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ dismiss();
+ }
+ });
+ butConfirm.setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ try {
+ long input = readTimeMillis();
+ onTimeEntered(input);
+ dismiss();
+ } catch (NumberFormatException e) {
+ e.printStackTrace();
+ Toast toast = Toast.makeText(context,
+ R.string.time_dialog_invalid_input,
+ Toast.LENGTH_LONG);
+ toast.show();
+ }
+ }
+ });
+ etxtTime.addTextChangedListener(new TextWatcher() {
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ checkInputLength(s.length());
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count,
+ int after) {
+
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before,
+ int count) {
+
+ }
+ });
+ checkInputLength(etxtTime.getText().length());
+ etxtTime.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.showSoftInput(etxtTime, InputMethodManager.SHOW_IMPLICIT);
+ }
+ }, 100);
+
+
+
+ }
+
+ private void checkInputLength(int length) {
+ if (length > 0) {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Length is larger than 0, enabling confirm button");
+ butConfirm.setEnabled(true);
+ } else {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Length is smaller than 0, disabling confirm button");
+ butConfirm.setEnabled(false);
+ }
+ }
+
+ public abstract void onTimeEntered(long millis);
+
+ private long readTimeMillis() {
+ TimeUnit selectedUnit = units[spTimeUnit.getSelectedItemPosition()];
+ long value = Long.valueOf(etxtTime.getText().toString());
+ return selectedUnit.toMillis(value);
+ }
}
diff --git a/src/de/danoeh/antennapod/feed/EventDistributor.java b/src/de/danoeh/antennapod/feed/EventDistributor.java
index 85a9b5727..5fb72048e 100644
--- a/src/de/danoeh/antennapod/feed/EventDistributor.java
+++ b/src/de/danoeh/antennapod/feed/EventDistributor.java
@@ -2,6 +2,9 @@ package de.danoeh.antennapod.feed;
import android.os.Handler;
import android.util.Log;
+
+import org.apache.commons.lang3.Validate;
+
import de.danoeh.antennapod.BuildConfig;
import java.util.AbstractQueue;
@@ -90,10 +93,7 @@ public class EventDistributor extends Observable {
@Override
public void addObserver(Observer observer) {
super.addObserver(observer);
- if (!(observer instanceof EventListener)) {
- throw new IllegalArgumentException(
- "Observer must be instance of EventListener");
- }
+ Validate.isInstanceOf(EventListener.class, observer);
}
public void sendDownloadQueuedBroadcast() {
diff --git a/src/de/danoeh/antennapod/feed/FeedMedia.java b/src/de/danoeh/antennapod/feed/FeedMedia.java
index 1f8e7f8f8..dc941cb48 100644
--- a/src/de/danoeh/antennapod/feed/FeedMedia.java
+++ b/src/de/danoeh/antennapod/feed/FeedMedia.java
@@ -401,7 +401,9 @@ public class FeedMedia extends FeedFile implements Playable {
@Override
public String getImageLoaderCacheKey() {
String out;
- if (item.hasItemImageDownloaded()) {
+ if (item == null) {
+ return null;
+ } else if (item.hasItemImageDownloaded()) {
out = item.getImageLoaderCacheKey();
} else {
out = new Playable.DefaultPlayableImageLoader(this)
diff --git a/src/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java b/src/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
index ed304ad37..082fe93fc 100644
--- a/src/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
+++ b/src/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
@@ -68,6 +68,7 @@ public class CompletedDownloadsFragment extends ListFragment {
listAdapter = null;
viewCreated = false;
feedItemDialog = null;
+ stopItemLoader();
}
@Override
@@ -103,6 +104,7 @@ public class CompletedDownloadsFragment extends ListFragment {
listAdapter = new DownloadedEpisodesListAdapter(getActivity(), itemAccess);
setListAdapter(listAdapter);
}
+ setListShown(true);
listAdapter.notifyDataSetChanged();
if (feedItemDialog != null) {
boolean res = feedItemDialog.updateContent(queue, items);
@@ -171,7 +173,6 @@ public class CompletedDownloadsFragment extends ListFragment {
@Override
protected void onPostExecute(Object[] results) {
super.onPostExecute(results);
- setListShown(true);
if (results != null) {
items = (List<FeedItem>) results[0];
queue = (QueueAccess) results[1];
diff --git a/src/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java b/src/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
index bf6974982..04c7fbf8e 100644
--- a/src/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
+++ b/src/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
@@ -2,8 +2,11 @@ package de.danoeh.antennapod.fragment;
import android.annotation.SuppressLint;
import android.app.Activity;
-import android.content.*;
-import android.content.res.TypedArray;
+import android.content.ActivityNotFoundException;
+import android.content.ClipData;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
@@ -11,26 +14,33 @@ import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
-import android.util.TypedValue;
-import android.view.*;
+import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
import android.webkit.WebSettings.LayoutAlgorithm;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.preferences.UserPreferences;
import de.danoeh.antennapod.storage.DBReader;
+import de.danoeh.antennapod.util.Converter;
import de.danoeh.antennapod.util.ShareUtils;
import de.danoeh.antennapod.util.ShownotesProvider;
import de.danoeh.antennapod.util.playback.Playable;
-import org.apache.commons.lang3.StringEscapeUtils;
-
-import java.util.concurrent.Callable;
+import de.danoeh.antennapod.util.playback.PlaybackController;
+import de.danoeh.antennapod.util.playback.Timeline;
-/** Displays the description of a Playable object in a Webview. */
+/**
+ * Displays the description of a Playable object in a Webview.
+ */
public class ItemDescriptionFragment extends Fragment {
private static final String TAG = "ItemDescriptionFragment";
@@ -43,6 +53,7 @@ public class ItemDescriptionFragment extends Fragment {
private static final String ARG_FEEDITEM_ID = "arg.feeditem";
private static final String ARG_SAVE_STATE = "arg.saveState";
+ private static final String ARG_HIGHLIGHT_TIMECODES = "arg.highlightTimecodes";
private WebView webvDescription;
@@ -63,21 +74,29 @@ public class ItemDescriptionFragment extends Fragment {
*/
private boolean saveState;
+ /**
+ * True if Fragment should highlight timecodes (e.g. time codes in the HH:MM:SS format).
+ */
+ private boolean highlightTimecodes;
+
public static ItemDescriptionFragment newInstance(Playable media,
- boolean saveState) {
+ boolean saveState,
+ boolean highlightTimecodes) {
ItemDescriptionFragment f = new ItemDescriptionFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_PLAYABLE, media);
args.putBoolean(ARG_SAVE_STATE, saveState);
+ args.putBoolean(ARG_HIGHLIGHT_TIMECODES, highlightTimecodes);
f.setArguments(args);
return f;
}
- public static ItemDescriptionFragment newInstance(FeedItem item, boolean saveState) {
+ public static ItemDescriptionFragment newInstance(FeedItem item, boolean saveState, boolean highlightTimecodes) {
ItemDescriptionFragment f = new ItemDescriptionFragment();
Bundle args = new Bundle();
args.putLong(ARG_FEEDITEM_ID, item.getId());
args.putBoolean(ARG_SAVE_STATE, saveState);
+ args.putBoolean(ARG_HIGHLIGHT_TIMECODES, highlightTimecodes);
f.setArguments(args);
return f;
}
@@ -106,12 +125,16 @@ public class ItemDescriptionFragment extends Fragment {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
- Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
- try {
- startActivity(intent);
- } catch (ActivityNotFoundException e) {
- e.printStackTrace();
- return false;
+ if (Timeline.isTimecodeLink(url)) {
+ onTimecodeLinkSelected(url);
+ } else {
+ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+ try {
+ startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ e.printStackTrace();
+ return true;
+ }
}
return true;
}
@@ -178,6 +201,7 @@ public class ItemDescriptionFragment extends Fragment {
Log.d(TAG, "Creating fragment");
Bundle args = getArguments();
saveState = args.getBoolean(ARG_SAVE_STATE, false);
+ highlightTimecodes = args.getBoolean(ARG_HIGHLIGHT_TIMECODES, false);
}
@@ -229,21 +253,6 @@ public class ItemDescriptionFragment extends Fragment {
}
}
- /**
- * Return the CSS style of the Webview.
- *
- * @param textColor the default color to use for the text in the webview. This
- * value is inserted directly into the CSS String.
- */
- private String applyWebviewStyle(String textColor, String data) {
- final String WEBVIEW_STYLE = "<html><head><style type=\"text/css\"> @font-face { font-family: 'Roboto-Light'; src: url('file:///android_asset/Roboto-Light.ttf'); } * { color: %s; font-family: roboto-Light; font-size: 11pt; } a { font-style: normal; text-decoration: none; font-weight: normal; color: #00A8DF; } img { display: block; margin: 10 auto; max-width: %s; height: auto; } body { margin: %dpx %dpx %dpx %dpx; }</style></head><body>%s</body></html>";
- final int pageMargin = (int) TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP, 8, getResources()
- .getDisplayMetrics());
- return String.format(WEBVIEW_STYLE, textColor, "100%", pageMargin,
- pageMargin, pageMargin, pageMargin, data);
- }
-
private View.OnLongClickListener webViewLongClickListener = new View.OnLongClickListener() {
@Override
@@ -254,7 +263,8 @@ public class ItemDescriptionFragment extends Fragment {
if (BuildConfig.DEBUG)
Log.d(TAG,
"Link of webview was long-pressed. Extra: "
- + r.getExtra());
+ + r.getExtra()
+ );
selectedURL = r.getExtra();
webvDescription.showContextMenu();
return true;
@@ -295,6 +305,13 @@ public class ItemDescriptionFragment extends Fragment {
R.string.copied_url_msg, Toast.LENGTH_SHORT);
t.show();
break;
+ case R.id.go_to_position_item:
+ if (Timeline.isTimecodeLink(selectedURL)) {
+ onTimecodeLinkSelected(selectedURL);
+ } else {
+ Log.e(TAG, "Selected go_to_position_item, but URL was no timecode link: " + selectedURL);
+ }
+ break;
default:
handled = false;
break;
@@ -311,13 +328,19 @@ public class ItemDescriptionFragment extends Fragment {
ContextMenuInfo menuInfo) {
if (selectedURL != null) {
super.onCreateContextMenu(menu, v, menuInfo);
- menu.add(Menu.NONE, R.id.open_in_browser_item, Menu.NONE,
- R.string.open_in_browser_label);
- menu.add(Menu.NONE, R.id.copy_url_item, Menu.NONE,
- R.string.copy_url_label);
- menu.add(Menu.NONE, R.id.share_url_item, Menu.NONE,
- R.string.share_url_label);
- menu.setHeaderTitle(selectedURL);
+ if (Timeline.isTimecodeLink(selectedURL)) {
+ menu.add(Menu.NONE, R.id.go_to_position_item, Menu.NONE,
+ R.string.go_to_position_label);
+ menu.setHeaderTitle(Converter.getDurationStringLong(Timeline.getTimecodeLinkTime(selectedURL)));
+ } else {
+ menu.add(Menu.NONE, R.id.open_in_browser_item, Menu.NONE,
+ R.string.open_in_browser_label);
+ menu.add(Menu.NONE, R.id.copy_url_item, Menu.NONE,
+ R.string.copy_url_label);
+ menu.add(Menu.NONE, R.id.share_url_item, Menu.NONE,
+ R.string.share_url_label);
+ menu.setHeaderTitle(selectedURL);
+ }
}
}
@@ -364,22 +387,10 @@ public class ItemDescriptionFragment extends Fragment {
if (BuildConfig.DEBUG)
Log.d(TAG, "Loading Webview");
try {
- Callable<String> shownotesLoadTask = shownotesProvider.loadShownotes();
- final String shownotes = shownotesLoadTask.call();
-
- data = StringEscapeUtils.unescapeHtml4(shownotes);
Activity activity = getActivity();
if (activity != null) {
- TypedArray res = activity
- .getTheme()
- .obtainStyledAttributes(
- new int[]{android.R.attr.textColorPrimary});
- int colorResource = res.getColor(0, 0);
- String colorString = String.format("#%06X",
- 0xFFFFFF & colorResource);
- Log.i(TAG, "text color: " + colorString);
- res.recycle();
- data = applyWebviewStyle(colorString, data);
+ Timeline timeline = new Timeline(activity, shownotesProvider);
+ data = timeline.processShownotes(highlightTimecodes);
} else {
cancel(true);
}
@@ -409,7 +420,8 @@ public class ItemDescriptionFragment extends Fragment {
if (BuildConfig.DEBUG)
Log.d(TAG,
"Saving scroll position: "
- + webvDescription.getScrollY());
+ + webvDescription.getScrollY()
+ );
editor.putInt(PREF_SCROLL_Y, webvDescription.getScrollY());
editor.putString(PREF_PLAYABLE_ID, media.getIdentifier()
.toString());
@@ -447,4 +459,18 @@ public class ItemDescriptionFragment extends Fragment {
}
return false;
}
+
+ private void onTimecodeLinkSelected(String link) {
+ int time = Timeline.getTimecodeLinkTime(link);
+ if (getActivity() != null && getActivity() instanceof ItemDescriptionFragmentCallback) {
+ PlaybackController pc = ((ItemDescriptionFragmentCallback) getActivity()).getPlaybackController();
+ if (pc != null) {
+ pc.seekTo(time);
+ }
+ }
+ }
+
+ public interface ItemDescriptionFragmentCallback {
+ public PlaybackController getPlaybackController();
+ }
}
diff --git a/src/de/danoeh/antennapod/fragment/ItemlistFragment.java b/src/de/danoeh/antennapod/fragment/ItemlistFragment.java
index 5d0b1bec8..d37f17b6d 100644
--- a/src/de/danoeh/antennapod/fragment/ItemlistFragment.java
+++ b/src/de/danoeh/antennapod/fragment/ItemlistFragment.java
@@ -12,11 +12,20 @@ import android.support.v4.app.ListFragment;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.SearchView;
import android.util.Log;
-import android.view.*;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
+
+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;
@@ -41,8 +50,7 @@ import de.danoeh.antennapod.storage.DownloadRequester;
import de.danoeh.antennapod.util.QueueAccess;
import de.danoeh.antennapod.util.menuhandler.FeedMenuHandler;
import de.danoeh.antennapod.util.menuhandler.MenuItemUtils;
-
-import java.util.List;
+import de.danoeh.antennapod.util.menuhandler.NavDrawerActivity;
/**
* Displays a list of FeedItems.
@@ -97,7 +105,7 @@ public class ItemlistFragment extends ListFragment {
setHasOptionsMenu(true);
Bundle args = getArguments();
- if (args == null) throw new IllegalArgumentException("args invalid");
+ Validate.notNull(args);
feedID = args.getLong(ARGUMENT_FEED_ID);
}
@@ -155,31 +163,36 @@ public class ItemlistFragment extends ListFragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
- FeedMenuHandler.onCreateOptionsMenu(inflater, menu);
- final SearchView sv = new SearchView(getActivity());
- MenuItemUtils.addSearchItem(menu, sv);
- sv.setQueryHint(getString(R.string.search_hint));
- sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String s) {
- sv.clearFocus();
- if (itemsLoaded) {
- ((MainActivity) getActivity()).loadChildFragment(SearchFragment.newInstance(s, feed.getId()));
+ if (itemsLoaded && !MenuItemUtils.isActivityDrawerOpen((NavDrawerActivity) getActivity())) {
+ FeedMenuHandler.onCreateOptionsMenu(inflater, menu);
+
+ final SearchView sv = new SearchView(getActivity());
+ MenuItemUtils.addSearchItem(menu, sv);
+ sv.setQueryHint(getString(R.string.search_hint));
+ sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ sv.clearFocus();
+ if (itemsLoaded) {
+ ((MainActivity) getActivity()).loadChildFragment(SearchFragment.newInstance(s, feed.getId()));
+ }
+ return true;
}
- return true;
- }
- @Override
- public boolean onQueryTextChange(String s) {
- return false;
- }
- });
+ @Override
+ public boolean onQueryTextChange(String s) {
+ return false;
+ }
+ });
+ }
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
- FeedMenuHandler.onPrepareOptionsMenu(menu, feed);
+ if (itemsLoaded && !MenuItemUtils.isActivityDrawerOpen((NavDrawerActivity) getActivity())) {
+ FeedMenuHandler.onPrepareOptionsMenu(menu, feed);
+ }
}
@Override
@@ -322,10 +335,11 @@ public class ItemlistFragment extends ListFragment {
Log.e(TAG, "Unable to setup listview: listView = null or feed = null");
return;
}
+ ListView lv = getListView();
LayoutInflater inflater = (LayoutInflater)
getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- View header = inflater.inflate(R.layout.feeditemlist_header, null);
- getListView().addHeaderView(header);
+ View header = inflater.inflate(R.layout.feeditemlist_header, lv, false);
+ lv.addHeaderView(header);
TextView txtvTitle = (TextView) header.findViewById(R.id.txtvTitle);
TextView txtvAuthor = (TextView) header.findViewById(R.id.txtvAuthor);
diff --git a/src/de/danoeh/antennapod/fragment/NewEpisodesFragment.java b/src/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
index a0861779c..fe995256b 100644
--- a/src/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
+++ b/src/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
@@ -34,6 +34,7 @@ import de.danoeh.antennapod.storage.DBWriter;
import de.danoeh.antennapod.storage.DownloadRequester;
import de.danoeh.antennapod.util.QueueAccess;
import de.danoeh.antennapod.util.menuhandler.MenuItemUtils;
+import de.danoeh.antennapod.util.menuhandler.NavDrawerActivity;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
@@ -138,31 +139,35 @@ public class NewEpisodesFragment extends Fragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
- inflater.inflate(R.menu.new_episodes, menu);
-
- final SearchView sv = new SearchView(getActivity());
- MenuItemUtils.addSearchItem(menu, sv);
- sv.setQueryHint(getString(R.string.search_hint));
- sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String s) {
- sv.clearFocus();
- ((MainActivity) getActivity()).loadChildFragment(SearchFragment.newInstance(s));
- return true;
- }
+ if (itemsLoaded && !MenuItemUtils.isActivityDrawerOpen((NavDrawerActivity) getActivity())) {
+ inflater.inflate(R.menu.new_episodes, menu);
+
+ final SearchView sv = new SearchView(getActivity());
+ MenuItemUtils.addSearchItem(menu, sv);
+ sv.setQueryHint(getString(R.string.search_hint));
+ sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ sv.clearFocus();
+ ((MainActivity) getActivity()).loadChildFragment(SearchFragment.newInstance(s));
+ return true;
+ }
- @Override
- public boolean onQueryTextChange(String s) {
- return false;
- }
- });
+ @Override
+ public boolean onQueryTextChange(String s) {
+ return false;
+ }
+ });
+ }
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
- menu.findItem(R.id.mark_all_read_item).setVisible(unreadItems != null && !unreadItems.isEmpty());
- menu.findItem(R.id.episode_filter_item).setChecked(showOnlyNewEpisodes);
+ if (itemsLoaded && !MenuItemUtils.isActivityDrawerOpen((NavDrawerActivity) getActivity())) {
+ menu.findItem(R.id.mark_all_read_item).setVisible(unreadItems != null && !unreadItems.isEmpty());
+ menu.findItem(R.id.episode_filter_item).setChecked(showOnlyNewEpisodes);
+ }
}
@Override
diff --git a/src/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java b/src/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
index bab5143d5..470186180 100644
--- a/src/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
+++ b/src/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
@@ -25,6 +25,8 @@ import de.danoeh.antennapod.service.download.Downloader;
import de.danoeh.antennapod.storage.DBReader;
import de.danoeh.antennapod.storage.DBWriter;
import de.danoeh.antennapod.util.QueueAccess;
+import de.danoeh.antennapod.util.menuhandler.MenuItemUtils;
+import de.danoeh.antennapod.util.menuhandler.NavDrawerActivity;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
@@ -129,17 +131,21 @@ public class PlaybackHistoryFragment extends ListFragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
- MenuItem clearHistory = menu.add(Menu.NONE, R.id.clear_history_item, Menu.CATEGORY_CONTAINER, R.string.clear_history_label);
- MenuItemCompat.setShowAsAction(clearHistory, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
- TypedArray drawables = getActivity().obtainStyledAttributes(new int[] {R.attr.content_discard});
- clearHistory.setIcon(drawables.getDrawable(0));
- drawables.recycle();
+ if (itemsLoaded && !MenuItemUtils.isActivityDrawerOpen((NavDrawerActivity) getActivity())) {
+ MenuItem clearHistory = menu.add(Menu.NONE, R.id.clear_history_item, Menu.CATEGORY_CONTAINER, R.string.clear_history_label);
+ MenuItemCompat.setShowAsAction(clearHistory, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
+ TypedArray drawables = getActivity().obtainStyledAttributes(new int[]{R.attr.content_discard});
+ clearHistory.setIcon(drawables.getDrawable(0));
+ drawables.recycle();
+ }
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
- menu.findItem(R.id.clear_history_item).setVisible(playbackHistory != null && !playbackHistory.isEmpty());
+ if (itemsLoaded && !MenuItemUtils.isActivityDrawerOpen((NavDrawerActivity) getActivity())) {
+ menu.findItem(R.id.clear_history_item).setVisible(playbackHistory != null && !playbackHistory.isEmpty());
+ }
}
@Override
diff --git a/src/de/danoeh/antennapod/fragment/QueueFragment.java b/src/de/danoeh/antennapod/fragment/QueueFragment.java
index e3f0bb19d..2f322f75b 100644
--- a/src/de/danoeh/antennapod/fragment/QueueFragment.java
+++ b/src/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -5,15 +5,25 @@ import android.content.Context;
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;
-import android.view.*;
+import android.view.ContextMenu;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ProgressBar;
import android.widget.TextView;
+
import com.mobeta.android.dslv.DragSortListView;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
@@ -27,12 +37,8 @@ import de.danoeh.antennapod.service.download.Downloader;
import de.danoeh.antennapod.storage.DBReader;
import de.danoeh.antennapod.storage.DBWriter;
import de.danoeh.antennapod.util.QueueAccess;
-import de.danoeh.antennapod.util.UndoBarController;
-import de.danoeh.antennapod.util.gui.FeedItemUndoToken;
import de.danoeh.antennapod.util.menuhandler.MenuItemUtils;
-
-import java.util.List;
-import java.util.concurrent.atomic.AtomicReference;
+import de.danoeh.antennapod.util.menuhandler.NavDrawerActivity;
/**
* Shows all items in the queue
@@ -47,7 +53,6 @@ public class QueueFragment extends Fragment {
private QueueListAdapter listAdapter;
private TextView txtvEmpty;
private ProgressBar progLoading;
- private UndoBarController undoBarController;
private List<FeedItem> queue;
private List<Downloader> downloaderList;
@@ -111,7 +116,6 @@ public class QueueFragment extends Fragment {
private void resetViewState() {
unregisterForContextMenu(listView);
listAdapter = null;
- undoBarController = null;
activity.set(null);
viewsCreated = false;
blockDownloadObserverUpdate = false;
@@ -133,22 +137,24 @@ public class QueueFragment extends Fragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
- final SearchView sv = new SearchView(getActivity());
- MenuItemUtils.addSearchItem(menu, sv);
- sv.setQueryHint(getString(R.string.search_hint));
- sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String s) {
- sv.clearFocus();
- ((MainActivity) getActivity()).loadChildFragment(SearchFragment.newInstance(s));
- return true;
- }
+ if (itemsLoaded && !MenuItemUtils.isActivityDrawerOpen((NavDrawerActivity) getActivity())) {
+ final SearchView sv = new SearchView(getActivity());
+ MenuItemUtils.addSearchItem(menu, sv);
+ sv.setQueryHint(getString(R.string.search_hint));
+ sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ sv.clearFocus();
+ ((MainActivity) getActivity()).loadChildFragment(SearchFragment.newInstance(s));
+ return true;
+ }
- @Override
- public boolean onQueryTextChange(String s) {
- return false;
- }
- });
+ @Override
+ public boolean onQueryTextChange(String s) {
+ return false;
+ }
+ });
+ }
}
@Override
@@ -185,6 +191,9 @@ public class QueueFragment extends Fragment {
case R.id.move_to_bottom_item:
DBWriter.moveQueueItemToBottom(getActivity(), selectedItem.getId(), true);
return true;
+ case R.id.remove_from_queue_item:
+ DBWriter.removeQueueItem(getActivity(), selectedItem.getId(), false);
+ return true;
default:
return super.onContextItemSelected(item);
}
@@ -212,19 +221,6 @@ public class QueueFragment extends Fragment {
}
});
- 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);
- }
- }
- });
-
listView.setDragSortListener(new DragSortListView.DragSortListener() {
@Override
public void drag(int from, int to) {
@@ -245,13 +241,6 @@ public class QueueFragment extends Fragment {
@Override
public void remove(int 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)
- );
}
});
diff --git a/src/de/danoeh/antennapod/fragment/SearchFragment.java b/src/de/danoeh/antennapod/fragment/SearchFragment.java
index f89e44717..b3ade4d70 100644
--- a/src/de/danoeh/antennapod/fragment/SearchFragment.java
+++ b/src/de/danoeh/antennapod/fragment/SearchFragment.java
@@ -20,6 +20,8 @@ import de.danoeh.antennapod.feed.*;
import de.danoeh.antennapod.storage.DBReader;
import de.danoeh.antennapod.storage.FeedSearcher;
import de.danoeh.antennapod.util.QueueAccess;
+import de.danoeh.antennapod.util.menuhandler.MenuItemUtils;
+import de.danoeh.antennapod.util.menuhandler.NavDrawerActivity;
import java.util.List;
@@ -131,26 +133,28 @@ public class SearchFragment extends ListFragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
- MenuItem item = menu.add(Menu.NONE, R.id.search_item, Menu.NONE, R.string.search_label);
- MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
- final SearchView sv = new SearchView(getActivity());
- sv.setQueryHint(getString(R.string.search_hint));
- sv.setQuery(getArguments().getString(ARG_QUERY), false);
- sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String s) {
- getArguments().putString(ARG_QUERY, s);
- itemsLoaded = false;
- startSearchTask();
- return true;
- }
+ if (itemsLoaded && !MenuItemUtils.isActivityDrawerOpen((NavDrawerActivity) getActivity())) {
+ MenuItem item = menu.add(Menu.NONE, R.id.search_item, Menu.NONE, R.string.search_label);
+ MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
+ final SearchView sv = new SearchView(getActivity());
+ sv.setQueryHint(getString(R.string.search_hint));
+ sv.setQuery(getArguments().getString(ARG_QUERY), false);
+ sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ getArguments().putString(ARG_QUERY, s);
+ itemsLoaded = false;
+ startSearchTask();
+ return true;
+ }
- @Override
- public boolean onQueryTextChange(String s) {
- return false;
- }
- });
- MenuItemCompat.setActionView(item, sv);
+ @Override
+ public boolean onQueryTextChange(String s) {
+ return false;
+ }
+ });
+ MenuItemCompat.setActionView(item, sv);
+ }
}
private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
diff --git a/src/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java b/src/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java
index 837df0594..1b4616207 100644
--- a/src/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java
+++ b/src/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java
@@ -21,6 +21,7 @@ import de.danoeh.antennapod.gpoddernet.GpodnetService;
import de.danoeh.antennapod.gpoddernet.GpodnetServiceException;
import de.danoeh.antennapod.gpoddernet.model.GpodnetPodcast;
import de.danoeh.antennapod.util.menuhandler.MenuItemUtils;
+import de.danoeh.antennapod.util.menuhandler.NavDrawerActivity;
import java.util.List;
@@ -44,22 +45,24 @@ public abstract class PodcastListFragment extends Fragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
- final android.support.v7.widget.SearchView sv = new android.support.v7.widget.SearchView(getActivity());
- MenuItemUtils.addSearchItem(menu, sv);
- sv.setQueryHint(getString(R.string.gpodnet_search_hint));
- sv.setOnQueryTextListener(new android.support.v7.widget.SearchView.OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String s) {
- sv.clearFocus();
- ((MainActivity) getActivity()).loadChildFragment(SearchListFragment.newInstance(s));
- return true;
- }
+ if (!MenuItemUtils.isActivityDrawerOpen((NavDrawerActivity) getActivity())) {
+ final android.support.v7.widget.SearchView sv = new android.support.v7.widget.SearchView(getActivity());
+ MenuItemUtils.addSearchItem(menu, sv);
+ sv.setQueryHint(getString(R.string.gpodnet_search_hint));
+ sv.setOnQueryTextListener(new android.support.v7.widget.SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ sv.clearFocus();
+ ((MainActivity) getActivity()).loadChildFragment(SearchListFragment.newInstance(s));
+ return true;
+ }
- @Override
- public boolean onQueryTextChange(String s) {
- return false;
- }
- });
+ @Override
+ public boolean onQueryTextChange(String s) {
+ return false;
+ }
+ });
+ }
}
@Override
@@ -123,7 +126,7 @@ public abstract class PodcastListFragment extends Fragment {
protected void onPostExecute(List<GpodnetPodcast> gpodnetPodcasts) {
super.onPostExecute(gpodnetPodcasts);
final Context context = getActivity();
- if (context != null && gpodnetPodcasts != null) {
+ if (context != null && gpodnetPodcasts != null && gpodnetPodcasts.size() > 0) {
PodcastListAdapter listAdapter = new PodcastListAdapter(context, 0, gpodnetPodcasts);
gridView.setAdapter(listAdapter);
listAdapter.notifyDataSetChanged();
@@ -132,13 +135,18 @@ public abstract class PodcastListFragment extends Fragment {
gridView.setVisibility(View.VISIBLE);
txtvError.setVisibility(View.GONE);
butRetry.setVisibility(View.GONE);
+ } else if (context != null && gpodnetPodcasts != null) {
+ gridView.setVisibility(View.GONE);
+ progressBar.setVisibility(View.GONE);
+ txtvError.setText(getString(R.string.search_status_no_results));
+ txtvError.setVisibility(View.VISIBLE);
+ butRetry.setVisibility(View.GONE);
} else if (context != null) {
gridView.setVisibility(View.GONE);
progressBar.setVisibility(View.GONE);
txtvError.setText(getString(R.string.error_msg_prefix) + exception.getMessage());
txtvError.setVisibility(View.VISIBLE);
butRetry.setVisibility(View.VISIBLE);
-
}
}
diff --git a/src/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java b/src/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java
index 79d0c5d6f..801024787 100644
--- a/src/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java
+++ b/src/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java
@@ -4,15 +4,17 @@ import android.os.Bundle;
import android.support.v7.widget.SearchView;
import android.view.Menu;
import android.view.MenuInflater;
+
+import org.apache.commons.lang3.Validate;
+
+import java.util.List;
+
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.fragment.SearchFragment;
import de.danoeh.antennapod.gpoddernet.GpodnetService;
import de.danoeh.antennapod.gpoddernet.GpodnetServiceException;
import de.danoeh.antennapod.gpoddernet.model.GpodnetPodcast;
import de.danoeh.antennapod.util.menuhandler.MenuItemUtils;
-
-import java.util.List;
+import de.danoeh.antennapod.util.menuhandler.NavDrawerActivity;
/**
* Performs a search on the gpodder.net directory and displays the results.
@@ -44,22 +46,24 @@ public class SearchListFragment extends PodcastListFragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
final SearchView sv = new SearchView(getActivity());
- MenuItemUtils.addSearchItem(menu, sv);
- sv.setQueryHint(getString(R.string.gpodnet_search_hint));
- sv.setQuery(query, false);
- sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String s) {
- sv.clearFocus();
- changeQuery(s);
- return true;
- }
+ if (!MenuItemUtils.isActivityDrawerOpen((NavDrawerActivity) getActivity())) {
+ MenuItemUtils.addSearchItem(menu, sv);
+ sv.setQueryHint(getString(R.string.gpodnet_search_hint));
+ sv.setQuery(query, false);
+ sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ sv.clearFocus();
+ changeQuery(s);
+ return true;
+ }
- @Override
- public boolean onQueryTextChange(String s) {
- return false;
- }
- });
+ @Override
+ public boolean onQueryTextChange(String s) {
+ return false;
+ }
+ });
+ }
}
@Override
@@ -68,9 +72,8 @@ public class SearchListFragment extends PodcastListFragment {
}
public void changeQuery(String query) {
- if (query == null) {
- throw new NullPointerException();
- }
+ Validate.notNull(query);
+
this.query = query;
loadData();
}
diff --git a/src/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java b/src/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java
index f016290bf..204dda992 100644
--- a/src/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java
+++ b/src/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java
@@ -1,6 +1,9 @@
package de.danoeh.antennapod.fragment.gpodnet;
import android.os.Bundle;
+
+import org.apache.commons.lang3.Validate;
+
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.gpoddernet.GpodnetService;
import de.danoeh.antennapod.gpoddernet.GpodnetServiceException;
@@ -21,7 +24,7 @@ public class TagFragment extends PodcastListFragment {
private GpodnetTag tag;
public static TagFragment newInstance(String tagName) {
- if (tagName == null) throw new IllegalArgumentException("tagName = null");
+ Validate.notNull(tagName);
TagFragment fragment = new TagFragment();
Bundle args = new Bundle();
args.putString("tag", tagName);
@@ -34,7 +37,7 @@ public class TagFragment extends PodcastListFragment {
super.onCreate(savedInstanceState);
Bundle args = getArguments();
- if (args == null || args.getString("tag") == null) throw new IllegalArgumentException("args invalid");
+ Validate.isTrue(args != null && args.getString("tag") != null, "args invalid");
tag = new GpodnetTag(args.getString("tag"));
((MainActivity) getActivity()).getMainActivtyActionBar().setTitle(tag.getName());
diff --git a/src/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java b/src/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java
index 880726e50..2289862aa 100644
--- a/src/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java
+++ b/src/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java
@@ -11,16 +11,17 @@ import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.fragment.SearchFragment;
import de.danoeh.antennapod.gpoddernet.GpodnetService;
import de.danoeh.antennapod.gpoddernet.GpodnetServiceException;
import de.danoeh.antennapod.gpoddernet.model.GpodnetTag;
import de.danoeh.antennapod.util.menuhandler.MenuItemUtils;
-
-import java.util.ArrayList;
-import java.util.List;
+import de.danoeh.antennapod.util.menuhandler.NavDrawerActivity;
public class TagListFragment extends ListFragment {
private static final String TAG = "TagListFragment";
@@ -35,22 +36,24 @@ public class TagListFragment extends ListFragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
- final SearchView sv = new SearchView(getActivity());
- MenuItemUtils.addSearchItem(menu, sv);
- sv.setQueryHint(getString(R.string.gpodnet_search_hint));
- sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String s) {
- sv.clearFocus();
- ((MainActivity) getActivity()).loadChildFragment(SearchListFragment.newInstance(s));
- return true;
- }
+ if (!MenuItemUtils.isActivityDrawerOpen((NavDrawerActivity) getActivity())) {
+ final SearchView sv = new SearchView(getActivity());
+ MenuItemUtils.addSearchItem(menu, sv);
+ sv.setQueryHint(getString(R.string.gpodnet_search_hint));
+ sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ sv.clearFocus();
+ ((MainActivity) getActivity()).loadChildFragment(SearchListFragment.newInstance(s));
+ return true;
+ }
- @Override
- public boolean onQueryTextChange(String s) {
- return false;
- }
- });
+ @Override
+ public boolean onQueryTextChange(String s) {
+ return false;
+ }
+ });
+ }
}
@Override
@@ -66,11 +69,26 @@ public class TagListFragment extends ListFragment {
}
});
- loadData();
+ startLoadTask();
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ cancelLoadTask();
+ }
+
+ private AsyncTask<Void, Void, List<GpodnetTag>> loadTask;
+
+ private void cancelLoadTask() {
+ if (loadTask != null && !loadTask.isCancelled()) {
+ loadTask.cancel(true);
+ }
}
- private void loadData() {
- AsyncTask<Void, Void, List<GpodnetTag>> task = new AsyncTask<Void, Void, List<GpodnetTag>>() {
+ private void startLoadTask() {
+ cancelLoadTask();
+ loadTask = new AsyncTask<Void, Void, List<GpodnetTag>>() {
private Exception exception;
@Override
@@ -115,9 +133,9 @@ public class TagListFragment extends ListFragment {
}
};
if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
- task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ loadTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} else {
- task.execute();
+ loadTask.execute();
}
}
}
diff --git a/src/de/danoeh/antennapod/gpoddernet/GpodnetService.java b/src/de/danoeh/antennapod/gpoddernet/GpodnetService.java
index a0c5b534c..038b2a367 100644
--- a/src/de/danoeh/antennapod/gpoddernet/GpodnetService.java
+++ b/src/de/danoeh/antennapod/gpoddernet/GpodnetService.java
@@ -1,8 +1,6 @@
package de.danoeh.antennapod.gpoddernet;
-import de.danoeh.antennapod.gpoddernet.model.*;
-import de.danoeh.antennapod.preferences.GpodnetPreferences;
-import de.danoeh.antennapod.service.download.AntennapodHttpClient;
+import org.apache.commons.lang3.Validate;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
@@ -32,6 +30,14 @@ import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
+import de.danoeh.antennapod.gpoddernet.model.GpodnetDevice;
+import de.danoeh.antennapod.gpoddernet.model.GpodnetPodcast;
+import de.danoeh.antennapod.gpoddernet.model.GpodnetSubscriptionChange;
+import de.danoeh.antennapod.gpoddernet.model.GpodnetTag;
+import de.danoeh.antennapod.gpoddernet.model.GpodnetUploadChangesResponse;
+import de.danoeh.antennapod.preferences.GpodnetPreferences;
+import de.danoeh.antennapod.service.download.AntennapodHttpClient;
+
/**
* Communicates with the gpodder.net service.
*/
@@ -89,10 +95,8 @@ public class GpodnetService {
*/
public List<GpodnetPodcast> getPodcastsForTag(GpodnetTag tag, int count)
throws GpodnetServiceException {
- if (tag == null) {
- throw new IllegalArgumentException(
- "Tag and title of tag must not be null");
- }
+ Validate.notNull(tag);
+
try {
URI uri = new URI(BASE_SCHEME, BASE_HOST, String.format(
"/api/2/tag/%s/%d.json", tag.getName(), count), null);
@@ -120,9 +124,8 @@ public class GpodnetService {
*/
public List<GpodnetPodcast> getPodcastToplist(int count)
throws GpodnetServiceException {
- if (count < 1 || count > 100) {
- throw new IllegalArgumentException("Count must be in range 1..100");
- }
+ Validate.isTrue(count >= 1 && count <= 100, "Count must be in range 1..100");
+
try {
URI uri = new URI(BASE_SCHEME, BASE_HOST, String.format(
"/toplist/%d.json", count), null);
@@ -155,9 +158,8 @@ public class GpodnetService {
* @throws GpodnetServiceAuthenticationException If there is an authentication error.
*/
public List<GpodnetPodcast> getSuggestions(int count) throws GpodnetServiceException {
- if (count < 1 || count > 100) {
- throw new IllegalArgumentException("Count must be in range 1..100");
- }
+ Validate.isTrue(count >= 1 && count <= 100, "Count must be in range 1..100");
+
try {
URI uri = new URI(BASE_SCHEME, BASE_HOST, String.format(
"/suggestions/%d.json", count), null);
@@ -220,9 +222,8 @@ public class GpodnetService {
*/
public List<GpodnetDevice> getDevices(String username)
throws GpodnetServiceException {
- if (username == null) {
- throw new IllegalArgumentException("Username must not be null");
- }
+ Validate.notNull(username);
+
try {
URI uri = new URI(BASE_SCHEME, BASE_HOST, String.format(
"/api/2/devices/%s.json", username), null);
@@ -255,10 +256,9 @@ public class GpodnetService {
public void configureDevice(String username, String deviceId,
String caption, GpodnetDevice.DeviceType type)
throws GpodnetServiceException {
- if (username == null || deviceId == null) {
- throw new IllegalArgumentException(
- "Username and device ID must not be null");
- }
+ Validate.notNull(username);
+ Validate.notNull(deviceId);
+
try {
URI uri = new URI(BASE_SCHEME, BASE_HOST, String.format(
"/api/2/devices/%s/%s.json", username, deviceId), null);
@@ -303,10 +303,9 @@ public class GpodnetService {
*/
public String getSubscriptionsOfDevice(String username, String deviceId)
throws GpodnetServiceException {
- if (username == null || deviceId == null) {
- throw new IllegalArgumentException(
- "Username and device ID must not be null");
- }
+ Validate.notNull(username);
+ Validate.notNull(deviceId);
+
try {
URI uri = new URI(BASE_SCHEME, BASE_HOST, String.format(
"/subscriptions/%s/%s.opml", username, deviceId), null);
@@ -332,9 +331,8 @@ public class GpodnetService {
*/
public String getSubscriptionsOfUser(String username)
throws GpodnetServiceException {
- if (username == null) {
- throw new IllegalArgumentException("Username must not be null");
- }
+ Validate.notNull(username);
+
try {
URI uri = new URI(BASE_SCHEME, BASE_HOST, String.format(
"/subscriptions/%s.opml", username), null);
@@ -406,10 +404,11 @@ public class GpodnetService {
*/
public GpodnetUploadChangesResponse uploadChanges(String username, String deviceId, Collection<String> added,
Collection<String> removed) throws GpodnetServiceException {
- if (username == null || deviceId == null || added == null || removed == null) {
- throw new IllegalArgumentException(
- "Username, device ID, added and removed must not be null");
- }
+ Validate.notNull(username);
+ Validate.notNull(deviceId);
+ Validate.notNull(added);
+ Validate.notNull(removed);
+
try {
URI uri = new URI(BASE_SCHEME, BASE_HOST, String.format(
"/api/2/subscriptions/%s/%s.json", username, deviceId), null);
@@ -453,10 +452,9 @@ public class GpodnetService {
*/
public GpodnetSubscriptionChange getSubscriptionChanges(String username,
String deviceId, long timestamp) throws GpodnetServiceException {
- if (username == null || deviceId == null) {
- throw new IllegalArgumentException(
- "Username and device ID must not be null");
- }
+ Validate.notNull(username);
+ Validate.notNull(deviceId);
+
String params = String.format("since=%d", timestamp);
String path = String.format("/api/2/subscriptions/%s/%s.json",
username, deviceId);
@@ -486,10 +484,9 @@ public class GpodnetService {
*/
public void authenticate(String username, String password)
throws GpodnetServiceException {
- if (username == null || password == null) {
- throw new IllegalArgumentException(
- "Username and password must not be null");
- }
+ Validate.notNull(username);
+ Validate.notNull(password);
+
URI uri;
try {
uri = new URI(BASE_SCHEME, BASE_HOST, String.format(
@@ -517,9 +514,8 @@ public class GpodnetService {
private String executeRequest(HttpRequestBase request)
throws GpodnetServiceException {
- if (request == null) {
- throw new IllegalArgumentException("request must not be null");
- }
+ Validate.notNull(request);
+
String responseString = null;
HttpResponse response = null;
try {
@@ -586,9 +582,8 @@ public class GpodnetService {
private String getStringFromEntity(HttpEntity entity)
throws GpodnetServiceException {
- if (entity == null) {
- throw new IllegalArgumentException("entity must not be null");
- }
+ Validate.notNull(entity);
+
ByteArrayOutputStream outputStream;
int contentLength = (int) entity.getContentLength();
if (contentLength > 0) {
@@ -613,9 +608,7 @@ public class GpodnetService {
private void checkStatusCode(HttpResponse response)
throws GpodnetServiceException {
- if (response == null) {
- throw new IllegalArgumentException("response must not be null");
- }
+ Validate.notNull(response);
int responseCode = response.getStatusLine().getStatusCode();
if (responseCode != HttpStatus.SC_OK) {
if (responseCode == HttpStatus.SC_UNAUTHORIZED) {
@@ -629,9 +622,8 @@ public class GpodnetService {
private List<GpodnetPodcast> readPodcastListFromJSONArray(JSONArray array)
throws JSONException {
- if (array == null) {
- throw new IllegalArgumentException("array must not be null");
- }
+ Validate.notNull(array);
+
List<GpodnetPodcast> result = new ArrayList<GpodnetPodcast>(
array.length());
for (int i = 0; i < array.length(); i++) {
@@ -685,9 +677,8 @@ public class GpodnetService {
private List<GpodnetDevice> readDeviceListFromJSONArray(JSONArray array)
throws JSONException {
- if (array == null) {
- throw new IllegalArgumentException("array must not be null");
- }
+ Validate.notNull(array);
+
List<GpodnetDevice> result = new ArrayList<GpodnetDevice>(
array.length());
for (int i = 0; i < array.length(); i++) {
@@ -707,9 +698,8 @@ public class GpodnetService {
private GpodnetSubscriptionChange readSubscriptionChangesFromJSONObject(
JSONObject object) throws JSONException {
- if (object == null) {
- throw new IllegalArgumentException("object must not be null");
- }
+ Validate.notNull(object);
+
List<String> added = new LinkedList<String>();
JSONArray jsonAdded = object.getJSONArray("add");
for (int i = 0; i < jsonAdded.length(); i++) {
diff --git a/src/de/danoeh/antennapod/gpoddernet/model/GpodnetDevice.java b/src/de/danoeh/antennapod/gpoddernet/model/GpodnetDevice.java
index ae7199fcc..86a2171fa 100644
--- a/src/de/danoeh/antennapod/gpoddernet/model/GpodnetDevice.java
+++ b/src/de/danoeh/antennapod/gpoddernet/model/GpodnetDevice.java
@@ -1,5 +1,7 @@
package de.danoeh.antennapod.gpoddernet.model;
+import org.apache.commons.lang3.Validate;
+
public class GpodnetDevice {
private String id;
@@ -9,9 +11,7 @@ public class GpodnetDevice {
public GpodnetDevice(String id, String caption, String type,
int subscriptions) {
- if (id == null) {
- throw new IllegalArgumentException("ID must not be null");
- }
+ Validate.notNull(id);
this.id = id;
this.caption = caption;
diff --git a/src/de/danoeh/antennapod/gpoddernet/model/GpodnetPodcast.java b/src/de/danoeh/antennapod/gpoddernet/model/GpodnetPodcast.java
index aa01b66e2..b002035c9 100644
--- a/src/de/danoeh/antennapod/gpoddernet/model/GpodnetPodcast.java
+++ b/src/de/danoeh/antennapod/gpoddernet/model/GpodnetPodcast.java
@@ -1,5 +1,7 @@
package de.danoeh.antennapod.gpoddernet.model;
+import org.apache.commons.lang3.Validate;
+
public class GpodnetPodcast {
private String url;
private String title;
@@ -11,10 +13,9 @@ public class GpodnetPodcast {
public GpodnetPodcast(String url, String title, String description,
int subscribers, String logoUrl, String website, String mygpoLink) {
- if (url == null || title == null || description == null) {
- throw new IllegalArgumentException(
- "URL, title and description must not be null");
- }
+ Validate.notNull(url);
+ Validate.notNull(title);
+ Validate.notNull(description);
this.url = url;
this.title = title;
diff --git a/src/de/danoeh/antennapod/gpoddernet/model/GpodnetSubscriptionChange.java b/src/de/danoeh/antennapod/gpoddernet/model/GpodnetSubscriptionChange.java
index dccb53e5d..a4617118d 100644
--- a/src/de/danoeh/antennapod/gpoddernet/model/GpodnetSubscriptionChange.java
+++ b/src/de/danoeh/antennapod/gpoddernet/model/GpodnetSubscriptionChange.java
@@ -1,5 +1,7 @@
package de.danoeh.antennapod.gpoddernet.model;
+import org.apache.commons.lang3.Validate;
+
import java.util.List;
public class GpodnetSubscriptionChange {
@@ -9,10 +11,9 @@ public class GpodnetSubscriptionChange {
public GpodnetSubscriptionChange(List<String> added, List<String> removed,
long timestamp) {
- if (added == null || removed == null) {
- throw new IllegalArgumentException(
- "added and remove must not be null");
- }
+ Validate.notNull(added);
+ Validate.notNull(removed);
+
this.added = added;
this.removed = removed;
this.timestamp = timestamp;
diff --git a/src/de/danoeh/antennapod/gpoddernet/model/GpodnetTag.java b/src/de/danoeh/antennapod/gpoddernet/model/GpodnetTag.java
index e8a36a554..80b84095e 100644
--- a/src/de/danoeh/antennapod/gpoddernet/model/GpodnetTag.java
+++ b/src/de/danoeh/antennapod/gpoddernet/model/GpodnetTag.java
@@ -1,5 +1,7 @@
package de.danoeh.antennapod.gpoddernet.model;
+import org.apache.commons.lang3.Validate;
+
import java.util.Comparator;
public class GpodnetTag {
@@ -8,9 +10,7 @@ public class GpodnetTag {
private int usage;
public GpodnetTag(String name, int usage) {
- if (name == null) {
- throw new IllegalArgumentException("Name must not be null");
- }
+ Validate.notNull(name);
this.name = name;
this.usage = usage;
diff --git a/src/de/danoeh/antennapod/preferences/PlaybackPreferences.java b/src/de/danoeh/antennapod/preferences/PlaybackPreferences.java
index 80e0768dd..1d1ab052f 100644
--- a/src/de/danoeh/antennapod/preferences/PlaybackPreferences.java
+++ b/src/de/danoeh/antennapod/preferences/PlaybackPreferences.java
@@ -4,6 +4,9 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
+
+import org.apache.commons.lang3.Validate;
+
import de.danoeh.antennapod.BuildConfig;
/**
@@ -66,8 +69,8 @@ public class PlaybackPreferences implements
public static void createInstance(Context context) {
if (BuildConfig.DEBUG)
Log.d(TAG, "Creating new instance of UserPreferences");
- if (context == null)
- throw new IllegalArgumentException("Context must not be null");
+ Validate.notNull(context);
+
instance = new PlaybackPreferences(context);
PreferenceManager.getDefaultSharedPreferences(context)
diff --git a/src/de/danoeh/antennapod/preferences/UserPreferences.java b/src/de/danoeh/antennapod/preferences/UserPreferences.java
index 31250bcd9..2020ddfae 100644
--- a/src/de/danoeh/antennapod/preferences/UserPreferences.java
+++ b/src/de/danoeh/antennapod/preferences/UserPreferences.java
@@ -7,11 +7,9 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
-import de.danoeh.antennapod.BuildConfig;
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.OpmlImportFromPathActivity;
-import de.danoeh.antennapod.receiver.FeedUpdateReceiver;
+
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Validate;
import org.json.JSONArray;
import org.json.JSONException;
@@ -21,6 +19,11 @@ import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;
+import de.danoeh.antennapod.BuildConfig;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.OpmlImportFromPathActivity;
+import de.danoeh.antennapod.receiver.FeedUpdateReceiver;
+
/**
* Provides access to preferences set by the user in the settings screen. A
* private instance of this class must first be instantiated via
@@ -28,347 +31,372 @@ import java.util.concurrent.TimeUnit;
* when called.
*/
public class UserPreferences implements
- SharedPreferences.OnSharedPreferenceChangeListener {
- private static final String TAG = "UserPreferences";
-
- public static final String PREF_PAUSE_ON_HEADSET_DISCONNECT = "prefPauseOnHeadsetDisconnect";
- public static final String PREF_FOLLOW_QUEUE = "prefFollowQueue";
- public static final String PREF_DOWNLOAD_MEDIA_ON_WIFI_ONLY = "prefDownloadMediaOnWifiOnly";
- public static final String PREF_UPDATE_INTERVAL = "prefAutoUpdateIntervall";
- 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_AUTO_FLATTR = "pref_auto_flattr";
- public static final String PREF_THEME = "prefTheme";
- public static final String PREF_DATA_FOLDER = "prefDataFolder";
- public static final String PREF_ENABLE_AUTODL = "prefEnableAutoDl";
- public static final String PREF_ENABLE_AUTODL_WIFI_FILTER = "prefEnableAutoDownloadWifiFilter";
- private static final String PREF_AUTODL_SELECTED_NETWORKS = "prefAutodownloadSelectedNetworks";
- public static final String PREF_EPISODE_CACHE_SIZE = "prefEpisodeCacheSize";
- 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";
+ SharedPreferences.OnSharedPreferenceChangeListener {
+ private static final String TAG = "UserPreferences";
+
+ public static final String PREF_PAUSE_ON_HEADSET_DISCONNECT = "prefPauseOnHeadsetDisconnect";
+ public static final String PREF_FOLLOW_QUEUE = "prefFollowQueue";
+ public static final String PREF_DOWNLOAD_MEDIA_ON_WIFI_ONLY = "prefDownloadMediaOnWifiOnly";
+ public static final String PREF_UPDATE_INTERVAL = "prefAutoUpdateIntervall";
+ 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_AUTO_FLATTR = "pref_auto_flattr";
+ public static final String PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD = "prefAutoFlattrPlayedDurationThreshold";
+ public static final String PREF_THEME = "prefTheme";
+ public static final String PREF_DATA_FOLDER = "prefDataFolder";
+ public static final String PREF_ENABLE_AUTODL = "prefEnableAutoDl";
+ public static final String PREF_ENABLE_AUTODL_WIFI_FILTER = "prefEnableAutoDownloadWifiFilter";
+ private static final String PREF_AUTODL_SELECTED_NETWORKS = "prefAutodownloadSelectedNetworks";
+ public static final String PREF_EPISODE_CACHE_SIZE = "prefEpisodeCacheSize";
+ 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";
// TODO: Make this value configurable
- private static final double PLAYED_DURATION_AUTOFLATTR_THRESHOLD = 0.8;
-
- private static int EPISODE_CACHE_SIZE_UNLIMITED = -1;
-
- private static UserPreferences instance;
- private final Context context;
-
- // Preferences
- private boolean pauseOnHeadsetDisconnect;
- private boolean followQueue;
- private boolean downloadMediaOnWifiOnly;
- private long updateInterval;
- private boolean allowMobileUpdate;
- private boolean displayOnlyEpisodes;
- private boolean autoDelete;
- private boolean autoFlattr;
- private int theme;
- private boolean enableAutodownload;
- private boolean enableAutodownloadWifiFilter;
- private String[] autodownloadSelectedNetworks;
- private int episodeCacheSize;
- private String playbackSpeed;
- private String[] playbackSpeedArray;
- private boolean pauseForFocusLoss;
- private boolean isFreshInstall;
-
- private UserPreferences(Context context) {
- this.context = context;
- loadPreferences();
- }
-
- /**
- * Sets up the UserPreferences class.
- *
- * @throws IllegalArgumentException
- * if context is null
- * */
- public static void createInstance(Context context) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Creating new instance of UserPreferences");
- if (context == null)
- throw new IllegalArgumentException("Context must not be null");
- instance = new UserPreferences(context);
-
- createImportDirectory();
- createNoMediaFile();
- PreferenceManager.getDefaultSharedPreferences(context)
- .registerOnSharedPreferenceChangeListener(instance);
-
- }
-
- private void loadPreferences() {
- SharedPreferences sp = PreferenceManager
- .getDefaultSharedPreferences(context);
- EPISODE_CACHE_SIZE_UNLIMITED = context.getResources().getInteger(
- R.integer.episode_cache_size_unlimited);
- pauseOnHeadsetDisconnect = sp.getBoolean(
- PREF_PAUSE_ON_HEADSET_DISCONNECT, true);
- followQueue = sp.getBoolean(PREF_FOLLOW_QUEUE, false);
- downloadMediaOnWifiOnly = sp.getBoolean(
- PREF_DOWNLOAD_MEDIA_ON_WIFI_ONLY, true);
- updateInterval = readUpdateInterval(sp.getString(PREF_UPDATE_INTERVAL,
- "0"));
- allowMobileUpdate = sp.getBoolean(PREF_MOBILE_UPDATE, false);
- displayOnlyEpisodes = sp.getBoolean(PREF_DISPLAY_ONLY_EPISODES, false);
- autoDelete = sp.getBoolean(PREF_AUTO_DELETE, false);
- autoFlattr = sp.getBoolean(PREF_AUTO_FLATTR, false);
- theme = readThemeValue(sp.getString(PREF_THEME, "0"));
- enableAutodownloadWifiFilter = sp.getBoolean(
- PREF_ENABLE_AUTODL_WIFI_FILTER, false);
- autodownloadSelectedNetworks = StringUtils.split(
- sp.getString(PREF_AUTODL_SELECTED_NETWORKS, ""), ',');
- episodeCacheSize = readEpisodeCacheSizeInternal(sp.getString(
- PREF_EPISODE_CACHE_SIZE, "20"));
- enableAutodownload = sp.getBoolean(PREF_ENABLE_AUTODL, false);
- playbackSpeed = sp.getString(PREF_PLAYBACK_SPEED, "1.0");
- playbackSpeedArray = readPlaybackSpeedArray(sp.getString(
- PREF_PLAYBACK_SPEED_ARRAY, null));
- pauseForFocusLoss = sp.getBoolean(PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS, false);
- }
-
- private int readThemeValue(String valueFromPrefs) {
- switch (Integer.parseInt(valueFromPrefs)) {
- case 0:
- return R.style.Theme_AntennaPod_Light;
- case 1:
- return R.style.Theme_AntennaPod_Dark;
- default:
- return R.style.Theme_AntennaPod_Light;
- }
- }
-
- private long readUpdateInterval(String valueFromPrefs) {
- int hours = Integer.parseInt(valueFromPrefs);
- return TimeUnit.HOURS.toMillis(hours);
- }
-
- private int readEpisodeCacheSizeInternal(String valueFromPrefs) {
- if (valueFromPrefs.equals(context
- .getString(R.string.pref_episode_cache_unlimited))) {
- return EPISODE_CACHE_SIZE_UNLIMITED;
- } else {
- return Integer.valueOf(valueFromPrefs);
- }
- }
-
- private String[] readPlaybackSpeedArray(String valueFromPrefs) {
- String[] selectedSpeeds = null;
- // If this preference hasn't been set yet, return the default options
- if (valueFromPrefs == null) {
- String[] allSpeeds = context.getResources().getStringArray(
- R.array.playback_speed_values);
- List<String> speedList = new LinkedList<String>();
- for (String speedStr : allSpeeds) {
- float speed = Float.parseFloat(speedStr);
- if (speed < 2.0001 && speed * 10 % 1 == 0) {
- speedList.add(speedStr);
- }
- }
- selectedSpeeds = speedList.toArray(new String[speedList.size()]);
- } else {
- try {
- JSONArray jsonArray = new JSONArray(valueFromPrefs);
- selectedSpeeds = new String[jsonArray.length()];
- for (int i = 0; i < jsonArray.length(); i++) {
- selectedSpeeds[i] = jsonArray.getString(i);
- }
- } catch (JSONException e) {
- Log.e(TAG,
- "Got JSON error when trying to get speeds from JSONArray");
- e.printStackTrace();
- }
- }
- return selectedSpeeds;
- }
-
- private static void instanceAvailable() {
- if (instance == null) {
- throw new IllegalStateException(
- "UserPreferences was used before being set up");
- }
- }
-
- public static boolean isPauseOnHeadsetDisconnect() {
- instanceAvailable();
- return instance.pauseOnHeadsetDisconnect;
- }
-
- public static boolean isFollowQueue() {
- instanceAvailable();
- return instance.followQueue;
- }
-
- public static boolean isDownloadMediaOnWifiOnly() {
- instanceAvailable();
- return instance.downloadMediaOnWifiOnly;
- }
-
- public static long getUpdateInterval() {
- instanceAvailable();
- return instance.updateInterval;
- }
-
- public static boolean isAllowMobileUpdate() {
- instanceAvailable();
- return instance.allowMobileUpdate;
- }
-
- public static boolean isDisplayOnlyEpisodes() {
- instanceAvailable();
- //return instance.displayOnlyEpisodes;
+ private static final float PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD_DEFAULT = 0.8f;
+
+ private static int EPISODE_CACHE_SIZE_UNLIMITED = -1;
+
+ private static UserPreferences instance;
+ private final Context context;
+
+ // Preferences
+ private boolean pauseOnHeadsetDisconnect;
+ private boolean followQueue;
+ private boolean downloadMediaOnWifiOnly;
+ private long updateInterval;
+ private boolean allowMobileUpdate;
+ private boolean displayOnlyEpisodes;
+ private boolean autoDelete;
+ private boolean autoFlattr;
+ private float autoFlattrPlayedDurationThreshold;
+ private int theme;
+ private boolean enableAutodownload;
+ private boolean enableAutodownloadWifiFilter;
+ private String[] autodownloadSelectedNetworks;
+ private int episodeCacheSize;
+ private String playbackSpeed;
+ private String[] playbackSpeedArray;
+ private boolean pauseForFocusLoss;
+ private int seekDeltaSecs;
+ private boolean isFreshInstall;
+
+ private UserPreferences(Context context) {
+ this.context = context;
+ loadPreferences();
+ }
+
+ /**
+ * Sets up the UserPreferences class.
+ *
+ * @throws IllegalArgumentException if context is null
+ */
+ public static void createInstance(Context context) {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Creating new instance of UserPreferences");
+ Validate.notNull(context);
+
+ instance = new UserPreferences(context);
+
+ createImportDirectory();
+ createNoMediaFile();
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .registerOnSharedPreferenceChangeListener(instance);
+
+ }
+
+ private void loadPreferences() {
+ SharedPreferences sp = PreferenceManager
+ .getDefaultSharedPreferences(context);
+ EPISODE_CACHE_SIZE_UNLIMITED = context.getResources().getInteger(
+ R.integer.episode_cache_size_unlimited);
+ pauseOnHeadsetDisconnect = sp.getBoolean(
+ PREF_PAUSE_ON_HEADSET_DISCONNECT, true);
+ followQueue = sp.getBoolean(PREF_FOLLOW_QUEUE, false);
+ downloadMediaOnWifiOnly = sp.getBoolean(
+ PREF_DOWNLOAD_MEDIA_ON_WIFI_ONLY, true);
+ updateInterval = readUpdateInterval(sp.getString(PREF_UPDATE_INTERVAL,
+ "0"));
+ allowMobileUpdate = sp.getBoolean(PREF_MOBILE_UPDATE, false);
+ displayOnlyEpisodes = sp.getBoolean(PREF_DISPLAY_ONLY_EPISODES, false);
+ autoDelete = sp.getBoolean(PREF_AUTO_DELETE, false);
+ autoFlattr = sp.getBoolean(PREF_AUTO_FLATTR, false);
+ autoFlattrPlayedDurationThreshold = sp.getFloat(PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD,
+ PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD_DEFAULT);
+ theme = readThemeValue(sp.getString(PREF_THEME, "0"));
+ enableAutodownloadWifiFilter = sp.getBoolean(
+ PREF_ENABLE_AUTODL_WIFI_FILTER, false);
+ autodownloadSelectedNetworks = StringUtils.split(
+ sp.getString(PREF_AUTODL_SELECTED_NETWORKS, ""), ',');
+ episodeCacheSize = readEpisodeCacheSizeInternal(sp.getString(
+ PREF_EPISODE_CACHE_SIZE, "20"));
+ enableAutodownload = sp.getBoolean(PREF_ENABLE_AUTODL, false);
+ playbackSpeed = sp.getString(PREF_PLAYBACK_SPEED, "1.0");
+ 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"));
+ }
+
+ private int readThemeValue(String valueFromPrefs) {
+ switch (Integer.parseInt(valueFromPrefs)) {
+ case 0:
+ return R.style.Theme_AntennaPod_Light;
+ case 1:
+ return R.style.Theme_AntennaPod_Dark;
+ default:
+ return R.style.Theme_AntennaPod_Light;
+ }
+ }
+
+ private long readUpdateInterval(String valueFromPrefs) {
+ int hours = Integer.parseInt(valueFromPrefs);
+ return TimeUnit.HOURS.toMillis(hours);
+ }
+
+ private int readEpisodeCacheSizeInternal(String valueFromPrefs) {
+ if (valueFromPrefs.equals(context
+ .getString(R.string.pref_episode_cache_unlimited))) {
+ return EPISODE_CACHE_SIZE_UNLIMITED;
+ } else {
+ return Integer.valueOf(valueFromPrefs);
+ }
+ }
+
+ private String[] readPlaybackSpeedArray(String valueFromPrefs) {
+ String[] selectedSpeeds = null;
+ // If this preference hasn't been set yet, return the default options
+ if (valueFromPrefs == null) {
+ String[] allSpeeds = context.getResources().getStringArray(
+ R.array.playback_speed_values);
+ List<String> speedList = new LinkedList<String>();
+ for (String speedStr : allSpeeds) {
+ float speed = Float.parseFloat(speedStr);
+ if (speed < 2.0001 && speed * 10 % 1 == 0) {
+ speedList.add(speedStr);
+ }
+ }
+ selectedSpeeds = speedList.toArray(new String[speedList.size()]);
+ } else {
+ try {
+ JSONArray jsonArray = new JSONArray(valueFromPrefs);
+ selectedSpeeds = new String[jsonArray.length()];
+ for (int i = 0; i < jsonArray.length(); i++) {
+ selectedSpeeds[i] = jsonArray.getString(i);
+ }
+ } catch (JSONException e) {
+ Log.e(TAG,
+ "Got JSON error when trying to get speeds from JSONArray");
+ e.printStackTrace();
+ }
+ }
+ return selectedSpeeds;
+ }
+
+ private static void instanceAvailable() {
+ if (instance == null) {
+ throw new IllegalStateException(
+ "UserPreferences was used before being set up");
+ }
+ }
+
+ public static boolean isPauseOnHeadsetDisconnect() {
+ instanceAvailable();
+ return instance.pauseOnHeadsetDisconnect;
+ }
+
+ public static boolean isFollowQueue() {
+ instanceAvailable();
+ return instance.followQueue;
+ }
+
+ public static boolean isDownloadMediaOnWifiOnly() {
+ instanceAvailable();
+ return instance.downloadMediaOnWifiOnly;
+ }
+
+ public static long getUpdateInterval() {
+ instanceAvailable();
+ return instance.updateInterval;
+ }
+
+ public static boolean isAllowMobileUpdate() {
+ instanceAvailable();
+ return instance.allowMobileUpdate;
+ }
+
+ public static boolean isDisplayOnlyEpisodes() {
+ instanceAvailable();
+ //return instance.displayOnlyEpisodes;
return false;
- }
-
- public static boolean isAutoDelete() {
- instanceAvailable();
- return instance.autoDelete;
- }
-
- public static boolean isAutoFlattr() {
- instanceAvailable();
- return instance.autoFlattr;
- }
-
- public static int getTheme() {
- instanceAvailable();
- return instance.theme;
- }
-
- public static boolean isEnableAutodownloadWifiFilter() {
- instanceAvailable();
- return instance.enableAutodownloadWifiFilter;
- }
-
- public static String[] getAutodownloadSelectedNetworks() {
- instanceAvailable();
- return instance.autodownloadSelectedNetworks;
- }
-
- public static int getEpisodeCacheSizeUnlimited() {
- return EPISODE_CACHE_SIZE_UNLIMITED;
- }
-
- public static String getPlaybackSpeed() {
- instanceAvailable();
- return instance.playbackSpeed;
- }
-
- public static String[] getPlaybackSpeedArray() {
- instanceAvailable();
- return instance.playbackSpeedArray;
- }
-
- /**
- * Returns the capacity of the episode cache. This method will return the
- * negative integer EPISODE_CACHE_SIZE_UNLIMITED if the cache size is set to
- * 'unlimited'.
- */
- public static int getEpisodeCacheSize() {
- instanceAvailable();
- return instance.episodeCacheSize;
- }
-
- public static boolean isEnableAutodownload() {
- instanceAvailable();
- return instance.enableAutodownload;
- }
-
- public static boolean shouldPauseForFocusLoss() {
- instanceAvailable();
- return instance.pauseForFocusLoss;
- }
-
- public static boolean isFreshInstall() {
- instanceAvailable();
- return instance.isFreshInstall;
- }
-
- @Override
- public void onSharedPreferenceChanged(SharedPreferences sp, String key) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Registered change of user preferences. Key: " + key);
-
- if (key.equals(PREF_DOWNLOAD_MEDIA_ON_WIFI_ONLY)) {
- downloadMediaOnWifiOnly = sp.getBoolean(
- PREF_DOWNLOAD_MEDIA_ON_WIFI_ONLY, true);
-
- } else if (key.equals(PREF_MOBILE_UPDATE)) {
- allowMobileUpdate = sp.getBoolean(PREF_MOBILE_UPDATE, false);
-
- } else if (key.equals(PREF_FOLLOW_QUEUE)) {
- followQueue = sp.getBoolean(PREF_FOLLOW_QUEUE, false);
-
- } else if (key.equals(PREF_UPDATE_INTERVAL)) {
- updateInterval = readUpdateInterval(sp.getString(
- PREF_UPDATE_INTERVAL, "0"));
- restartUpdateAlarm(updateInterval);
-
- } else if (key.equals(PREF_AUTO_DELETE)) {
- autoDelete = sp.getBoolean(PREF_AUTO_DELETE, false);
-
- } else if (key.equals(PREF_AUTO_FLATTR)) {
- autoFlattr = sp.getBoolean(PREF_AUTO_FLATTR, false);
- } else if (key.equals(PREF_DISPLAY_ONLY_EPISODES)) {
- displayOnlyEpisodes = sp.getBoolean(PREF_DISPLAY_ONLY_EPISODES,
- false);
- } else if (key.equals(PREF_THEME)) {
- theme = readThemeValue(sp.getString(PREF_THEME, ""));
- } else if (key.equals(PREF_ENABLE_AUTODL_WIFI_FILTER)) {
- enableAutodownloadWifiFilter = sp.getBoolean(
- PREF_ENABLE_AUTODL_WIFI_FILTER, false);
- } else if (key.equals(PREF_AUTODL_SELECTED_NETWORKS)) {
- autodownloadSelectedNetworks = StringUtils.split(
- sp.getString(PREF_AUTODL_SELECTED_NETWORKS, ""), ',');
- } else if (key.equals(PREF_EPISODE_CACHE_SIZE)) {
- episodeCacheSize = readEpisodeCacheSizeInternal(sp.getString(
- PREF_EPISODE_CACHE_SIZE, "20"));
- } else if (key.equals(PREF_ENABLE_AUTODL)) {
- enableAutodownload = sp.getBoolean(PREF_ENABLE_AUTODL, false);
- } else if (key.equals(PREF_PLAYBACK_SPEED)) {
- playbackSpeed = sp.getString(PREF_PLAYBACK_SPEED, "1.0");
- } else if (key.equals(PREF_PLAYBACK_SPEED_ARRAY)) {
- playbackSpeedArray = readPlaybackSpeedArray(sp.getString(
- 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_PAUSE_ON_HEADSET_DISCONNECT)) {
+ }
+
+ public static boolean isAutoDelete() {
+ instanceAvailable();
+ return instance.autoDelete;
+ }
+
+ public static boolean isAutoFlattr() {
+ instanceAvailable();
+ return instance.autoFlattr;
+ }
+
+ /**
+ * Returns the time after which an episode should be auto-flattr'd in percent of the episode's
+ * duration.
+ */
+ public static float getAutoFlattrPlayedDurationThreshold() {
+ instanceAvailable();
+ return instance.autoFlattrPlayedDurationThreshold;
+ }
+
+ public static int getTheme() {
+ instanceAvailable();
+ return instance.theme;
+ }
+
+ public static boolean isEnableAutodownloadWifiFilter() {
+ instanceAvailable();
+ return instance.enableAutodownloadWifiFilter;
+ }
+
+ public static String[] getAutodownloadSelectedNetworks() {
+ instanceAvailable();
+ return instance.autodownloadSelectedNetworks;
+ }
+
+ public static int getEpisodeCacheSizeUnlimited() {
+ return EPISODE_CACHE_SIZE_UNLIMITED;
+ }
+
+ public static String getPlaybackSpeed() {
+ instanceAvailable();
+ return instance.playbackSpeed;
+ }
+
+ public static String[] getPlaybackSpeedArray() {
+ instanceAvailable();
+ return instance.playbackSpeedArray;
+ }
+
+ public static int getSeekDeltaMs() {
+ instanceAvailable();
+ return 1000 * instance.seekDeltaSecs;
+ }
+
+ /**
+ * Returns the capacity of the episode cache. This method will return the
+ * negative integer EPISODE_CACHE_SIZE_UNLIMITED if the cache size is set to
+ * 'unlimited'.
+ */
+ public static int getEpisodeCacheSize() {
+ instanceAvailable();
+ return instance.episodeCacheSize;
+ }
+
+ public static boolean isEnableAutodownload() {
+ instanceAvailable();
+ return instance.enableAutodownload;
+ }
+
+ public static boolean shouldPauseForFocusLoss() {
+ instanceAvailable();
+ return instance.pauseForFocusLoss;
+ }
+
+ public static boolean isFreshInstall() {
+ instanceAvailable();
+ return instance.isFreshInstall;
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sp, String key) {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Registered change of user preferences. Key: " + key);
+
+ if (key.equals(PREF_DOWNLOAD_MEDIA_ON_WIFI_ONLY)) {
+ downloadMediaOnWifiOnly = sp.getBoolean(
+ PREF_DOWNLOAD_MEDIA_ON_WIFI_ONLY, true);
+
+ } else if (key.equals(PREF_MOBILE_UPDATE)) {
+ allowMobileUpdate = sp.getBoolean(PREF_MOBILE_UPDATE, false);
+
+ } else if (key.equals(PREF_FOLLOW_QUEUE)) {
+ followQueue = sp.getBoolean(PREF_FOLLOW_QUEUE, false);
+
+ } else if (key.equals(PREF_UPDATE_INTERVAL)) {
+ updateInterval = readUpdateInterval(sp.getString(
+ PREF_UPDATE_INTERVAL, "0"));
+ restartUpdateAlarm(updateInterval);
+
+ } else if (key.equals(PREF_AUTO_DELETE)) {
+ autoDelete = sp.getBoolean(PREF_AUTO_DELETE, false);
+
+ } else if (key.equals(PREF_AUTO_FLATTR)) {
+ autoFlattr = sp.getBoolean(PREF_AUTO_FLATTR, false);
+ } else if (key.equals(PREF_DISPLAY_ONLY_EPISODES)) {
+ displayOnlyEpisodes = sp.getBoolean(PREF_DISPLAY_ONLY_EPISODES,
+ false);
+ } else if (key.equals(PREF_THEME)) {
+ theme = readThemeValue(sp.getString(PREF_THEME, ""));
+ } else if (key.equals(PREF_ENABLE_AUTODL_WIFI_FILTER)) {
+ enableAutodownloadWifiFilter = sp.getBoolean(
+ PREF_ENABLE_AUTODL_WIFI_FILTER, false);
+ } else if (key.equals(PREF_AUTODL_SELECTED_NETWORKS)) {
+ autodownloadSelectedNetworks = StringUtils.split(
+ sp.getString(PREF_AUTODL_SELECTED_NETWORKS, ""), ',');
+ } else if (key.equals(PREF_EPISODE_CACHE_SIZE)) {
+ episodeCacheSize = readEpisodeCacheSizeInternal(sp.getString(
+ PREF_EPISODE_CACHE_SIZE, "20"));
+ } else if (key.equals(PREF_ENABLE_AUTODL)) {
+ enableAutodownload = sp.getBoolean(PREF_ENABLE_AUTODL, false);
+ } else if (key.equals(PREF_PLAYBACK_SPEED)) {
+ playbackSpeed = sp.getString(PREF_PLAYBACK_SPEED, "1.0");
+ } else if (key.equals(PREF_PLAYBACK_SPEED_ARRAY)) {
+ playbackSpeedArray = readPlaybackSpeedArray(sp.getString(
+ 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_PAUSE_ON_HEADSET_DISCONNECT)) {
pauseOnHeadsetDisconnect = sp.getBoolean(PREF_PAUSE_ON_HEADSET_DISCONNECT, true);
+ } else if (key.equals(PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD)) {
+ autoFlattrPlayedDurationThreshold = sp.getFloat(PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD,
+ PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD_DEFAULT);
}
- }
-
- public static void setPlaybackSpeed(String speed) {
- PreferenceManager.getDefaultSharedPreferences(instance.context).edit()
- .putString(PREF_PLAYBACK_SPEED, speed).apply();
- }
-
- public static void setPlaybackSpeedArray(String[] speeds) {
- JSONArray jsonArray = new JSONArray();
- for (String speed : speeds) {
- jsonArray.put(speed);
- }
- PreferenceManager.getDefaultSharedPreferences(instance.context).edit()
- .putString(PREF_PLAYBACK_SPEED_ARRAY, jsonArray.toString())
- .apply();
- }
-
- public static void setAutodownloadSelectedNetworks(Context context,
- String[] value) {
- SharedPreferences.Editor editor = PreferenceManager
- .getDefaultSharedPreferences(context.getApplicationContext())
- .edit();
- editor.putString(PREF_AUTODL_SELECTED_NETWORKS,
- StringUtils.join(value, ','));
- editor.commit();
- }
+ }
+
+ public static void setPlaybackSpeed(String speed) {
+ PreferenceManager.getDefaultSharedPreferences(instance.context).edit()
+ .putString(PREF_PLAYBACK_SPEED, speed).apply();
+ }
+
+ public static void setPlaybackSpeedArray(String[] speeds) {
+ JSONArray jsonArray = new JSONArray();
+ for (String speed : speeds) {
+ jsonArray.put(speed);
+ }
+ PreferenceManager.getDefaultSharedPreferences(instance.context).edit()
+ .putString(PREF_PLAYBACK_SPEED_ARRAY, jsonArray.toString())
+ .apply();
+ }
+
+ public static void setAutodownloadSelectedNetworks(Context context,
+ String[] value) {
+ SharedPreferences.Editor editor = PreferenceManager
+ .getDefaultSharedPreferences(context.getApplicationContext())
+ .edit();
+ editor.putString(PREF_AUTODL_SELECTED_NETWORKS,
+ StringUtils.join(value, ','));
+ editor.commit();
+ }
/**
- * Sets the update interval value. Should only be used for testing purposes!
- * */
+ * Sets the update interval value. Should only be used for testing purposes!
+ */
public static void setUpdateInterval(Context context, long newValue) {
instanceAvailable();
SharedPreferences.Editor editor = PreferenceManager
@@ -380,154 +408,170 @@ public class UserPreferences implements
instance.updateInterval = newValue;
}
- /**
- * Return the folder where the app stores all of its data. This method will
- * return the standard data folder if none has been set by the user.
- *
- * @param type
- * The name of the folder inside the data folder. May be null
- * when accessing the root of the data folder.
- * @return The data folder that has been requested or null if the folder
- * could not be created.
- */
- public static File getDataFolder(Context context, String type) {
- instanceAvailable();
- SharedPreferences prefs = PreferenceManager
- .getDefaultSharedPreferences(context.getApplicationContext());
- String strDir = prefs.getString(PREF_DATA_FOLDER, null);
- if (strDir == null) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Using default data folder");
- return context.getExternalFilesDir(type);
- } else {
- File dataDir = new File(strDir);
- if (!dataDir.exists()) {
- if (!dataDir.mkdir()) {
- Log.w(TAG, "Could not create data folder");
- return null;
- }
- }
-
- if (type == null) {
- return dataDir;
- } else {
- // handle path separators
- String[] dirs = type.split("/");
- for (int i = 0; i < dirs.length; i++) {
- if (dirs.length > 0) {
- if (i < dirs.length - 1) {
- dataDir = getDataFolder(context, dirs[i]);
- if (dataDir == null) {
- return null;
- }
- }
- type = dirs[i];
- }
- }
- File typeDir = new File(dataDir, type);
- if (!typeDir.exists()) {
- if (dataDir.canWrite()) {
- if (!typeDir.mkdir()) {
- Log.e(TAG, "Could not create data folder named "
- + type);
- return null;
- }
- }
- }
- return typeDir;
- }
-
- }
- }
-
- public static void setDataFolder(String dir) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Result from DirectoryChooser: " + dir);
- instanceAvailable();
- SharedPreferences prefs = PreferenceManager
- .getDefaultSharedPreferences(instance.context);
- SharedPreferences.Editor editor = prefs.edit();
- editor.putString(PREF_DATA_FOLDER, dir);
- editor.commit();
- createImportDirectory();
- }
-
- /** Create a .nomedia file to prevent scanning by the media scanner. */
- private static void createNoMediaFile() {
- File f = new File(instance.context.getExternalFilesDir(null),
- ".nomedia");
- if (!f.exists()) {
- try {
- f.createNewFile();
- } catch (IOException e) {
- Log.e(TAG, "Could not create .nomedia file");
- e.printStackTrace();
- }
- if (BuildConfig.DEBUG)
- Log.d(TAG, ".nomedia file created");
- }
- }
-
- /**
- * Creates the import directory if it doesn't exist and if storage is
- * available
- */
- private static void createImportDirectory() {
- File importDir = getDataFolder(instance.context,
- OpmlImportFromPathActivity.IMPORT_DIR);
- if (importDir != null) {
- if (importDir.exists()) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Import directory already exists");
- } else {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Creating import directory");
- importDir.mkdir();
- }
- } else {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Could not access external storage.");
- }
- }
-
- /**
- * Updates alarm registered with the AlarmManager service or deactivates it.
- *
- * @param millis
- * new value to register with AlarmManager. If millis is 0, the
- * alarm is deactivated.
- * */
- public static void restartUpdateAlarm(long millis) {
- instanceAvailable();
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Restarting update alarm. New value: " + millis);
- AlarmManager alarmManager = (AlarmManager) instance.context
- .getSystemService(Context.ALARM_SERVICE);
- PendingIntent updateIntent = PendingIntent.getBroadcast(
- instance.context, 0, new Intent(
- FeedUpdateReceiver.ACTION_REFRESH_FEEDS), 0);
- alarmManager.cancel(updateIntent);
- if (millis != 0) {
- alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, millis, millis,
- updateIntent);
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Changed alarm to new interval");
- } else {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Automatic update was deactivated");
- }
- }
-
/**
- * Reads episode cache size as it is saved in the episode_cache_size_values array.
+ * Change the auto-flattr settings
+ *
+ * @param context For accessing the shared preferences
+ * @param enabled Whether automatic flattring should be enabled at all
+ * @param autoFlattrThreshold The percentage of playback time after which an episode should be
+ * flattrd. Must be a value between 0 and 1 (inclusive)
* */
- public static int readEpisodeCacheSize(String valueFromPrefs) {
+ public static void setAutoFlattrSettings(Context context, boolean enabled, float autoFlattrThreshold) {
instanceAvailable();
- return instance.readEpisodeCacheSizeInternal(valueFromPrefs);
+ Validate.inclusiveBetween(0.0, 1.0, autoFlattrThreshold);
+ PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext())
+ .edit()
+ .putBoolean(PREF_AUTO_FLATTR, enabled)
+ .putFloat(PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD, autoFlattrThreshold)
+ .commit();
+ instance.autoFlattr = enabled;
+ instance.autoFlattrPlayedDurationThreshold = autoFlattrThreshold;
+ }
+
+ /**
+ * Return the folder where the app stores all of its data. This method will
+ * return the standard data folder if none has been set by the user.
+ *
+ * @param type The name of the folder inside the data folder. May be null
+ * when accessing the root of the data folder.
+ * @return The data folder that has been requested or null if the folder
+ * could not be created.
+ */
+ public static File getDataFolder(Context context, String type) {
+ instanceAvailable();
+ SharedPreferences prefs = PreferenceManager
+ .getDefaultSharedPreferences(context.getApplicationContext());
+ String strDir = prefs.getString(PREF_DATA_FOLDER, null);
+ if (strDir == null) {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Using default data folder");
+ return context.getExternalFilesDir(type);
+ } else {
+ File dataDir = new File(strDir);
+ if (!dataDir.exists()) {
+ if (!dataDir.mkdir()) {
+ Log.w(TAG, "Could not create data folder");
+ return null;
+ }
+ }
+
+ if (type == null) {
+ return dataDir;
+ } else {
+ // handle path separators
+ String[] dirs = type.split("/");
+ for (int i = 0; i < dirs.length; i++) {
+ if (dirs.length > 0) {
+ if (i < dirs.length - 1) {
+ dataDir = getDataFolder(context, dirs[i]);
+ if (dataDir == null) {
+ return null;
+ }
+ }
+ type = dirs[i];
+ }
+ }
+ File typeDir = new File(dataDir, type);
+ if (!typeDir.exists()) {
+ if (dataDir.canWrite()) {
+ if (!typeDir.mkdir()) {
+ Log.e(TAG, "Could not create data folder named "
+ + type);
+ return null;
+ }
+ }
+ }
+ return typeDir;
+ }
+
+ }
+ }
+
+ public static void setDataFolder(String dir) {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Result from DirectoryChooser: " + dir);
+ instanceAvailable();
+ SharedPreferences prefs = PreferenceManager
+ .getDefaultSharedPreferences(instance.context);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putString(PREF_DATA_FOLDER, dir);
+ editor.commit();
+ createImportDirectory();
}
- public static double getPlayedDurationAutoflattrThreshold() {
+ /**
+ * Create a .nomedia file to prevent scanning by the media scanner.
+ */
+ private static void createNoMediaFile() {
+ File f = new File(instance.context.getExternalFilesDir(null),
+ ".nomedia");
+ if (!f.exists()) {
+ try {
+ f.createNewFile();
+ } catch (IOException e) {
+ Log.e(TAG, "Could not create .nomedia file");
+ e.printStackTrace();
+ }
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, ".nomedia file created");
+ }
+ }
+
+ /**
+ * Creates the import directory if it doesn't exist and if storage is
+ * available
+ */
+ private static void createImportDirectory() {
+ File importDir = getDataFolder(instance.context,
+ OpmlImportFromPathActivity.IMPORT_DIR);
+ if (importDir != null) {
+ if (importDir.exists()) {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Import directory already exists");
+ } else {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Creating import directory");
+ importDir.mkdir();
+ }
+ } else {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Could not access external storage.");
+ }
+ }
+
+ /**
+ * Updates alarm registered with the AlarmManager service or deactivates it.
+ *
+ * @param millis new value to register with AlarmManager. If millis is 0, the
+ * alarm is deactivated.
+ */
+ public static void restartUpdateAlarm(long millis) {
instanceAvailable();
- return PLAYED_DURATION_AUTOFLATTR_THRESHOLD;
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Restarting update alarm. New value: " + millis);
+ AlarmManager alarmManager = (AlarmManager) instance.context
+ .getSystemService(Context.ALARM_SERVICE);
+ PendingIntent updateIntent = PendingIntent.getBroadcast(
+ instance.context, 0, new Intent(
+ FeedUpdateReceiver.ACTION_REFRESH_FEEDS), 0
+ );
+ alarmManager.cancel(updateIntent);
+ if (millis != 0) {
+ alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, millis, millis,
+ updateIntent);
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Changed alarm to new interval");
+ } else {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Automatic update was deactivated");
+ }
+ }
+
+ /**
+ * Reads episode cache size as it is saved in the episode_cache_size_values array.
+ */
+ public static int readEpisodeCacheSize(String valueFromPrefs) {
+ instanceAvailable();
+ return instance.readEpisodeCacheSizeInternal(valueFromPrefs);
}
}
diff --git a/src/de/danoeh/antennapod/receiver/AlarmUpdateReceiver.java b/src/de/danoeh/antennapod/receiver/AlarmUpdateReceiver.java
index 7b8879f21..a0539e276 100644
--- a/src/de/danoeh/antennapod/receiver/AlarmUpdateReceiver.java
+++ b/src/de/danoeh/antennapod/receiver/AlarmUpdateReceiver.java
@@ -4,6 +4,9 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
+
+import org.apache.commons.lang3.StringUtils;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.preferences.UserPreferences;
@@ -15,10 +18,10 @@ public class AlarmUpdateReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
if (BuildConfig.DEBUG)
Log.d(TAG, "Received intent");
- if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
+ if (StringUtils.equals(intent.getAction(), Intent.ACTION_BOOT_COMPLETED)) {
if (BuildConfig.DEBUG)
Log.d(TAG, "Resetting update alarm after reboot");
- } else if (intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED)) {
+ } else if (StringUtils.equals(intent.getAction(), Intent.ACTION_PACKAGE_REPLACED)) {
if (BuildConfig.DEBUG)
Log.d(TAG, "Resetting update alarm after app upgrade");
}
diff --git a/src/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java b/src/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java
index 31c053386..4dcf0b6aa 100644
--- a/src/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java
+++ b/src/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java
@@ -6,6 +6,9 @@ import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.Log;
+
+import org.apache.commons.lang3.StringUtils;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.storage.DBTasks;
import de.danoeh.antennapod.storage.DownloadRequester;
@@ -16,7 +19,7 @@ public class ConnectivityActionReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, Intent intent) {
- if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+ if (StringUtils.equals(intent.getAction(), ConnectivityManager.CONNECTIVITY_ACTION)) {
if (BuildConfig.DEBUG)
Log.d(TAG, "Received intent");
diff --git a/src/de/danoeh/antennapod/receiver/FeedUpdateReceiver.java b/src/de/danoeh/antennapod/receiver/FeedUpdateReceiver.java
index a7482fbf4..3c283a30b 100644
--- a/src/de/danoeh/antennapod/receiver/FeedUpdateReceiver.java
+++ b/src/de/danoeh/antennapod/receiver/FeedUpdateReceiver.java
@@ -6,6 +6,9 @@ import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.Log;
+
+import org.apache.commons.lang3.StringUtils;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.preferences.UserPreferences;
import de.danoeh.antennapod.storage.DBTasks;
@@ -17,7 +20,7 @@ public class FeedUpdateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(ACTION_REFRESH_FEEDS)) {
+ if (StringUtils.equals(intent.getAction(), ACTION_REFRESH_FEEDS)) {
if (BuildConfig.DEBUG)
Log.d(TAG, "Received intent");
boolean mobileUpdate = UserPreferences.isAllowMobileUpdate();
diff --git a/src/de/danoeh/antennapod/receiver/PlayerWidget.java b/src/de/danoeh/antennapod/receiver/PlayerWidget.java
index 5748ced3b..9f8892181 100644
--- a/src/de/danoeh/antennapod/receiver/PlayerWidget.java
+++ b/src/de/danoeh/antennapod/receiver/PlayerWidget.java
@@ -5,6 +5,9 @@ import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
+
+import org.apache.commons.lang3.StringUtils;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.service.playback.PlayerWidgetService;
@@ -15,9 +18,9 @@ public class PlayerWidget extends AppWidgetProvider {
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(FORCE_WIDGET_UPDATE)) {
+ if (StringUtils.equals(intent.getAction(), FORCE_WIDGET_UPDATE)) {
startUpdate(context);
- } else if (intent.getAction().equals(STOP_WIDGET_UPDATE)) {
+ } else if (StringUtils.equals(intent.getAction(), STOP_WIDGET_UPDATE)) {
stopUpdate(context);
}
diff --git a/src/de/danoeh/antennapod/service/download/DownloadRequest.java b/src/de/danoeh/antennapod/service/download/DownloadRequest.java
index 7ff9bc01c..e803d30d4 100644
--- a/src/de/danoeh/antennapod/service/download/DownloadRequest.java
+++ b/src/de/danoeh/antennapod/service/download/DownloadRequest.java
@@ -3,6 +3,8 @@ package de.danoeh.antennapod.service.download;
import android.os.Parcel;
import android.os.Parcelable;
+import org.apache.commons.lang3.Validate;
+
public class DownloadRequest implements Parcelable {
private final String destination;
@@ -21,15 +23,9 @@ public class DownloadRequest implements Parcelable {
public DownloadRequest(String destination, String source, String title,
long feedfileId, int feedfileType, String username, String password, boolean deleteOnFailure) {
- if (destination == null) {
- throw new IllegalArgumentException("Destination must not be null");
- }
- if (source == null) {
- throw new IllegalArgumentException("Source must not be null");
- }
- if (title == null) {
- throw new IllegalArgumentException("Title must not be null");
- }
+ Validate.notNull(destination);
+ Validate.notNull(source);
+ Validate.notNull(title);
this.destination = destination;
this.source = source;
@@ -110,11 +106,14 @@ public class DownloadRequest implements Parcelable {
if (size != that.size) return false;
if (soFar != that.soFar) return false;
if (statusMsg != that.statusMsg) return false;
- if (destination != null ? !destination.equals(that.destination) : that.destination != null) return false;
- if (password != null ? !password.equals(that.password) : that.password != null) return false;
+ if (destination != null ? !destination.equals(that.destination) : that.destination != null)
+ return false;
+ if (password != null ? !password.equals(that.password) : that.password != null)
+ return false;
if (source != null ? !source.equals(that.source) : that.source != null) return false;
if (title != null ? !title.equals(that.title) : that.title != null) return false;
- if (username != null ? !username.equals(that.username) : that.username != null) return false;
+ if (username != null ? !username.equals(that.username) : that.username != null)
+ return false;
return true;
}
diff --git a/src/de/danoeh/antennapod/service/download/DownloadService.java b/src/de/danoeh/antennapod/service/download/DownloadService.java
index 4f60ef8d6..63be91b57 100644
--- a/src/de/danoeh/antennapod/service/download/DownloadService.java
+++ b/src/de/danoeh/antennapod/service/download/DownloadService.java
@@ -19,30 +19,63 @@ import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import android.webkit.URLUtil;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Validate;
+import org.apache.http.HttpStatus;
+import org.xml.sax.SAXException;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CompletionService;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorCompletionService;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingDeque;
+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 java.util.concurrent.atomic.AtomicInteger;
+
+import javax.xml.parsers.ParserConfigurationException;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.DownloadAuthenticationActivity;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.NavListAdapter;
-import de.danoeh.antennapod.feed.*;
+import de.danoeh.antennapod.feed.EventDistributor;
+import de.danoeh.antennapod.feed.Feed;
+import de.danoeh.antennapod.feed.FeedImage;
+import de.danoeh.antennapod.feed.FeedItem;
+import de.danoeh.antennapod.feed.FeedMedia;
+import de.danoeh.antennapod.feed.FeedPreferences;
import de.danoeh.antennapod.fragment.DownloadsFragment;
-import de.danoeh.antennapod.storage.*;
+import de.danoeh.antennapod.storage.DBReader;
+import de.danoeh.antennapod.storage.DBTasks;
+import de.danoeh.antennapod.storage.DBWriter;
+import de.danoeh.antennapod.storage.DownloadRequestException;
+import de.danoeh.antennapod.storage.DownloadRequester;
import de.danoeh.antennapod.syndication.handler.FeedHandler;
import de.danoeh.antennapod.syndication.handler.UnsupportedFeedtypeException;
import de.danoeh.antennapod.util.ChapterUtils;
import de.danoeh.antennapod.util.DownloadError;
import de.danoeh.antennapod.util.InvalidFeedException;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.http.HttpStatus;
-import org.xml.sax.SAXException;
-
-import javax.xml.parsers.ParserConfigurationException;
-import java.io.File;
-import java.io.IOException;
-import java.util.*;
-import java.util.concurrent.*;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* Manages the download of feedfiles in the app. Downloads can be enqueued viathe startService intent.
@@ -82,9 +115,14 @@ public class DownloadService extends Service {
public static final String EXTRA_REQUEST = "request";
/**
- * Stores DownloadStatus objects of completed downloads for creating a report at the end of the lifecylce.
+ * Stores new media files that will be queued for auto-download if possible.
+ */
+ private List<Long> newMediaFiles;
+
+ /**
+ * Contains all completed downloads that have not been included in the report yet.
*/
- private List<DownloadStatus> completedDownloads;
+ private List<DownloadStatus> reportQueue;
private ExecutorService syncExecutor;
private CompletionService<Downloader> downloadExecutor;
@@ -206,7 +244,8 @@ public class DownloadService extends Service {
Log.d(TAG, "Service started");
isRunning = true;
handler = new Handler();
- completedDownloads = Collections.synchronizedList(new ArrayList<DownloadStatus>());
+ newMediaFiles = Collections.synchronizedList(new ArrayList<Long>());
+ reportQueue = Collections.synchronizedList(new ArrayList<DownloadStatus>());
downloads = new ArrayList<Downloader>();
numberOfDownloads = new AtomicInteger(0);
@@ -285,7 +324,10 @@ public class DownloadService extends Service {
cancelNotificationUpdater();
unregisterReceiver(cancelDownloadReceiver);
- DBTasks.autodownloadUndownloadedItems(getApplicationContext());
+ if (!newMediaFiles.isEmpty()) {
+ DBTasks.autodownloadUndownloadedItems(getApplicationContext(),
+ ArrayUtils.toPrimitive(newMediaFiles.toArray(new Long[newMediaFiles.size()])));
+ }
}
@SuppressLint("NewApi")
@@ -394,12 +436,10 @@ public class DownloadService extends Service {
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(ACTION_CANCEL_DOWNLOAD)) {
+ if (StringUtils.equals(intent.getAction(), ACTION_CANCEL_DOWNLOAD)) {
String url = intent.getStringExtra(EXTRA_DOWNLOAD_URL);
- if (url == null) {
- throw new IllegalArgumentException(
- "ACTION_CANCEL_DOWNLOAD intent needs download url extra");
- }
+ Validate.notNull(url, "ACTION_CANCEL_DOWNLOAD intent needs download url extra");
+
if (BuildConfig.DEBUG)
Log.d(TAG, "Cancelling download with url " + url);
Downloader d = getDownloader(url);
@@ -409,7 +449,7 @@ public class DownloadService extends Service {
Log.e(TAG, "Could not cancel download with url " + url);
}
- } else if (intent.getAction().equals(ACTION_CANCEL_ALL_DOWNLOADS)) {
+ } else if (StringUtils.equals(intent.getAction(), ACTION_CANCEL_ALL_DOWNLOADS)) {
for (Downloader d : downloads) {
d.cancel();
if (BuildConfig.DEBUG)
@@ -482,7 +522,7 @@ public class DownloadService extends Service {
* @param status the download that is going to be saved
*/
private void saveDownloadStatus(DownloadStatus status) {
- completedDownloads.add(status);
+ reportQueue.add(status);
DBWriter.addDownloadStatus(this, status);
}
@@ -505,7 +545,7 @@ public class DownloadService extends Service {
// a download report is created if at least one download has failed
// (excluding failed image downloads)
- for (DownloadStatus status : completedDownloads) {
+ for (DownloadStatus status : reportQueue) {
if (status.isSuccessful()) {
successfulDownloads++;
} else if (!status.isCancelled()) {
@@ -552,7 +592,7 @@ public class DownloadService extends Service {
if (BuildConfig.DEBUG)
Log.d(TAG, "No report is created");
}
- completedDownloads.clear();
+ reportQueue.clear();
}
/**
@@ -698,7 +738,8 @@ public class DownloadService extends Service {
Log.d(TAG, "Waiting for " + (startTime + WAIT_TIMEOUT - currentTime) + " ms");
sleep(startTime + WAIT_TIMEOUT - currentTime);
} catch (InterruptedException e) {
- if (BuildConfig.DEBUG) Log.d(TAG, "interrupted while waiting for more downloads");
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "interrupted while waiting for more downloads");
tasks += pollCompletedDownloads();
} finally {
currentTime = System.currentTimeMillis();
@@ -794,6 +835,14 @@ public class DownloadService extends Service {
);
}
}
+
+ // queue new media files for automatic download
+ for (FeedItem item : savedFeed.getItems()) {
+ if (!item.isRead() && item.hasMedia() && !item.getMedia().isDownloaded()) {
+ newMediaFiles.add(item.getMedia().getId());
+ }
+ }
+
numberOfDownloads.decrementAndGet();
}
@@ -1033,12 +1082,9 @@ public class DownloadService extends Service {
private DownloadStatus status;
public ImageHandlerThread(DownloadStatus status, DownloadRequest request) {
- if (status == null) {
- throw new IllegalArgumentException("Status must not be null");
- }
- if (request == null) {
- throw new IllegalArgumentException("Request must not be null");
- }
+ Validate.notNull(status);
+ Validate.notNull(request);
+
this.status = status;
this.request = request;
}
@@ -1070,12 +1116,8 @@ public class DownloadService extends Service {
private DownloadStatus status;
public MediaHandlerThread(DownloadStatus status, DownloadRequest request) {
- if (status == null) {
- throw new IllegalArgumentException("Status must not be null");
- }
- if (request == null) {
- throw new IllegalArgumentException("Request must not be null");
- }
+ Validate.notNull(status);
+ Validate.notNull(request);
this.status = status;
this.request = request;
diff --git a/src/de/danoeh/antennapod/service/download/DownloadStatus.java b/src/de/danoeh/antennapod/service/download/DownloadStatus.java
index b240cddbc..1d76770bb 100644
--- a/src/de/danoeh/antennapod/service/download/DownloadStatus.java
+++ b/src/de/danoeh/antennapod/service/download/DownloadStatus.java
@@ -1,5 +1,7 @@
package de.danoeh.antennapod.service.download;
+import org.apache.commons.lang3.Validate;
+
import de.danoeh.antennapod.feed.FeedFile;
import de.danoeh.antennapod.util.DownloadError;
@@ -59,9 +61,8 @@ public class DownloadStatus {
public DownloadStatus(DownloadRequest request, DownloadError reason,
boolean successful, boolean cancelled, String reasonDetailed) {
- if (request == null) {
- throw new IllegalArgumentException("request must not be null");
- }
+ Validate.notNull(request);
+
this.title = request.getTitle();
this.feedfileId = request.getFeedfileId();
this.feedfileType = request.getFeedfileType();
@@ -75,9 +76,7 @@ public class DownloadStatus {
/** Constructor for creating new completed downloads. */
public DownloadStatus(FeedFile feedfile, String title, DownloadError reason,
boolean successful, String reasonDetailed) {
- if (feedfile == null) {
- throw new IllegalArgumentException("feedfile must not be null");
- }
+ Validate.notNull(feedfile);
this.title = title;
this.done = true;
diff --git a/src/de/danoeh/antennapod/service/playback/PlaybackService.java b/src/de/danoeh/antennapod/service/playback/PlaybackService.java
index b7ff62129..163a57ed2 100644
--- a/src/de/danoeh/antennapod/service/playback/PlaybackService.java
+++ b/src/de/danoeh/antennapod/service/playback/PlaybackService.java
@@ -22,6 +22,10 @@ import android.util.Log;
import android.util.Pair;
import android.view.KeyEvent;
import android.view.SurfaceHolder;
+import android.widget.Toast;
+
+import org.apache.commons.lang3.StringUtils;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.PodcastApp;
import de.danoeh.antennapod.R;
@@ -39,9 +43,9 @@ import de.danoeh.antennapod.storage.DBTasks;
import de.danoeh.antennapod.storage.DBWriter;
import de.danoeh.antennapod.util.BitmapDecoder;
import de.danoeh.antennapod.util.QueueAccess;
+import de.danoeh.antennapod.util.flattr.FlattrThing;
import de.danoeh.antennapod.util.flattr.FlattrUtils;
import de.danoeh.antennapod.util.playback.Playable;
-import de.danoeh.antennapod.util.playback.PlaybackController;
import java.util.List;
@@ -282,7 +286,8 @@ public class PlaybackService extends Service {
if (BuildConfig.DEBUG)
Log.d(TAG, "Handling keycode: " + keycode);
- final PlayerStatus status = mediaPlayer.getPSMPInfo().playerStatus;
+ final PlaybackServiceMediaPlayer.PSMPInfo info = mediaPlayer.getPSMPInfo();
+ final PlayerStatus status = info.playerStatus;
switch (keycode) {
case KeyEvent.KEYCODE_HEADSETHOOK:
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
@@ -310,14 +315,20 @@ public class PlaybackService extends Service {
mediaPlayer.pause(true, true);
}
break;
- case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
- mediaPlayer.seekDelta(PlaybackController.DEFAULT_SEEK_DELTA);
+ case KeyEvent.KEYCODE_MEDIA_NEXT:
+ case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+ mediaPlayer.seekDelta(UserPreferences.getSeekDeltaMs());
break;
- }
- case KeyEvent.KEYCODE_MEDIA_REWIND: {
- mediaPlayer.seekDelta(-PlaybackController.DEFAULT_SEEK_DELTA);
+ case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+ case KeyEvent.KEYCODE_MEDIA_REWIND:
+ mediaPlayer.seekDelta(-UserPreferences.getSeekDeltaMs());
+ break;
+ default:
+ if (info.playable != null && info.playerStatus == PlayerStatus.PLAYING) { // only notify the user about an unknown key event if it is actually doing something
+ String message = String.format(getResources().getString(R.string.unknown_media_key), keycode);
+ Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
+ }
break;
- }
}
}
@@ -502,6 +513,11 @@ public class PlaybackService extends Service {
DBWriter.removeQueueItem(PlaybackService.this, item.getId(), true);
}
DBWriter.addItemToPlaybackHistory(PlaybackService.this, (FeedMedia) media);
+
+ // auto-flattr if enabled
+ if (isAutoFlattrable(media) && UserPreferences.getAutoFlattrPlayedDurationThreshold() == 1.0f) {
+ DBTasks.flattrItemIfLoggedIn(PlaybackService.this, item);
+ }
}
// Load next episode if previous episode was in the queue and if there
@@ -752,14 +768,13 @@ public class PlaybackService extends Service {
FeedItem item = m.getItem();
m.setPlayedDuration(m.getPlayedDuration() + ((int)(deltaPlayedDuration * playbackSpeed)));
// Auto flattr
- if (FlattrUtils.hasToken() && UserPreferences.isAutoFlattr() && item.getPaymentLink() != null && item.getFlattrStatus().getUnflattred() &&
- (m.getPlayedDuration() > UserPreferences.getPlayedDurationAutoflattrThreshold() * duration)) {
+ 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())
- + " is " + UserPreferences.getPlayedDurationAutoflattrThreshold() * 100 + "% of file duration " + Integer.toString(duration));
- item.getFlattrStatus().setFlattrQueue();
- DBWriter.setFeedItemFlattrStatus(PodcastApp.getInstance(), item, false);
+ + " is " + UserPreferences.getAutoFlattrPlayedDurationThreshold() * 100 + "% of file duration " + Integer.toString(duration));
+ DBTasks.flattrItemIfLoggedIn(this, item);
}
}
playable.saveCurrentPosition(PreferenceManager
@@ -889,8 +904,7 @@ public class PlaybackService extends Service {
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction() != null &&
- intent.getAction().equals(Intent.ACTION_HEADSET_PLUG)) {
+ if (StringUtils.equals(intent.getAction(), Intent.ACTION_HEADSET_PLUG)) {
int state = intent.getIntExtra("state", -1);
if (state != -1) {
if (BuildConfig.DEBUG)
@@ -932,8 +946,7 @@ public class PlaybackService extends Service {
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction() != null &&
- intent.getAction().equals(ACTION_SHUTDOWN_PLAYBACK_SERVICE)) {
+ if (StringUtils.equals(intent.getAction(), ACTION_SHUTDOWN_PLAYBACK_SERVICE)) {
stopSelf();
}
}
@@ -943,8 +956,7 @@ public class PlaybackService extends Service {
private BroadcastReceiver skipCurrentEpisodeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction() != null &&
- intent.getAction().equals(ACTION_SKIP_CURRENT_EPISODE)) {
+ if (StringUtils.equals(intent.getAction(), ACTION_SKIP_CURRENT_EPISODE)) {
if (BuildConfig.DEBUG)
Log.d(TAG, "Received SKIP_CURRENT_EPISODE intent");
mediaPlayer.endPlayback();
@@ -1045,4 +1057,13 @@ 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();
+ return item != null && FlattrUtils.hasToken() && UserPreferences.isAutoFlattr() && item.getPaymentLink() != null && item.getFlattrStatus().getUnflattred();
+ } else {
+ return false;
+ }
+ }
}
diff --git a/src/de/danoeh/antennapod/service/playback/PlaybackServiceMediaPlayer.java b/src/de/danoeh/antennapod/service/playback/PlaybackServiceMediaPlayer.java
index 2915da5a1..477eea9a6 100644
--- a/src/de/danoeh/antennapod/service/playback/PlaybackServiceMediaPlayer.java
+++ b/src/de/danoeh/antennapod/service/playback/PlaybackServiceMediaPlayer.java
@@ -7,6 +7,9 @@ import android.media.RemoteControlClient;
import android.util.Log;
import android.util.Pair;
import android.view.SurfaceHolder;
+
+import org.apache.commons.lang3.Validate;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.feed.Chapter;
import de.danoeh.antennapod.feed.MediaType;
@@ -61,10 +64,8 @@ public class PlaybackServiceMediaPlayer {
private final ThreadPoolExecutor executor;
public PlaybackServiceMediaPlayer(Context context, PSMPCallback callback) {
- if (context == null)
- throw new IllegalArgumentException("context = null");
- if (callback == null)
- throw new IllegalArgumentException("callback = null");
+ Validate.notNull(context);
+ Validate.notNull(callback);
this.context = context;
this.callback = callback;
@@ -115,8 +116,8 @@ public class PlaybackServiceMediaPlayer {
* @param prepareImmediately Set to true if the method should also prepare the episode for playback.
*/
public void playMediaObject(final Playable playable, final boolean stream, final boolean startWhenPrepared, final boolean prepareImmediately) {
- if (playable == null)
- throw new IllegalArgumentException("playable = null");
+ Validate.notNull(playable);
+
if (BuildConfig.DEBUG) Log.d(TAG, "Play media object.");
executor.submit(new Runnable() {
@Override
@@ -125,6 +126,7 @@ public class PlaybackServiceMediaPlayer {
try {
playMediaObject(playable, false, stream, startWhenPrepared, prepareImmediately);
} catch (RuntimeException e) {
+ e.printStackTrace();
throw e;
} finally {
playerLock.unlock();
@@ -142,8 +144,7 @@ public class PlaybackServiceMediaPlayer {
* @see #playMediaObject(de.danoeh.antennapod.util.playback.Playable, boolean, boolean, boolean)
*/
private void playMediaObject(final Playable playable, final boolean forceReset, final boolean stream, final boolean startWhenPrepared, final boolean prepareImmediately) {
- if (playable == null)
- throw new IllegalArgumentException("playable = null");
+ Validate.notNull(playable);
if (!playerLock.isHeldByCurrentThread())
throw new IllegalStateException("method requires playerLock");
@@ -458,8 +459,8 @@ public class PlaybackServiceMediaPlayer {
* Seek to the start of the specified chapter.
*/
public void seekToChapter(Chapter c) {
- if (c == null)
- throw new IllegalArgumentException("c = null");
+ Validate.notNull(c);
+
seekTo((int) c.getStart());
}
@@ -662,8 +663,8 @@ public class PlaybackServiceMediaPlayer {
* @param newMedia The new playable object of the PSMP object. This can be null.
*/
private synchronized void setPlayerStatus(PlayerStatus newStatus, Playable newMedia) {
- if (newStatus == null)
- throw new IllegalArgumentException("newStatus = null");
+ Validate.notNull(newStatus);
+
if (BuildConfig.DEBUG) Log.d(TAG, "Setting player status to " + newStatus);
this.playerStatus = newStatus;
diff --git a/src/de/danoeh/antennapod/service/playback/PlaybackServiceTaskManager.java b/src/de/danoeh/antennapod/service/playback/PlaybackServiceTaskManager.java
index 3ab06910a..680ec2e2f 100644
--- a/src/de/danoeh/antennapod/service/playback/PlaybackServiceTaskManager.java
+++ b/src/de/danoeh/antennapod/service/playback/PlaybackServiceTaskManager.java
@@ -2,6 +2,9 @@ package de.danoeh.antennapod.service.playback;
import android.content.Context;
import android.util.Log;
+
+import org.apache.commons.lang3.Validate;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.feed.EventDistributor;
import de.danoeh.antennapod.feed.FeedItem;
@@ -52,10 +55,8 @@ public class PlaybackServiceTaskManager {
* @param callback A PSTMCallback object for notifying the user about updates. Must not be null.
*/
public PlaybackServiceTaskManager(Context context, PSTMCallback callback) {
- if (context == null)
- throw new IllegalArgumentException("context must not be null");
- if (callback == null)
- throw new IllegalArgumentException("callback must not be null");
+ Validate.notNull(context);
+ Validate.notNull(callback);
this.context = context;
this.callback = callback;
@@ -195,8 +196,7 @@ public class PlaybackServiceTaskManager {
* @throws java.lang.IllegalArgumentException if waitingTime <= 0
*/
public synchronized void setSleepTimer(long waitingTime) {
- if (waitingTime <= 0)
- throw new IllegalArgumentException("waitingTime <= 0");
+ Validate.isTrue(waitingTime > 0, "Waiting time <= 0");
if (BuildConfig.DEBUG)
Log.d(TAG, "Setting sleep timer to " + Long.toString(waitingTime)
@@ -271,8 +271,7 @@ public class PlaybackServiceTaskManager {
* On completion, the callback's onChapterLoaded method will be called.
*/
public synchronized void startChapterLoader(final Playable media) {
- if (media == null)
- throw new IllegalArgumentException("media = null");
+ Validate.notNull(media);
if (isChapterLoaderActive()) {
cancelChapterLoader();
diff --git a/src/de/danoeh/antennapod/spa/SPAUtil.java b/src/de/danoeh/antennapod/spa/SPAUtil.java
index 0d83741ed..75cbd8b5a 100644
--- a/src/de/danoeh/antennapod/spa/SPAUtil.java
+++ b/src/de/danoeh/antennapod/spa/SPAUtil.java
@@ -5,6 +5,9 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
+
+import org.apache.commons.lang3.Validate;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.receiver.SPAReceiver;
@@ -56,7 +59,7 @@ public class SPAUtil {
*/
public static void resetSPAPreferences(Context c) {
if (BuildConfig.DEBUG) {
- if (c == null) throw new IllegalArgumentException("c = null");
+ Validate.notNull(c);
SharedPreferences.Editor editor = PreferenceManager
.getDefaultSharedPreferences(c.getApplicationContext()).edit();
editor.putBoolean(PREF_HAS_QUERIED_SP_APPS, false);
diff --git a/src/de/danoeh/antennapod/storage/DBTasks.java b/src/de/danoeh/antennapod/storage/DBTasks.java
index c6a34aeea..8d0ffd9c1 100644
--- a/src/de/danoeh/antennapod/storage/DBTasks.java
+++ b/src/de/danoeh/antennapod/storage/DBTasks.java
@@ -156,7 +156,7 @@ public final class DBTasks {
if (FlattrUtils.hasToken()) {
if (BuildConfig.DEBUG) Log.d(TAG, "Flattring all pending things.");
- new FlattrClickWorker(context, FlattrClickWorker.FLATTR_NOTIFICATION).executeAsync(); // flattr pending things
+ new FlattrClickWorker(context).executeAsync(); // flattr pending things
if (BuildConfig.DEBUG) Log.d(TAG, "Fetching flattr status.");
new FlattrStatusFetcher(context).start();
@@ -386,9 +386,11 @@ public final class DBTasks {
* This method is executed on an internal single thread executor.
*
* @param context Used for accessing the DB.
+ * @param mediaIds If this list is not empty, the method will only download a candidate for automatic downloading if
+ * its media ID is in the mediaIds list.
* @return A Future that can be used for waiting for the methods completion.
*/
- public static Future<?> autodownloadUndownloadedItems(final Context context) {
+ public static Future<?> autodownloadUndownloadedItems(final Context context, final long... mediaIds) {
return autodownloadExec.submit(new Runnable() {
@Override
public void run() {
@@ -417,11 +419,17 @@ public final class DBTasks {
- (downloadedEpisodes - deletedEpisodes);
}
+ Arrays.sort(mediaIds); // sort for binary search
+ final boolean ignoreMediaIds = mediaIds.length == 0;
List<FeedItem> itemsToDownload = new ArrayList<FeedItem>();
+
if (episodeSpaceLeft > 0 && undownloadedEpisodes > 0) {
for (int i = 0; i < queue.size(); i++) { // ignore playing item
FeedItem item = queue.get(i);
- if (item.hasMedia() && !item.getMedia().isDownloaded()
+ long mediaId = (item.hasMedia()) ? item.getMedia().getId() : -1;
+ if ((ignoreMediaIds || Arrays.binarySearch(mediaIds, mediaId) >= 0)
+ && item.hasMedia()
+ && !item.getMedia().isDownloaded()
&& !item.getMedia().isPlaying()
&& item.getFeed().getPreferences().getAutoDownload()) {
itemsToDownload.add(item);
@@ -433,9 +441,13 @@ public final class DBTasks {
}
}
}
+
if (episodeSpaceLeft > 0 && undownloadedEpisodes > 0) {
for (FeedItem item : unreadItems) {
- if (item.hasMedia() && !item.getMedia().isDownloaded()
+ long mediaId = (item.hasMedia()) ? item.getMedia().getId() : -1;
+ if ((ignoreMediaIds || Arrays.binarySearch(mediaIds, mediaId) >= 0)
+ && item.hasMedia()
+ && !item.getMedia().isDownloaded()
&& item.getFeed().getPreferences().getAutoDownload()) {
itemsToDownload.add(item);
episodeSpaceLeft--;
diff --git a/src/de/danoeh/antennapod/storage/DBWriter.java b/src/de/danoeh/antennapod/storage/DBWriter.java
index ffdfc65fd..9916ac97f 100644
--- a/src/de/danoeh/antennapod/storage/DBWriter.java
+++ b/src/de/danoeh/antennapod/storage/DBWriter.java
@@ -868,7 +868,7 @@ public class DBWriter {
adapter.setFeedItemFlattrStatus(item);
adapter.close();
if (startFlattrClickWorker) {
- new FlattrClickWorker(context, FlattrClickWorker.FLATTR_TOAST).executeAsync();
+ new FlattrClickWorker(context).executeAsync();
}
}
});
@@ -891,7 +891,7 @@ public class DBWriter {
adapter.setFeedFlattrStatus(feed);
adapter.close();
if (startFlattrClickWorker) {
- new FlattrClickWorker(context, FlattrClickWorker.FLATTR_TOAST).executeAsync();
+ new FlattrClickWorker(context).executeAsync();
}
}
});
diff --git a/src/de/danoeh/antennapod/storage/DownloadRequester.java b/src/de/danoeh/antennapod/storage/DownloadRequester.java
index 7bf21352a..d305c572b 100644
--- a/src/de/danoeh/antennapod/storage/DownloadRequester.java
+++ b/src/de/danoeh/antennapod/storage/DownloadRequester.java
@@ -14,6 +14,7 @@ import de.danoeh.antennapod.util.URLChecker;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Validate;
import java.io.File;
import java.util.Map;
@@ -57,8 +58,9 @@ public class DownloadRequester {
* @return True if the download request was accepted, false otherwise.
*/
public boolean download(Context context, DownloadRequest request) {
- if (context == null) throw new IllegalArgumentException("context = null");
- if (request == null) throw new IllegalArgumentException("request = null");
+ Validate.notNull(context);
+ Validate.notNull(request);
+
if (downloads.containsKey(request.getSource())) {
if (BuildConfig.DEBUG) Log.i(TAG, "DownloadRequest is already stored.");
return false;
diff --git a/src/de/danoeh/antennapod/storage/PodDBAdapter.java b/src/de/danoeh/antennapod/storage/PodDBAdapter.java
index 06c8b1fc9..671ac30d5 100644
--- a/src/de/danoeh/antennapod/storage/PodDBAdapter.java
+++ b/src/de/danoeh/antennapod/storage/PodDBAdapter.java
@@ -10,14 +10,23 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
-import de.danoeh.antennapod.BuildConfig;
-import de.danoeh.antennapod.feed.*;
-import de.danoeh.antennapod.service.download.DownloadStatus;
-import de.danoeh.antennapod.util.flattr.FlattrStatus;
+
+import org.apache.commons.lang3.Validate;
import java.util.Arrays;
import java.util.List;
+import de.danoeh.antennapod.BuildConfig;
+import de.danoeh.antennapod.feed.Chapter;
+import de.danoeh.antennapod.feed.Feed;
+import de.danoeh.antennapod.feed.FeedComponent;
+import de.danoeh.antennapod.feed.FeedImage;
+import de.danoeh.antennapod.feed.FeedItem;
+import de.danoeh.antennapod.feed.FeedMedia;
+import de.danoeh.antennapod.feed.FeedPreferences;
+import de.danoeh.antennapod.service.download.DownloadStatus;
+import de.danoeh.antennapod.util.flattr.FlattrStatus;
+
// TODO Remove media column from feeditem table
/**
@@ -1024,9 +1033,8 @@ public class PodDBAdapter {
* @throws IllegalArgumentException if limit < 0
*/
public final Cursor getCompletedMediaCursor(int limit) {
- if (limit < 0) {
- throw new IllegalArgumentException("Limit must be >= 0");
- }
+ Validate.isTrue(limit >= 0, "Limit must be >= 0");
+
Cursor c = db.query(TABLE_NAME_FEED_MEDIA, null,
KEY_PLAYBACK_COMPLETION_DATE + " > 0 LIMIT " + limit, null, null,
null, null);
diff --git a/src/de/danoeh/antennapod/syndication/handler/TypeGetter.java b/src/de/danoeh/antennapod/syndication/handler/TypeGetter.java
index 5ed9ff2b0..2496e112a 100644
--- a/src/de/danoeh/antennapod/syndication/handler/TypeGetter.java
+++ b/src/de/danoeh/antennapod/syndication/handler/TypeGetter.java
@@ -4,6 +4,8 @@ import android.util.Log;
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.feed.Feed;
import org.apache.commons.io.input.XmlStreamReader;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
@@ -64,7 +66,7 @@ public class TypeGetter {
} else {
if (BuildConfig.DEBUG)
Log.d(TAG, "Type is invalid");
- throw new UnsupportedFeedtypeException(Type.INVALID);
+ throw new UnsupportedFeedtypeException(Type.INVALID, tag);
}
} else {
eventType = xpp.next();
@@ -73,7 +75,19 @@ public class TypeGetter {
} catch (XmlPullParserException e) {
e.printStackTrace();
- } catch (IOException e) {
+ // XML document might actually be a HTML document -> try to parse as HTML
+ String rootElement = null;
+ try {
+ if (Jsoup.parse(new File(feed.getFile_url()), null) != null) {
+ rootElement = "html";
+ }
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ } finally {
+ throw new UnsupportedFeedtypeException(Type.INVALID, rootElement);
+ }
+
+ } catch (IOException e) {
e.printStackTrace();
}
}
diff --git a/src/de/danoeh/antennapod/syndication/handler/UnsupportedFeedtypeException.java b/src/de/danoeh/antennapod/syndication/handler/UnsupportedFeedtypeException.java
index 67fbc9cc9..605dad2fb 100644
--- a/src/de/danoeh/antennapod/syndication/handler/UnsupportedFeedtypeException.java
+++ b/src/de/danoeh/antennapod/syndication/handler/UnsupportedFeedtypeException.java
@@ -5,18 +5,27 @@ import de.danoeh.antennapod.syndication.handler.TypeGetter.Type;
public class UnsupportedFeedtypeException extends Exception {
private static final long serialVersionUID = 9105878964928170669L;
private TypeGetter.Type type;
+ private String rootElement;
public UnsupportedFeedtypeException(Type type) {
super();
this.type = type;
-
}
-
- public TypeGetter.Type getType() {
+
+ public UnsupportedFeedtypeException(Type type, String rootElement) {
+ this.type = type;
+ this.rootElement = rootElement;
+ }
+
+ public TypeGetter.Type getType() {
return type;
}
-
- @Override
+
+ public String getRootElement() {
+ return rootElement;
+ }
+
+ @Override
public String getMessage() {
if (type == TypeGetter.Type.INVALID) {
return "Invalid type";
diff --git a/src/de/danoeh/antennapod/util/Converter.java b/src/de/danoeh/antennapod/util/Converter.java
index 46a0d30b4..f4c2b2f66 100644
--- a/src/de/danoeh/antennapod/util/Converter.java
+++ b/src/de/danoeh/antennapod/util/Converter.java
@@ -78,5 +78,26 @@ public final class Converter {
return String.format("%02d:%02d", h, m);
}
+
+ /** Converts long duration string (HH:MM:SS) to milliseconds. */
+ public static int durationStringLongToMs(String input) {
+ String[] parts = input.split(":");
+ if (parts.length != 3) {
+ return 0;
+ }
+ return Integer.valueOf(parts[0]) * 3600 * 1000 +
+ Integer.valueOf(parts[1]) * 60 * 1000 +
+ Integer.valueOf(parts[2]) * 1000;
+ }
+
+ /** Converts short duration string (HH:MM) to milliseconds. */
+ public static int durationStringShortToMs(String input) {
+ String[] parts = input.split(":");
+ if (parts.length != 2) {
+ return 0;
+ }
+ return Integer.valueOf(parts[0]) * 3600 * 1000 +
+ Integer.valueOf(parts[1]) * 1000 * 60;
+ }
}
diff --git a/src/de/danoeh/antennapod/util/DuckType.java b/src/de/danoeh/antennapod/util/DuckType.java
index 0dfc01508..163110418 100644
--- a/src/de/danoeh/antennapod/util/DuckType.java
+++ b/src/de/danoeh/antennapod/util/DuckType.java
@@ -6,6 +6,8 @@ import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
+import de.danoeh.antennapod.BuildConfig;
+
/**
* Allows "duck typing" or dynamic invocation based on method signature rather
* than type hierarchy. In other words, rather than checking whether something
@@ -63,7 +65,7 @@ public class DuckType {
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public <T> T to(Class iface) {
- assert iface.isInterface() : "cannot coerce object to a class, must be an interface";
+ if (BuildConfig.DEBUG && !iface.isInterface()) throw new AssertionError("cannot coerce object to a class, must be an interface");
if (isA(iface)) {
return (T) iface.cast(objectToCoerce);
}
diff --git a/src/de/danoeh/antennapod/util/URLChecker.java b/src/de/danoeh/antennapod/util/URLChecker.java
index eb522ffa8..9997daaf7 100644
--- a/src/de/danoeh/antennapod/util/URLChecker.java
+++ b/src/de/danoeh/antennapod/util/URLChecker.java
@@ -1,6 +1,9 @@
package de.danoeh.antennapod.util;
import android.util.Log;
+
+import org.apache.commons.lang3.StringUtils;
+
import de.danoeh.antennapod.BuildConfig;
/**
@@ -27,12 +30,16 @@ public final class URLChecker {
*/
public static String prepareURL(String url) {
StringBuilder builder = new StringBuilder();
+ url = StringUtils.trim(url);
if (url.startsWith("feed://")) {
if (BuildConfig.DEBUG) Log.d(TAG, "Replacing feed:// with http://");
url = url.replaceFirst("feed://", "http://");
} else if (url.startsWith("pcast://")) {
if (BuildConfig.DEBUG) Log.d(TAG, "Replacing pcast:// with http://");
url = url.replaceFirst("pcast://", "http://");
+ } else if (url.startsWith("itpc")) {
+ if (BuildConfig.DEBUG) Log.d(TAG, "Replacing itpc:// with http://");
+ url = url.replaceFirst("itpc://", "http://");
} else if (!(url.startsWith("http://") || url.startsWith("https://"))) {
if (BuildConfig.DEBUG) Log.d(TAG, "Adding http:// at the beginning of the URL");
builder.append("http://");
diff --git a/src/de/danoeh/antennapod/util/menuhandler/FeedItemMenuHandler.java b/src/de/danoeh/antennapod/util/menuhandler/FeedItemMenuHandler.java
index 6733430da..2c7a7f074 100644
--- a/src/de/danoeh/antennapod/util/menuhandler/FeedItemMenuHandler.java
+++ b/src/de/danoeh/antennapod/util/menuhandler/FeedItemMenuHandler.java
@@ -3,6 +3,7 @@ package de.danoeh.antennapod.util.menuhandler;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.feed.FeedItem;
@@ -113,6 +114,25 @@ public class FeedItemMenuHandler {
return true;
}
+ /**
+ * The same method as onPrepareMenu(MenuInterface, FeedItem, boolean, QueueAccess), but lets the
+ * caller also specify a list of menu items that should not be shown.
+ *
+ * @param excludeIds Menu item that should be excluded
+ * @return true if selectedItem is not null.
+ */
+ public static boolean onPrepareMenu(MenuInterface mi,
+ FeedItem selectedItem, boolean showExtendedMenu, QueueAccess queueAccess, int... excludeIds) {
+ boolean rc = onPrepareMenu(mi, selectedItem, showExtendedMenu, queueAccess);
+ if (rc && excludeIds != null) {
+ for (int id : excludeIds) {
+ mi.setItemVisibility(id, false);
+ }
+ }
+
+ return rc;
+ }
+
public static boolean onMenuItemClicked(Context context, int menuItemId,
FeedItem selectedItem) throws DownloadRequestException {
DownloadRequester requester = DownloadRequester.getInstance();
diff --git a/src/de/danoeh/antennapod/util/menuhandler/MenuItemUtils.java b/src/de/danoeh/antennapod/util/menuhandler/MenuItemUtils.java
index e75fa394a..7aa04d24c 100644
--- a/src/de/danoeh/antennapod/util/menuhandler/MenuItemUtils.java
+++ b/src/de/danoeh/antennapod/util/menuhandler/MenuItemUtils.java
@@ -4,6 +4,7 @@ import android.support.v4.view.MenuItemCompat;
import android.support.v7.widget.SearchView;
import android.view.Menu;
import android.view.MenuItem;
+
import de.danoeh.antennapod.R;
/**
@@ -17,4 +18,14 @@ public class MenuItemUtils {
MenuItemCompat.setActionView(item, searchView);
return item;
}
+
+ /**
+ * Checks if the navigation drawer of the DrawerActivity is opened. This can be useful for Fragments
+ * that hide their menu if the navigation drawer is open.
+ *
+ * @return True if the drawer is open, false otherwise (also if the parameter is null)
+ */
+ public static boolean isActivityDrawerOpen(NavDrawerActivity activity) {
+ return activity != null && activity.isDrawerOpen();
+ }
}
diff --git a/src/de/danoeh/antennapod/util/menuhandler/NavDrawerActivity.java b/src/de/danoeh/antennapod/util/menuhandler/NavDrawerActivity.java
new file mode 100644
index 000000000..9c611a452
--- /dev/null
+++ b/src/de/danoeh/antennapod/util/menuhandler/NavDrawerActivity.java
@@ -0,0 +1,9 @@
+package de.danoeh.antennapod.util.menuhandler;
+
+/**
+ * Defines useful methods for activities that have a navigation drawer
+ */
+public interface NavDrawerActivity {
+
+ public boolean isDrawerOpen();
+}
diff --git a/src/de/danoeh/antennapod/util/playback/Playable.java b/src/de/danoeh/antennapod/util/playback/Playable.java
index 8eefb0be5..9ed45abfc 100644
--- a/src/de/danoeh/antennapod/util/playback/Playable.java
+++ b/src/de/danoeh/antennapod/util/playback/Playable.java
@@ -12,6 +12,7 @@ import de.danoeh.antennapod.feed.MediaType;
import de.danoeh.antennapod.storage.DBReader;
import de.danoeh.antennapod.util.ShownotesProvider;
import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.Validate;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
@@ -216,9 +217,8 @@ public interface Playable extends Parcelable,
private Playable playable;
public DefaultPlayableImageLoader(Playable playable) {
- if (playable == null) {
- throw new IllegalArgumentException("Playable must not be null");
- }
+ Validate.notNull(playable);
+
this.playable = playable;
}
diff --git a/src/de/danoeh/antennapod/util/playback/PlaybackController.java b/src/de/danoeh/antennapod/util/playback/PlaybackController.java
index 1992fce2c..64dbf4868 100644
--- a/src/de/danoeh/antennapod/util/playback/PlaybackController.java
+++ b/src/de/danoeh/antennapod/util/playback/PlaybackController.java
@@ -15,12 +15,17 @@ import android.view.View.OnClickListener;
import android.widget.ImageButton;
import android.widget.SeekBar;
import android.widget.TextView;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Validate;
+
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.feed.Chapter;
import de.danoeh.antennapod.feed.FeedMedia;
import de.danoeh.antennapod.feed.MediaType;
import de.danoeh.antennapod.preferences.PlaybackPreferences;
+import de.danoeh.antennapod.preferences.UserPreferences;
import de.danoeh.antennapod.service.playback.PlaybackService;
import de.danoeh.antennapod.service.playback.PlaybackServiceMediaPlayer;
import de.danoeh.antennapod.service.playback.PlayerStatus;
@@ -37,7 +42,6 @@ import java.util.concurrent.*;
public abstract class PlaybackController {
private static final String TAG = "PlaybackController";
- public static final int DEFAULT_SEEK_DELTA = 30000;
public static final int INVALID_TIME = -1;
private final Activity activity;
@@ -62,8 +66,8 @@ public abstract class PlaybackController {
private boolean reinitOnPause;
public PlaybackController(Activity activity, boolean reinitOnPause) {
- if (activity == null)
- throw new IllegalArgumentException("activity = null");
+ Validate.notNull(activity);
+
this.activity = activity;
this.reinitOnPause = reinitOnPause;
schedExecutor = new ScheduledThreadPoolExecutor(SCHED_EX_POOLSIZE,
@@ -360,7 +364,7 @@ public abstract class PlaybackController {
@Override
public void onReceive(Context context, Intent intent) {
if (isConnectedToPlaybackService()) {
- if (intent.getAction().equals(
+ if (StringUtils.equals(intent.getAction(),
PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE)) {
release();
onShutdownNotification();
@@ -605,7 +609,7 @@ public abstract class PlaybackController {
@Override
public void onClick(View v) {
if (status == PlayerStatus.PLAYING) {
- playbackService.seekDelta(-DEFAULT_SEEK_DELTA);
+ playbackService.seekDelta(-UserPreferences.getSeekDeltaMs());
}
}
};
@@ -616,7 +620,7 @@ public abstract class PlaybackController {
@Override
public void onClick(View v) {
if (status == PlayerStatus.PLAYING) {
- playbackService.seekDelta(DEFAULT_SEEK_DELTA);
+ playbackService.seekDelta(UserPreferences.getSeekDeltaMs());
}
}
};
@@ -680,6 +684,12 @@ public abstract class PlaybackController {
}
}
+ public void seekTo(int time) {
+ if (playbackService != null) {
+ playbackService.seekTo(time);
+ }
+ }
+
public void setVideoSurface(SurfaceHolder holder) {
if (playbackService != null) {
playbackService.setVideoSurface(holder);
diff --git a/src/de/danoeh/antennapod/util/playback/Timeline.java b/src/de/danoeh/antennapod/util/playback/Timeline.java
new file mode 100644
index 000000000..ceed68183
--- /dev/null
+++ b/src/de/danoeh/antennapod/util/playback/Timeline.java
@@ -0,0 +1,161 @@
+package de.danoeh.antennapod.util.playback;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.Log;
+import android.util.TypedValue;
+
+import org.apache.commons.lang3.Validate;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.jsoup.select.Elements;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import de.danoeh.antennapod.BuildConfig;
+import de.danoeh.antennapod.util.Converter;
+import de.danoeh.antennapod.util.ShownotesProvider;
+
+/**
+ * Connects chapter information and shownotes of a shownotesProvider, for example by making it possible to use the
+ * shownotes to navigate to another position in the podcast or by highlighting certain parts of the shownotesProvider's
+ * shownotes.
+ * <p/>
+ * A timeline object needs a shownotesProvider from which the chapter information is retrieved and shownotes are generated.
+ */
+public class Timeline {
+ private static final String TAG = "Timeline";
+
+ private static final String WEBVIEW_STYLE = "@font-face { font-family: 'Roboto-Light'; src: url('file:///android_asset/Roboto-Light.ttf'); } * { color: %s; font-family: roboto-Light; font-size: 11pt; } a { font-style: normal; text-decoration: none; font-weight: normal; color: #00A8DF; } a.timecode { color: #669900; } img { display: block; margin: 10 auto; max-width: %s; height: auto; } body { margin: %dpx %dpx %dpx %dpx; }";
+
+
+ private ShownotesProvider shownotesProvider;
+
+
+ private final String colorString;
+ private final int pageMargin;
+
+ public Timeline(Context context, ShownotesProvider shownotesProvider) {
+ if (shownotesProvider == null) throw new IllegalArgumentException("shownotesProvider = null");
+ this.shownotesProvider = shownotesProvider;
+
+ TypedArray res = context
+ .getTheme()
+ .obtainStyledAttributes(
+ new int[]{android.R.attr.textColorPrimary});
+ int colorResource = res.getColor(0, 0);
+ colorString = String.format("#%06X",
+ 0xFFFFFF & colorResource);
+ res.recycle();
+
+ pageMargin = (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, 8, context.getResources()
+ .getDisplayMetrics()
+ );
+ }
+
+ private static final Pattern TIMECODE_LINK_REGEX = Pattern.compile("antennapod://timecode/((\\d+))");
+ private static final String TIMECODE_LINK = "<a class=\"timecode\" href=\"antennapod://timecode/%d\">%s</a>";
+ private static final Pattern TIMECODE_REGEX = Pattern.compile("\\b(?:(?:(([0-9][0-9])):))?(([0-9][0-9])):(([0-9][0-9]))\\b");
+
+ /**
+ * Applies an app-specific CSS stylesheet and adds timecode links (optional).
+ * <p/>
+ * This method does NOT change the original shownotes string of the shownotesProvider object and it should
+ * also not be changed by the caller.
+ *
+ * @param addTimecodes True if this method should add timecode links
+ * @return The processed HTML string.
+ */
+ public String processShownotes(final boolean addTimecodes) {
+ final Playable playable = (shownotesProvider instanceof Playable) ? (Playable) shownotesProvider : null;
+
+ // load shownotes
+
+ String shownotes;
+ try {
+ shownotes = shownotesProvider.loadShownotes().call();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ if (shownotes == null) {
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "shownotesProvider contained no shownotes. Returning empty string");
+ return "";
+ }
+
+ Document document = Jsoup.parse(shownotes);
+
+ // apply style
+ String styleStr = String.format(WEBVIEW_STYLE, colorString, "100%", pageMargin,
+ pageMargin, pageMargin, pageMargin);
+ document.head().appendElement("style").attr("type", "text/css").text(styleStr);
+
+ // apply timecode links
+ if (addTimecodes) {
+ Elements elementsWithTimeCodes = document.body().getElementsMatchingOwnText(TIMECODE_REGEX);
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Recognized " + elementsWithTimeCodes.size() + " timecodes");
+ for (Element element : elementsWithTimeCodes) {
+ Matcher matcherLong = TIMECODE_REGEX.matcher(element.text());
+ StringBuffer buffer = new StringBuffer();
+ while (matcherLong.find()) {
+ String h = matcherLong.group(1);
+ String group = matcherLong.group(0);
+ int time = (h != null) ? Converter.durationStringLongToMs(group) :
+ Converter.durationStringShortToMs(group);
+
+ String rep;
+ if (playable == null || playable.getDuration() > time) {
+ rep = String.format(TIMECODE_LINK, time, group);
+ } else {
+ rep = group;
+ }
+ matcherLong.appendReplacement(buffer, rep);
+ }
+ matcherLong.appendTail(buffer);
+
+ element.html(buffer.toString());
+ }
+ }
+
+ Log.i(TAG, "Out: " + document.toString());
+ return document.toString();
+ }
+
+
+ /**
+ * Returns true if the given link is a timecode link.
+ */
+ public static boolean isTimecodeLink(String link) {
+ return link != null && link.matches(TIMECODE_LINK_REGEX.pattern());
+ }
+
+ /**
+ * Returns the time in milliseconds that is attached to this link or -1
+ * if the link is no valid timecode link.
+ */
+ public static int getTimecodeLinkTime(String link) {
+ if (isTimecodeLink(link)) {
+ Matcher m = TIMECODE_LINK_REGEX.matcher(link);
+
+ try {
+ if (m.find()) {
+ return Integer.valueOf(m.group(1));
+ }
+ } catch (NumberFormatException e) {
+ e.printStackTrace();
+ }
+ }
+ return -1;
+ }
+
+
+ public void setShownotesProvider(ShownotesProvider shownotesProvider) {
+ Validate.notNull(shownotesProvider);
+ this.shownotesProvider = shownotesProvider;
+ }
+}
diff --git a/src/de/danoeh/antennapod/util/syndication/FeedDiscoverer.java b/src/de/danoeh/antennapod/util/syndication/FeedDiscoverer.java
new file mode 100644
index 000000000..ac38ec876
--- /dev/null
+++ b/src/de/danoeh/antennapod/util/syndication/FeedDiscoverer.java
@@ -0,0 +1,78 @@
+package de.danoeh.antennapod.util.syndication;
+
+import android.net.Uri;
+import org.apache.commons.lang3.StringUtils;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.jsoup.select.Elements;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Finds RSS/Atom URLs in a HTML document using the auto-discovery techniques described here:
+ * <p/>
+ * http://www.rssboard.org/rss-autodiscovery
+ * <p/>
+ * http://blog.whatwg.org/feed-autodiscovery
+ */
+public class FeedDiscoverer {
+
+ private static final String MIME_RSS = "application/rss+xml";
+ private static final String MIME_ATOM = "application/atom+xml";
+
+ /**
+ * Discovers links to RSS and Atom feeds in the given File which must be a HTML document.
+ *
+ * @return A map which contains the feed URLs as keys and titles as values (the feed URL is also used as a title if
+ * a title cannot be found).
+ */
+ public Map<String, String> findLinks(File in, String baseUrl) throws IOException {
+ return findLinks(Jsoup.parse(in, null), baseUrl);
+ }
+
+ /**
+ * Discovers links to RSS and Atom feeds in the given File which must be a HTML document.
+ *
+ * @return A map which contains the feed URLs as keys and titles as values (the feed URL is also used as a title if
+ * a title cannot be found).
+ */
+ public Map<String, String> findLinks(String in, String baseUrl) throws IOException {
+ return findLinks(Jsoup.parse(in), baseUrl);
+ }
+
+ private Map<String, String> findLinks(Document document, String baseUrl) {
+ Map<String, String> res = new LinkedHashMap<String, String>();
+ Elements links = document.head().getElementsByTag("link");
+ for (Element link : links) {
+ String rel = link.attr("rel");
+ String href = link.attr("href");
+ if (!StringUtils.isEmpty(href) &&
+ (rel.equals("alternate") || rel.equals("feed"))) {
+ String type = link.attr("type");
+ if (type.equals(MIME_RSS) || type.equals(MIME_ATOM)) {
+ String title = link.attr("title");
+ String processedUrl = processURL(baseUrl, href);
+ if (processedUrl != null) {
+ res.put(processedUrl,
+ (StringUtils.isEmpty(title)) ? href : title);
+ }
+ }
+ }
+ }
+ return res;
+ }
+
+ private String processURL(String baseUrl, String strUrl) {
+ Uri uri = Uri.parse(strUrl);
+ if (uri.isRelative()) {
+ Uri res = Uri.parse(baseUrl).buildUpon().path(strUrl).build();
+ return (res != null) ? res.toString() : null;
+ } else {
+ return strUrl;
+ }
+ }
+}