diff options
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> |