summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/src/main/AndroidManifest.xml7
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java30
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/StatisticsActivity.java107
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java97
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java8
-rw-r--r--app/src/main/res/layout/statistics_activity.xml41
-rw-r--r--app/src/main/res/layout/statistics_listitem.xml57
-rw-r--r--app/src/main/res/xml/preferences.xml3
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java83
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/Converter.java12
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java200
-rw-r--r--core/src/main/res/values/strings.xml5
12 files changed, 529 insertions, 121 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index b20aab7b0..a9c306306 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -145,6 +145,13 @@
android:value="de.danoeh.antennapod.activity.PreferenceActivity"/>
</activity>
<activity
+ android:name=".activity.StatisticsActivity"
+ android:label="@string/statistics_label">
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value="de.danoeh.antennapod.activity.PreferenceActivity"/>
+ </activity>
+ <activity
android:name=".activity.OpmlImportFromPathActivity"
android:configChanges="keyboardHidden|orientation"
android:label="@string/opml_import_label">
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
index 9b0733b43..6c0f0ab4d 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
@@ -43,15 +43,13 @@ import de.danoeh.antennapod.core.event.ProgressEvent;
import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
-import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.StorageUtils;
-import de.danoeh.antennapod.core.util.playback.Playable;
-import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.dialog.RatingDialog;
import de.danoeh.antennapod.fragment.AddFeedFragment;
import de.danoeh.antennapod.fragment.DownloadsFragment;
@@ -567,21 +565,15 @@ public class MainActivity extends AppCompatActivity implements NavDrawerActivity
public void onConfirmButtonPressed(
DialogInterface dialog) {
dialog.dismiss();
- if (externalPlayerFragment != null) {
- PlaybackController controller = externalPlayerFragment.getPlaybackControllerTestingOnly();
- if (controller != null) {
- Playable playable = controller.getMedia();
- if (playable != null && playable instanceof FeedMedia) {
- FeedMedia media = (FeedMedia) playable;
- if (media.getItem().getFeed().getId() == feed.getId()) {
- Log.d(TAG, "Currently playing episode is about to be deleted, skipping");
- remover.skipOnCompletion = true;
- if(controller.getStatus() == PlayerStatus.PLAYING) {
- sendBroadcast(new Intent(
- PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE));
- }
- }
- }
+ long mediaId = PlaybackPreferences.getCurrentlyPlayingFeedMediaId();
+ if (mediaId > 0 &&
+ FeedItemUtil.indexOfItemWithMediaId(feed.getItems(), mediaId) >= 0) {
+ Log.d(TAG, "Currently playing episode is about to be deleted, skipping");
+ remover.skipOnCompletion = true;
+ int playerStatus = PlaybackPreferences.getCurrentPlayerStatus();
+ if(playerStatus == PlaybackPreferences.PLAYER_STATUS_PLAYING) {
+ sendBroadcast(new Intent(
+ PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE));
}
}
remover.executeAsync();
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/StatisticsActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/StatisticsActivity.java
new file mode 100644
index 000000000..0254617e4
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/activity/StatisticsActivity.java
@@ -0,0 +1,107 @@
+package de.danoeh.antennapod.activity;
+
+import android.os.Bundle;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatActivity;
+import android.util.Log;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ListView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.adapter.StatisticsListAdapter;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.util.Converter;
+import rx.Observable;
+import rx.Subscription;
+import rx.android.schedulers.AndroidSchedulers;
+import rx.schedulers.Schedulers;
+
+/**
+ * Displays the 'statistics' screen
+ */
+public class StatisticsActivity extends AppCompatActivity
+ implements AdapterView.OnItemClickListener {
+
+ private static final String TAG = StatisticsActivity.class.getSimpleName();
+
+ private Subscription subscription;
+ private TextView totalTimeTextView;
+ private ListView feedStatisticsList;
+ private ProgressBar progressBar;
+ private StatisticsListAdapter listAdapter;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ setTheme(UserPreferences.getTheme());
+ super.onCreate(savedInstanceState);
+ getSupportActionBar().setDisplayShowHomeEnabled(true);
+ setContentView(R.layout.statistics_activity);
+
+ totalTimeTextView = (TextView) findViewById(R.id.total_time);
+ feedStatisticsList = (ListView) findViewById(R.id.statistics_list);
+ progressBar = (ProgressBar) findViewById(R.id.progressBar);
+ listAdapter = new StatisticsListAdapter(this);
+ feedStatisticsList.setAdapter(listAdapter);
+ feedStatisticsList.setOnItemClickListener(this);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ progressBar.setVisibility(View.VISIBLE);
+ totalTimeTextView.setVisibility(View.GONE);
+ feedStatisticsList.setVisibility(View.GONE);
+ loadStats();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ finish();
+ return true;
+ } else {
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private void loadStats() {
+ if(subscription != null) {
+ subscription.unsubscribe();
+ }
+ subscription = Observable.fromCallable(() -> DBReader.getStatistics())
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(result -> {
+ if (result != null) {
+ totalTimeTextView.setText(Converter
+ .shortLocalizedDuration(this, result.totalTime));
+ listAdapter.update(result.feedTime);
+ progressBar.setVisibility(View.GONE);
+ totalTimeTextView.setVisibility(View.VISIBLE);
+ feedStatisticsList.setVisibility(View.VISIBLE);
+ }
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ });
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ DBReader.StatisticsItem stats = listAdapter.getItem(position);
+
+ AlertDialog.Builder dialog = new AlertDialog.Builder(this);
+ dialog.setTitle(stats.feed.getTitle());
+ dialog.setMessage(getString(R.string.statistics_details_dialog,
+ stats.episodesStarted,
+ stats.episodes,
+ Converter.shortLocalizedDuration(this, stats.timePlayed),
+ Converter.shortLocalizedDuration(this, stats.time)));
+ dialog.setPositiveButton(android.R.string.ok, null);
+ dialog.show();
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java
new file mode 100644
index 000000000..7fb1472ad
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java
@@ -0,0 +1,97 @@
+package de.danoeh.antennapod.adapter;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.bumptech.glide.Glide;
+import com.joanzapata.iconify.widget.IconTextView;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.glide.ApGlideSettings;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.util.Converter;
+
+/**
+ * Adapter for the statistics list
+ */
+public class StatisticsListAdapter extends BaseAdapter {
+ private Context context;
+ List<DBReader.StatisticsItem> feedTime = new ArrayList<>();
+
+ public StatisticsListAdapter(Context context) {
+ this.context = context;
+ }
+
+
+ @Override
+ public int getCount() {
+ return feedTime.size();
+ }
+
+ @Override
+ public DBReader.StatisticsItem getItem(int position) {
+ return feedTime.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return feedTime.get(position).feed.getId();
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ StatisticsHolder holder;
+ Feed feed = feedTime.get(position).feed;
+
+ if (convertView == null) {
+ holder = new StatisticsHolder();
+ LayoutInflater inflater = (LayoutInflater) context
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ convertView = inflater.inflate(R.layout.statistics_listitem, parent, false);
+
+ holder.image = (ImageView) convertView.findViewById(R.id.imgvCover);
+ holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
+ holder.time = (TextView) convertView.findViewById(R.id.txtvTime);
+ convertView.setTag(holder);
+ } else {
+ holder = (StatisticsHolder) convertView.getTag();
+ }
+
+ Glide.with(context)
+ .load(feed.getImageUri())
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate()
+ .into(holder.image);
+
+ holder.title.setText(feed.getTitle());
+ holder.time.setText(Converter.shortLocalizedDuration(context,
+ feedTime.get(position).timePlayed));
+ return convertView;
+ }
+
+ public void update(List<DBReader.StatisticsItem> feedTime) {
+ this.feedTime = feedTime;
+ notifyDataSetChanged();
+ }
+
+ static class StatisticsHolder {
+ ImageView image;
+ TextView title;
+ TextView time;
+ }
+
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java
index ceb0c10ea..3aa04f202 100644
--- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java
+++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java
@@ -50,6 +50,7 @@ import de.danoeh.antennapod.activity.DirectoryChooserActivity;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.activity.PreferenceActivityGingerbread;
+import de.danoeh.antennapod.activity.StatisticsActivity;
import de.danoeh.antennapod.asynctask.OpmlExportWorker;
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
@@ -76,6 +77,7 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
public static final String PREF_FLATTR_REVOKE = "prefRevokeAccess";
public static final String PREF_AUTO_FLATTR_PREFS = "prefAutoFlattrPrefs";
public static final String PREF_OPML_EXPORT = "prefOpmlExport";
+ public static final String STATISTICS = "statistics";
public static final String PREF_ABOUT = "prefAbout";
public static final String PREF_CHOOSE_DATA_DIR = "prefChooseDataDir";
public static final String AUTO_DL_PREF_SCREEN = "prefAutoDownloadSettings";
@@ -156,6 +158,12 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
return true;
}
);
+ ui.findPreference(PreferenceController.STATISTICS).setOnPreferenceClickListener(
+ preference -> {
+ activity.startActivity(new Intent(activity, StatisticsActivity.class));
+ return true;
+ }
+ );
ui.findPreference(PreferenceController.PREF_OPML_EXPORT).setOnPreferenceClickListener(
preference -> {
new OpmlExportWorker(activity).executeAsync();
diff --git a/app/src/main/res/layout/statistics_activity.xml b/app/src/main/res/layout/statistics_activity.xml
new file mode 100644
index 000000000..4a72dc7de
--- /dev/null
+++ b/app/src/main/res/layout/statistics_activity.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/total_time_listened_to_podcasts"
+ android:gravity="center_horizontal"/>
+
+ <ProgressBar
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/progressBar"
+ android:layout_gravity="center_horizontal"
+ style="?android:attr/progressBarStyleLarge"/>
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/total_time"
+ android:gravity="center_horizontal"
+ android:textSize="45sp"/>
+
+ <ListView
+ android:id="@+id/statistics_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:choiceMode="singleChoice"
+ android:clipToPadding="false"
+ android:divider="@android:color/transparent"
+ android:dividerHeight="0dp"
+ android:paddingBottom="@dimen/list_vertical_padding"
+ android:paddingTop="@dimen/list_vertical_padding"
+ android:scrollbarStyle="outsideOverlay" />
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/statistics_listitem.xml b/app/src/main/res/layout/statistics_listitem.xml
new file mode 100644
index 000000000..20e01bf32
--- /dev/null
+++ b/app/src/main/res/layout/statistics_listitem.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/listitem_iconwithtext_height"
+ android:paddingRight="@dimen/listitem_threeline_verticalpadding"
+ tools:background="@android:color/darker_gray">
+
+ <ImageView
+ android:id="@+id/imgvCover"
+ android:contentDescription="@string/cover_label"
+ android:layout_width="@dimen/thumbnail_length_navlist"
+ android:layout_height="@dimen/thumbnail_length_navlist"
+ android:layout_alignParentLeft="true"
+ android:layout_centerVertical="true"
+ android:adjustViewBounds="true"
+ android:cropToPadding="true"
+ android:scaleType="centerCrop"
+ android:layout_marginTop="4dp"
+ android:layout_marginBottom="4dp"
+ android:layout_marginLeft="@dimen/listitem_icon_leftpadding"
+ tools:src="@drawable/ic_stat_antenna_default"
+ tools:background="@android:color/holo_green_dark"/>
+
+ <TextView
+ android:id="@+id/txtvTime"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/list_vertical_padding"
+ android:lines="1"
+ android:textColor="?android:attr/textColorTertiary"
+ android:textSize="@dimen/text_size_navdrawer"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true"
+ tools:text="23"
+ tools:background="@android:color/holo_green_dark"/>
+
+ <TextView
+ android:id="@+id/txtvTitle"
+ android:lines="1"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:layout_centerVertical="true"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="@dimen/text_size_navdrawer"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/listitem_iconwithtext_textleftpadding"
+ android:layout_toRightOf="@id/imgvCover"
+ android:layout_toLeftOf="@id/txtvTime"
+ android:layout_alignWithParentIfMissing="true"
+ tools:text="Navigation feed item title"
+ tools:background="@android:color/holo_green_dark"/>
+
+
+</RelativeLayout>
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index 778db763f..836f85833 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -269,6 +269,9 @@
android:key="prefOpmlExport"
android:title="@string/opml_export_label"/>
<Preference
+ android:key="statistics"
+ android:title="@string/statistics_label"/>
+ <Preference
android:key="prefAbout"
android:title="@string/about_pref"/>
<Preference
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java
index fc7d83978..94629ba9d 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java
@@ -592,6 +592,7 @@ public final class DBReader {
Cursor itemCursor = adapter.getFeedItemCursor(Long.toString(itemId));
if (!itemCursor.moveToFirst()) {
+ itemCursor.close();
return null;
}
List<FeedItem> list = extractItemlistFromCursor(adapter, itemCursor);
@@ -924,6 +925,88 @@ public final class DBReader {
}
/**
+ * Searches the DB for statistics
+ *
+ * @return The StatisticsInfo object
+ */
+ public static StatisticsData getStatistics() {
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
+ adapter.open();
+
+ long totalTime = 0;
+ List<StatisticsItem> feedTime = new ArrayList<>();
+
+ List<Feed> feeds = getFeedList();
+ for (Feed feed : feeds) {
+ long feedPlayedTime = 0;
+ long feedTotalTime = 0;
+ long episodes = 0;
+ long episodesStarted = 0;
+ List<FeedItem> items = getFeed(feed.getId()).getItems();
+ for(FeedItem item : items) {
+ FeedMedia media = item.getMedia();
+ if(media == null) {
+ continue;
+ }
+
+ if(item.isPlayed()) {
+ feedPlayedTime += media.getDuration() / 1000;
+ } else {
+ feedPlayedTime += media.getPosition() / 1000;
+ }
+ if(item.isPlayed() || media.getPosition() != 0) {
+ episodesStarted++;
+ }
+ feedTotalTime += media.getDuration() / 1000;
+ episodes++;
+ }
+ feedTime.add(new StatisticsItem(
+ feed, feedTotalTime, feedPlayedTime, episodes, episodesStarted));
+ totalTime += feedPlayedTime;
+ }
+
+ Collections.sort(feedTime, (item1, item2) -> {
+ if(item1.timePlayed > item2.timePlayed) {
+ return -1;
+ } else if(item1.timePlayed < item2.timePlayed) {
+ return 1;
+ } else {
+ return 0;
+ }
+ });
+
+ adapter.close();
+ return new StatisticsData(totalTime, feedTime);
+ }
+
+ public static class StatisticsData {
+ public long totalTime;
+ public List<StatisticsItem> feedTime;
+
+ public StatisticsData(long totalTime, List<StatisticsItem> feedTime) {
+ this.totalTime = totalTime;
+ this.feedTime = feedTime;
+ }
+ }
+
+ public static class StatisticsItem {
+ public Feed feed;
+ public long time;
+ public long timePlayed;
+ public long episodes;
+ public long episodesStarted;
+
+ public StatisticsItem(Feed feed, long time, long timePlayed,
+ long episodes, long episodesStarted) {
+ this.feed = feed;
+ this.time = time;
+ this.timePlayed = timePlayed;
+ this.episodes = episodes;
+ this.episodesStarted = episodesStarted;
+ }
+ }
+
+ /**
* Returns the flattr queue as a List of FlattrThings. The list consists of Feeds and FeedItems.
*
* @return The flattr queue as a List.
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/Converter.java b/core/src/main/java/de/danoeh/antennapod/core/util/Converter.java
index 5b046d7a7..70a180913 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/Converter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/Converter.java
@@ -3,6 +3,8 @@ package de.danoeh.antennapod.core.util;
import android.content.Context;
import android.util.Log;
+import java.util.Locale;
+
import de.danoeh.antennapod.core.R;
/** Provides methods for converting various units. */
@@ -120,6 +122,16 @@ public final class Converter {
}
/**
+ * Converts seconds to a localized representation
+ * @param time The time in seconds
+ * @return "HH:MM hours"
+ */
+ public static String shortLocalizedDuration(Context context, long time) {
+ float hours = (float) time / 3600f;
+ return String.format(Locale.getDefault(), "%.1f ", hours) + context.getString(R.string.time_hours);
+ }
+
+ /**
* Converts the volume as read as the progress from a SeekBar scaled to 100 and as saved in
* UserPreferences to the format taken by setVolume methods.
* @param progress integer between 0 to 100 taken from the SeekBar progress
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
index 016ff9a85..0ad286093 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
@@ -10,7 +10,6 @@ import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.media.MediaPlayer;
-import android.os.AsyncTask;
import android.os.Build;
import android.os.IBinder;
import android.preference.PreferenceManager;
@@ -41,6 +40,10 @@ import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.playback.Playable.PlayableUtils;
+import rx.Observable;
+import rx.Subscription;
+import rx.android.schedulers.AndroidSchedulers;
+import rx.schedulers.Schedulers;
/**
* Communicates with the playback service. GUI classes should use this class to
@@ -50,28 +53,30 @@ public abstract class PlaybackController {
private static final String TAG = "PlaybackController";
- public static final int INVALID_TIME = -1;
+ private static final int INVALID_TIME = -1;
private final Activity activity;
private PlaybackService playbackService;
- protected Playable media;
+ private Playable media;
private PlayerStatus status;
- private ScheduledThreadPoolExecutor schedExecutor;
+ private final ScheduledThreadPoolExecutor schedExecutor;
private static final int SCHED_EX_POOLSIZE = 1;
- protected MediaPositionObserver positionObserver;
- protected ScheduledFuture positionObserverFuture;
+ private MediaPositionObserver positionObserver;
+ private ScheduledFuture positionObserverFuture;
private boolean mediaInfoLoaded = false;
private boolean released = false;
+ private Subscription serviceBinder;
+
/**
* True if controller should reinit playback service if 'pause' button is
* pressed.
*/
- private boolean reinitOnPause;
+ private final boolean reinitOnPause;
public PlaybackController(@NonNull Activity activity, boolean reinitOnPause) {
@@ -86,8 +91,7 @@ public abstract class PlaybackController {
@Override
public void rejectedExecution(Runnable r,
ThreadPoolExecutor executor) {
- Log.w(TAG,
- "Rejected execution of runnable in schedExecutor");
+ Log.w(TAG, "Rejected execution of runnable in schedExecutor");
}
}
);
@@ -110,8 +114,7 @@ public abstract class PlaybackController {
if (!released) {
bindToService();
} else {
- throw new IllegalStateException(
- "Can't call init() after release() has been called");
+ throw new IllegalStateException("Can't call init() after release() has been called");
}
checkMediaInfoLoaded();
}
@@ -135,6 +138,9 @@ public abstract class PlaybackController {
// ignore
}
+ if(serviceBinder != null) {
+ serviceBinder.unsubscribe();
+ }
try {
activity.unbindService(mConnection);
} catch (IllegalArgumentException e) {
@@ -167,34 +173,33 @@ public abstract class PlaybackController {
*/
private void bindToService() {
Log.d(TAG, "Trying to connect to service");
- AsyncTask<Void, Void, Intent> intentLoader = new AsyncTask<Void, Void, Intent>() {
- @Override
- protected Intent doInBackground(Void... voids) {
- return getPlayLastPlayedMediaIntent();
- }
-
- @Override
- protected void onPostExecute(Intent serviceIntent) {
- boolean bound = false;
- if (!PlaybackService.started) {
- if (serviceIntent != null) {
- Log.d(TAG, "Calling start service");
- activity.startService(serviceIntent);
- bound = activity.bindService(serviceIntent, mConnection, 0);
+ if(serviceBinder != null) {
+ serviceBinder.unsubscribe();
+ }
+ serviceBinder = Observable.fromCallable(() -> getPlayLastPlayedMediaIntent())
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(intent -> {
+ boolean bound = false;
+ if (!PlaybackService.started) {
+ if (intent != null) {
+ Log.d(TAG, "Calling start service");
+ activity.startService(intent);
+ bound = activity.bindService(intent, mConnection, 0);
+ } else {
+ status = PlayerStatus.STOPPED;
+ setupGUI();
+ handleStatus();
+ }
} else {
- status = PlayerStatus.STOPPED;
- setupGUI();
- handleStatus();
+ Log.d(TAG, "PlaybackService is running, trying to connect without start command.");
+ bound = activity.bindService(new Intent(activity, PlaybackService.class),
+ mConnection, 0);
}
- } else {
- Log.d(TAG, "PlaybackService is running, trying to connect without start command.");
- bound = activity.bindService(new Intent(activity,
- PlaybackService.class), mConnection, 0);
- }
- Log.d(TAG, "Result for service binding: " + bound);
- }
- };
- intentLoader.execute();
+ Log.d(TAG, "Result for service binding: " + bound);
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ });
}
/**
@@ -203,27 +208,21 @@ public abstract class PlaybackController {
*/
private Intent getPlayLastPlayedMediaIntent() {
Log.d(TAG, "Trying to restore last played media");
- SharedPreferences prefs = PreferenceManager
- .getDefaultSharedPreferences(activity.getApplicationContext());
- long currentlyPlayingMedia = PlaybackPreferences
- .getCurrentlyPlayingMedia();
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
+ activity.getApplicationContext());
+ long currentlyPlayingMedia = PlaybackPreferences.getCurrentlyPlayingMedia();
if (currentlyPlayingMedia != PlaybackPreferences.NO_MEDIA_PLAYING) {
Playable media = PlayableUtils.createInstanceFromPreferences(activity,
(int) currentlyPlayingMedia, prefs);
if (media != null) {
- Intent serviceIntent = new Intent(activity,
- PlaybackService.class);
+ Intent serviceIntent = new Intent(activity, PlaybackService.class);
serviceIntent.putExtra(PlaybackService.EXTRA_PLAYABLE, media);
- serviceIntent.putExtra(
- PlaybackService.EXTRA_START_WHEN_PREPARED, false);
- serviceIntent.putExtra(
- PlaybackService.EXTRA_PREPARE_IMMEDIATELY, false);
+ serviceIntent.putExtra(PlaybackService.EXTRA_START_WHEN_PREPARED, false);
+ serviceIntent.putExtra(PlaybackService.EXTRA_PREPARE_IMMEDIATELY, false);
boolean fileExists = media.localFileAvailable();
- boolean lastIsStream = PlaybackPreferences
- .getCurrentEpisodeIsStream();
+ boolean lastIsStream = PlaybackPreferences.getCurrentEpisodeIsStream();
if (!fileExists && !lastIsStream && media instanceof FeedMedia) {
- DBTasks.notifyMissingFeedMediaFile(
- activity, (FeedMedia) media);
+ DBTasks.notifyMissingFeedMediaFile(activity, (FeedMedia) media);
}
serviceIntent.putExtra(PlaybackService.EXTRA_SHOULD_STREAM,
lastIsStream || !fileExists);
@@ -257,7 +256,7 @@ public abstract class PlaybackController {
}
}
- private ServiceConnection mConnection = new ServiceConnection() {
+ private final ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
playbackService = ((PlaybackService.LocalBinder) service)
.getService();
@@ -265,7 +264,8 @@ public abstract class PlaybackController {
queryService();
Log.d(TAG, "Connection to Service established");
} else {
- Log.i(TAG, "Connection to playback service has been established, but controller has already been released");
+ Log.i(TAG, "Connection to playback service has been established, " +
+ "but controller has already been released");
}
}
@@ -276,7 +276,7 @@ public abstract class PlaybackController {
}
};
- protected BroadcastReceiver statusUpdate = new BroadcastReceiver() {
+ private final BroadcastReceiver statusUpdate = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Received statusUpdate Intent.");
@@ -286,66 +286,62 @@ public abstract class PlaybackController {
media = info.playable;
handleStatus();
} else {
- Log.w(TAG,
- "Couldn't receive status update: playbackService was null");
+ Log.w(TAG, "Couldn't receive status update: playbackService was null");
bindToService();
}
}
};
- protected BroadcastReceiver notificationReceiver = new BroadcastReceiver() {
+ private final BroadcastReceiver notificationReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (isConnectedToPlaybackService()) {
- int type = intent.getIntExtra(
- PlaybackService.EXTRA_NOTIFICATION_TYPE, -1);
- int code = intent.getIntExtra(
- PlaybackService.EXTRA_NOTIFICATION_CODE, -1);
- if (code != -1 && type != -1) {
- switch (type) {
- case PlaybackService.NOTIFICATION_TYPE_ERROR:
- handleError(code);
- break;
- case PlaybackService.NOTIFICATION_TYPE_BUFFER_UPDATE:
- float progress = ((float) code) / 100;
- onBufferUpdate(progress);
- break;
- case PlaybackService.NOTIFICATION_TYPE_RELOAD:
- cancelPositionObserver();
- mediaInfoLoaded = false;
- queryService();
- onReloadNotification(intent.getIntExtra(
- PlaybackService.EXTRA_NOTIFICATION_CODE, -1));
- break;
- case PlaybackService.NOTIFICATION_TYPE_SLEEPTIMER_UPDATE:
- onSleepTimerUpdate();
- break;
- case PlaybackService.NOTIFICATION_TYPE_BUFFER_START:
- onBufferStart();
- break;
- case PlaybackService.NOTIFICATION_TYPE_BUFFER_END:
- onBufferEnd();
- break;
- case PlaybackService.NOTIFICATION_TYPE_PLAYBACK_END:
- onPlaybackEnd();
- break;
- case PlaybackService.NOTIFICATION_TYPE_PLAYBACK_SPEED_CHANGE:
- onPlaybackSpeedChange();
- break;
- }
-
- } else {
- Log.d(TAG, "Bad arguments. Won't handle intent");
- }
- } else {
+ if (!isConnectedToPlaybackService()) {
bindToService();
+ return;
+ }
+ int type = intent.getIntExtra(PlaybackService.EXTRA_NOTIFICATION_TYPE, -1);
+ int code = intent.getIntExtra(PlaybackService.EXTRA_NOTIFICATION_CODE, -1);
+ if(code == -1 || type == -1) {
+ Log.d(TAG, "Bad arguments. Won't handle intent");
+ return;
+ }
+ switch (type) {
+ case PlaybackService.NOTIFICATION_TYPE_ERROR:
+ handleError(code);
+ break;
+ case PlaybackService.NOTIFICATION_TYPE_BUFFER_UPDATE:
+ float progress = ((float) code) / 100;
+ onBufferUpdate(progress);
+ break;
+ case PlaybackService.NOTIFICATION_TYPE_RELOAD:
+ cancelPositionObserver();
+ mediaInfoLoaded = false;
+ queryService();
+ onReloadNotification(intent.getIntExtra(
+ PlaybackService.EXTRA_NOTIFICATION_CODE, -1));
+ break;
+ case PlaybackService.NOTIFICATION_TYPE_SLEEPTIMER_UPDATE:
+ onSleepTimerUpdate();
+ break;
+ case PlaybackService.NOTIFICATION_TYPE_BUFFER_START:
+ onBufferStart();
+ break;
+ case PlaybackService.NOTIFICATION_TYPE_BUFFER_END:
+ onBufferEnd();
+ break;
+ case PlaybackService.NOTIFICATION_TYPE_PLAYBACK_END:
+ onPlaybackEnd();
+ break;
+ case PlaybackService.NOTIFICATION_TYPE_PLAYBACK_SPEED_CHANGE:
+ onPlaybackSpeedChange();
+ break;
}
}
};
- private BroadcastReceiver shutdownReceiver = new BroadcastReceiver() {
+ private final BroadcastReceiver shutdownReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -499,7 +495,7 @@ public abstract class PlaybackController {
* Called when connection to playback service has been established or
* information has to be refreshed
*/
- void queryService() {
+ private void queryService() {
Log.d(TAG, "Querying service info");
if (playbackService != null) {
status = playbackService.getStatus();
@@ -729,7 +725,7 @@ public abstract class PlaybackController {
* Returns true if PlaybackController can communicate with the playback
* service.
*/
- public boolean isConnectedToPlaybackService() {
+ private boolean isConnectedToPlaybackService() {
return playbackService != null;
}
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index d363a5cc0..eaf6d09fb 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -6,6 +6,7 @@
<!-- Activitiy and fragment titles -->
<string name="app_name">AntennaPod</string>
<string name="feeds_label">Feeds</string>
+ <string name="statistics_label">Statistics</string>
<string name="add_feed_label">Add Podcast</string>
<string name="podcasts_label">PODCASTS</string>
<string name="episodes_label">Episodes</string>
@@ -33,6 +34,10 @@
<string name="recently_published_episodes_label">Recently published</string>
<string name="episode_filter_label">Show only new Episodes</string>
+ <!-- Statistics fragment -->
+ <string name="total_time_listened_to_podcasts">Total time of podcasts played:</string>
+ <string name="statistics_details_dialog">%1$d out of %2$d episodes started.\n\nPlayed %3$s out of %4$s.</string>
+
<!-- Main activity -->
<string name="drawer_open">Open menu</string>
<string name="drawer_close">Close menu</string>