diff options
69 files changed, 1001 insertions, 820 deletions
diff --git a/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/Rss2Generator.java b/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/Rss2Generator.java index 6b294244a..6b85f3bf8 100644 --- a/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/Rss2Generator.java +++ b/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/Rss2Generator.java @@ -127,15 +127,9 @@ public class Rss2Generator implements FeedGenerator { } } - writeAdditionalAttributes(xml); - xml.endTag(null, "channel"); xml.endTag(null, "rss"); xml.endDocument(); } - - protected void writeAdditionalAttributes(XmlSerializer xml) throws IOException { - - } } diff --git a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java index 1fc16ab32..05a514c76 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java @@ -4,7 +4,6 @@ import android.content.Intent; import android.os.Build; import android.os.Bundle; import android.provider.Settings; -import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -131,12 +130,6 @@ public class PreferenceActivity extends AppCompatActivity implements SearchPrefe } @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - return true; - } - - @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == android.R.id.home) { if (getSupportFragmentManager().getBackStackEntryCount() == 0) { diff --git a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java index 4ff2a5775..954a6c2f6 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java @@ -91,6 +91,7 @@ public class VideoplayerActivity extends CastEnabledActivity implements SeekBar. private PlaybackController controller; private boolean showTimeLeft = false; private boolean isFavorite = false; + private boolean switchToAudioOnly = false; private Disposable disposable; private float prog; @@ -119,6 +120,7 @@ public class VideoplayerActivity extends CastEnabledActivity implements SeekBar. protected void onResume() { super.onResume(); StorageUtils.checkStorageAvailability(this); + switchToAudioOnly = false; if (PlaybackService.isCasting()) { Intent intent = PlaybackService.getPlayerActivityIntent(this); if (!intent.getComponent().getClassName().equals(VideoplayerActivity.class.getName())) { @@ -149,8 +151,7 @@ public class VideoplayerActivity extends CastEnabledActivity implements SeekBar. @Override public void onUserLeaveHint() { - if (!PictureInPictureUtil.isInPictureInPictureMode(this) && UserPreferences.getVideoBackgroundBehavior() - == UserPreferences.VideoBackgroundBehavior.PICTURE_IN_PICTURE) { + if (!PictureInPictureUtil.isInPictureInPictureMode(this)) { compatEnterPictureInPicture(); } } @@ -480,9 +481,7 @@ public class VideoplayerActivity extends CastEnabledActivity implements SeekBar. public void surfaceDestroyed(SurfaceHolder holder) { Log.d(TAG, "Videosurface was destroyed"); videoSurfaceCreated = false; - if (controller != null && !destroyingDueToReload - && UserPreferences.getVideoBackgroundBehavior() - != UserPreferences.VideoBackgroundBehavior.CONTINUE_PLAYING) { + if (controller != null && !destroyingDueToReload && !switchToAudioOnly) { controller.notifyVideoSurfaceAbandoned(); } } @@ -590,17 +589,16 @@ public class VideoplayerActivity extends CastEnabledActivity implements SeekBar. menu.findItem(R.id.set_sleeptimer_item).setVisible(!controller.sleepTimerActive()); menu.findItem(R.id.disable_sleeptimer_item).setVisible(controller.sleepTimerActive()); - if (PictureInPictureUtil.supportsPictureInPicture(this)) { - menu.findItem(R.id.player_go_to_picture_in_picture).setVisible(true); - } + menu.findItem(R.id.player_switch_to_audio_only).setVisible(true); menu.findItem(R.id.audio_controls).setIcon(R.drawable.ic_sliders); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == R.id.player_go_to_picture_in_picture) { - compatEnterPictureInPicture(); + if (item.getItemId() == R.id.player_switch_to_audio_only) { + switchToAudioOnly = true; + finish(); return true; } if (item.getItemId() == android.R.id.home) { diff --git a/app/src/main/java/de/danoeh/antennapod/activity/WidgetConfigActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/WidgetConfigActivity.java index 2d2fd643b..7245ce675 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/WidgetConfigActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/WidgetConfigActivity.java @@ -47,7 +47,7 @@ public class WidgetConfigActivity extends AppCompatActivity { opacityTextView = findViewById(R.id.widget_opacity_textView); opacitySeekBar = findViewById(R.id.widget_opacity_seekBar); widgetPreview = findViewById(R.id.widgetLayout); - findViewById(R.id.butConfirm).setOnClickListener(this::confirmCreateWidget); + findViewById(R.id.butConfirm).setOnClickListener(v -> confirmCreateWidget()); opacitySeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override @@ -94,7 +94,7 @@ public class WidgetConfigActivity extends AppCompatActivity { widgetPreview.findViewById(R.id.butRew).setVisibility(ckRewind.isChecked() ? View.VISIBLE : View.GONE); } - private void confirmCreateWidget(View v) { + private void confirmCreateWidget() { int backgroundColor = getColorWithAlpha(PlayerWidget.DEFAULT_COLOR, opacitySeekBar.getProgress()); SharedPreferences prefs = getSharedPreferences(PlayerWidget.PREFS_NAME, MODE_PRIVATE); diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/PlaybackStatisticsListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/PlaybackStatisticsListAdapter.java index 26674b2b2..e566836e4 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/PlaybackStatisticsListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/PlaybackStatisticsListAdapter.java @@ -2,15 +2,15 @@ package de.danoeh.antennapod.adapter; import androidx.fragment.app.Fragment; import de.danoeh.antennapod.R; -import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.storage.StatisticsItem; import de.danoeh.antennapod.core.util.Converter; -import de.danoeh.antennapod.core.util.DateFormatter; import de.danoeh.antennapod.fragment.FeedStatisticsDialogFragment; import de.danoeh.antennapod.view.PieChartView; +import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; +import java.util.Locale; /** * Adapter for the playback statistics list. @@ -18,26 +18,30 @@ import java.util.List; public class PlaybackStatisticsListAdapter extends StatisticsListAdapter { private final Fragment fragment; - boolean countAll = true; + private long timeFilterFrom = 0; + private long timeFilterTo = Long.MAX_VALUE; + private boolean includeMarkedAsPlayed = false; public PlaybackStatisticsListAdapter(Fragment fragment) { super(fragment.getContext()); this.fragment = fragment; } - public void setCountAll(boolean countAll) { - this.countAll = countAll; + public void setTimeFilter(boolean includeMarkedAsPlayed, long timeFilterFrom, long timeFilterTo) { + this.includeMarkedAsPlayed = includeMarkedAsPlayed; + this.timeFilterFrom = timeFilterFrom; + this.timeFilterTo = timeFilterTo; } @Override String getHeaderCaption() { - long usageCounting = UserPreferences.getUsageCountingDateMillis(); - if (usageCounting > 0) { - String date = DateFormatter.formatAbbrev(context, new Date(usageCounting)); - return context.getString(R.string.statistics_counting_since, date); - } else { - return context.getString(R.string.total_time_listened_to_podcasts); + if (includeMarkedAsPlayed) { + return context.getString(R.string.statistics_counting_total); } + SimpleDateFormat dateFormat = new SimpleDateFormat("MMM yyyy", Locale.getDefault()); + String dateFrom = dateFormat.format(new Date(timeFilterFrom)); + String dateTo = dateFormat.format(new Date(timeFilterTo)); + return context.getString(R.string.statistics_counting_range, dateFrom, dateTo); } @Override @@ -50,14 +54,14 @@ public class PlaybackStatisticsListAdapter extends StatisticsListAdapter { float[] dataValues = new float[statisticsData.size()]; for (int i = 0; i < statisticsData.size(); i++) { StatisticsItem item = statisticsData.get(i); - dataValues[i] = countAll ? item.timePlayedCountAll : item.timePlayed; + dataValues[i] = item.timePlayed; } return new PieChartView.PieChartData(dataValues); } @Override void onBindFeedViewHolder(StatisticsHolder holder, StatisticsItem statsItem) { - long time = countAll ? statsItem.timePlayedCountAll : statsItem.timePlayed; + long time = statsItem.timePlayed; holder.value.setText(Converter.shortLocalizedDuration(context, time)); holder.itemView.setOnClickListener(v -> { diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java index 23b5cfdce..f0451e974 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java @@ -48,10 +48,7 @@ public abstract class StatisticsListAdapter extends RecyclerView.Adapter<Recycle public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { LayoutInflater inflater = LayoutInflater.from(context); if (viewType == TYPE_HEADER) { - View view = inflater.inflate(R.layout.statistics_listitem_total, parent, false); - TextView totalText = view.findViewById(R.id.total_description); - totalText.setText(getHeaderCaption()); - return new HeaderHolder(view); + return new HeaderHolder(inflater.inflate(R.layout.statistics_listitem_total, parent, false)); } return new StatisticsHolder(inflater.inflate(R.layout.statistics_listitem, parent, false)); } @@ -62,6 +59,7 @@ public abstract class StatisticsListAdapter extends RecyclerView.Adapter<Recycle HeaderHolder holder = (HeaderHolder) h; holder.pieChart.setData(pieChartData); holder.totalTime.setText(getHeaderValue()); + holder.totalText.setText(getHeaderCaption()); } else { StatisticsHolder holder = (StatisticsHolder) h; StatisticsItem statsItem = statisticsData.get(position - 1); @@ -90,11 +88,13 @@ public abstract class StatisticsListAdapter extends RecyclerView.Adapter<Recycle static class HeaderHolder extends RecyclerView.ViewHolder { TextView totalTime; PieChartView pieChart; + TextView totalText; HeaderHolder(View itemView) { super(itemView); totalTime = itemView.findViewById(R.id.total_time); pieChart = itemView.findViewById(R.id.pie_chart); + totalText = itemView.findViewById(R.id.total_description); } } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/YearStatisticsListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/YearStatisticsListAdapter.java new file mode 100644 index 000000000..ad20574b3 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/adapter/YearStatisticsListAdapter.java @@ -0,0 +1,121 @@ +package de.danoeh.antennapod.adapter; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.storage.DBReader; +import de.danoeh.antennapod.core.util.LongList; +import de.danoeh.antennapod.view.LineChartView; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +/** + * Adapter for the yearly playback statistics list. + */ +public class YearStatisticsListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { + private static final int TYPE_HEADER = 0; + private static final int TYPE_FEED = 1; + final Context context; + private List<DBReader.MonthlyStatisticsItem> statisticsData = new ArrayList<>(); + LineChartView.LineChartData lineChartData; + + public YearStatisticsListAdapter(Context context) { + this.context = context; + } + + @Override + public int getItemCount() { + return statisticsData.size() + 1; + } + + @Override + public int getItemViewType(int position) { + return position == 0 ? TYPE_HEADER : TYPE_FEED; + } + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + LayoutInflater inflater = LayoutInflater.from(context); + if (viewType == TYPE_HEADER) { + return new HeaderHolder(inflater.inflate(R.layout.statistics_listitem_linechart, parent, false)); + } + return new StatisticsHolder(inflater.inflate(R.layout.statistics_year_listitem, parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder h, int position) { + if (getItemViewType(position) == TYPE_HEADER) { + HeaderHolder holder = (HeaderHolder) h; + holder.lineChart.setData(lineChartData); + } else { + StatisticsHolder holder = (StatisticsHolder) h; + DBReader.MonthlyStatisticsItem statsItem = statisticsData.get(position - 1); + holder.year.setText(String.format(Locale.getDefault(), "%d ", statsItem.year)); + holder.hours.setText(String.format(Locale.getDefault(), "%.1f ", statsItem.timePlayed / 3600000.0f) + + context.getString(R.string.time_hours)); + } + } + + public void update(List<DBReader.MonthlyStatisticsItem> statistics) { + int lastYear = statistics.size() > 0 ? statistics.get(0).year : 0; + int lastDataPoint = statistics.size() > 0 ? (statistics.get(0).month - 1) + lastYear * 12 : 0; + long yearSum = 0; + statisticsData.clear(); + LongList lineChartValues = new LongList(); + LongList lineChartHorizontalLines = new LongList(); + for (DBReader.MonthlyStatisticsItem statistic : statistics) { + if (statistic.year != lastYear) { + DBReader.MonthlyStatisticsItem yearAggregate = new DBReader.MonthlyStatisticsItem(); + yearAggregate.year = lastYear; + yearAggregate.timePlayed = yearSum; + statisticsData.add(yearAggregate); + yearSum = 0; + lastYear = statistic.year; + lineChartHorizontalLines.add(lineChartValues.size()); + } + yearSum += statistic.timePlayed; + while (lastDataPoint + 1 < (statistic.month - 1) + statistic.year * 12) { + lineChartValues.add(0); // Compensate for months without playback + lastDataPoint++; + } + lineChartValues.add(statistic.timePlayed); + lastDataPoint = (statistic.month - 1) + statistic.year * 12; + } + DBReader.MonthlyStatisticsItem yearAggregate = new DBReader.MonthlyStatisticsItem(); + yearAggregate.year = lastYear; + yearAggregate.timePlayed = yearSum; + statisticsData.add(yearAggregate); + Collections.reverse(statisticsData); + lineChartData = new LineChartView.LineChartData(lineChartValues.toArray(), lineChartHorizontalLines.toArray()); + notifyDataSetChanged(); + } + + static class HeaderHolder extends RecyclerView.ViewHolder { + LineChartView lineChart; + + HeaderHolder(View itemView) { + super(itemView); + lineChart = itemView.findViewById(R.id.lineChart); + } + } + + static class StatisticsHolder extends RecyclerView.ViewHolder { + TextView year; + TextView hours; + + StatisticsHolder(View itemView) { + super(itemView); + year = itemView.findViewById(R.id.yearLabel); + hours = itemView.findViewById(R.id.hoursLabel); + } + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java b/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java index 767845cb4..c8096e9c5 100644 --- a/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java +++ b/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java @@ -39,10 +39,6 @@ public class PodcastSearchResult { this.author = author; } - private PodcastSearchResult(String title, @Nullable String imageUrl, @Nullable String feedUrl) { - this(title, imageUrl, feedUrl, ""); - } - public static PodcastSearchResult dummy() { return new PodcastSearchResult("", "", "", ""); } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedStatisticsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedStatisticsFragment.java index e85c2a386..d6ab34855 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedStatisticsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedStatisticsFragment.java @@ -17,7 +17,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; -import java.util.List; +import java.util.Collections; import java.util.Locale; public class FeedStatisticsFragment extends Fragment { @@ -60,8 +60,11 @@ public class FeedStatisticsFragment extends Fragment { private void loadStatistics() { disposable = Observable.fromCallable(() -> { - List<StatisticsItem> statisticsData = DBReader.getStatistics(); - for (StatisticsItem statisticsItem : statisticsData) { + DBReader.StatisticsResult statisticsData = DBReader.getStatistics(true, 0, Long.MAX_VALUE); + Collections.sort(statisticsData.feedTime, (item1, item2) -> + Long.compare(item2.timePlayed, item1.timePlayed)); + + for (StatisticsItem statisticsItem : statisticsData.feedTime) { if (statisticsItem.feed.getId() == feedId) { return statisticsItem; } @@ -77,7 +80,6 @@ public class FeedStatisticsFragment extends Fragment { viewBinding.startedTotalLabel.setText(String.format(Locale.getDefault(), "%d / %d", s.episodesStarted, s.episodes)); viewBinding.timePlayedLabel.setText(Converter.shortLocalizedDuration(getContext(), s.timePlayed)); - viewBinding.durationPlayedLabel.setText(Converter.shortLocalizedDuration(getContext(), s.timePlayedCountAll)); viewBinding.totalDurationLabel.setText(Converter.shortLocalizedDuration(getContext(), s.time)); viewBinding.onDeviceLabel.setText(String.format(Locale.getDefault(), "%d", s.episodesDownloadCount)); viewBinding.spaceUsedLabel.setText(Formatter.formatShortFileSize(getContext(), s.totalDownloadSize)); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java index 715786921..7d9814998 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java @@ -283,7 +283,7 @@ public class ItemFragment extends Fragment { if (item.getPubDate() != null) { String pubDateStr = DateFormatter.formatAbbrev(getActivity(), item.getPubDate()); txtvPublished.setText(pubDateStr); - txtvPublished.setContentDescription(DateFormatter.formatForAccessibility(getContext(), item.getPubDate())); + txtvPublished.setContentDescription(DateFormatter.formatForAccessibility(item.getPubDate())); } RequestOptions options = new RequestOptions() diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java index 6526be005..1a3962de2 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java @@ -335,8 +335,8 @@ public class SearchFragment extends Fragment { return new Pair<>(Collections.emptyList(), Collections.emptyList()); } long feed = getArguments().getLong(ARG_FEED); - List<FeedItem> items = FeedSearcher.searchFeedItems(getContext(), query, feed); - List<Feed> feeds = FeedSearcher.searchFeeds(getContext(), query); + List<FeedItem> items = FeedSearcher.searchFeedItems(query, feed); + List<Feed> feeds = FeedSearcher.searchFeeds(query); return new Pair<>(items, feeds); } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/actions/FeedMultiSelectActionHandler.java b/app/src/main/java/de/danoeh/antennapod/fragment/actions/FeedMultiSelectActionHandler.java index e3dfe8ade..9067b17a7 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/actions/FeedMultiSelectActionHandler.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/actions/FeedMultiSelectActionHandler.java @@ -8,8 +8,6 @@ import androidx.core.util.Consumer; import com.google.android.material.snackbar.Snackbar; -import java.text.DecimalFormat; -import java.text.DecimalFormatSymbols; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -66,9 +64,6 @@ public class FeedMultiSelectActionHandler { preferenceSwitchDialog.openDialog(); } - private static final DecimalFormat SPEED_FORMAT = - new DecimalFormat("0.00", DecimalFormatSymbols.getInstance(Locale.US)); - private void playbackSpeedPrefHandler() { PlaybackSpeedFeedSettingDialogBinding viewBinding = PlaybackSpeedFeedSettingDialogBinding.inflate(activity.getLayoutInflater()); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java index 62e2e30d1..655cd6ed4 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java @@ -15,8 +15,6 @@ import de.danoeh.antennapod.net.sync.gpoddernet.model.GpodnetTag; * Use the newInstance method of this class to create a new TagFragment. */ public class TagFragment extends PodcastListFragment { - - private static final String TAG = "TagFragment"; private static final int PODCAST_COUNT = 50; private GpodnetTag tag; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/DownloadStatisticsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/DownloadStatisticsFragment.java index ff94cc20c..f8f489fc4 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/DownloadStatisticsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/DownloadStatisticsFragment.java @@ -3,6 +3,7 @@ package de.danoeh.antennapod.fragment.preferences; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; +import android.view.Menu; import android.view.View; import android.view.ViewGroup; import android.widget.ProgressBar; @@ -16,14 +17,12 @@ import androidx.recyclerview.widget.RecyclerView; import de.danoeh.antennapod.R; import de.danoeh.antennapod.adapter.DownloadStatisticsListAdapter; import de.danoeh.antennapod.core.storage.DBReader; -import de.danoeh.antennapod.core.storage.StatisticsItem; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; import java.util.Collections; -import java.util.List; /** * Displays the 'download statistics' screen @@ -36,14 +35,10 @@ public class DownloadStatisticsFragment extends Fragment { private ProgressBar progressBar; private DownloadStatisticsListAdapter listAdapter; - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - } - @Nullable @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { View root = inflater.inflate(R.layout.statistics_activity, container, false); downloadStatisticsList = root.findViewById(R.id.statistics_list); progressBar = root.findViewById(R.id.progressBar); @@ -59,6 +54,13 @@ public class DownloadStatisticsFragment extends Fragment { refreshDownloadStatistics(); } + @Override + public void onPrepareOptionsMenu(@NonNull Menu menu) { + super.onPrepareOptionsMenu(menu); + menu.findItem(R.id.statistics_reset).setVisible(false); + menu.findItem(R.id.statistics_filter).setVisible(false); + } + private void refreshDownloadStatistics() { progressBar.setVisibility(View.VISIBLE); downloadStatisticsList.setVisibility(View.GONE); @@ -72,15 +74,16 @@ public class DownloadStatisticsFragment extends Fragment { disposable = Observable.fromCallable(() -> { - List<StatisticsItem> statisticsData = DBReader.getStatistics(); - Collections.sort(statisticsData, (item1, item2) -> + // Filters do not matter here + DBReader.StatisticsResult statisticsData = DBReader.getStatistics(false, 0, Long.MAX_VALUE); + Collections.sort(statisticsData.feedTime, (item1, item2) -> Long.compare(item2.totalDownloadSize, item1.totalDownloadSize)); return statisticsData; }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(result -> { - listAdapter.update(result); + listAdapter.update(result.feedTime); progressBar.setVisibility(View.GONE); downloadStatisticsList.setVisibility(View.VISIBLE); }, error -> Log.e(TAG, Log.getStackTraceString(error))); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java index 5156de432..5bbc8f0a0 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java @@ -31,7 +31,6 @@ import de.danoeh.antennapod.core.export.ExportWriter; import de.danoeh.antennapod.core.export.favorites.FavoritesWriter; import de.danoeh.antennapod.core.export.html.HtmlWriter; import de.danoeh.antennapod.core.export.opml.OpmlWriter; -import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.storage.DatabaseExporter; import io.reactivex.Completable; import io.reactivex.Observable; @@ -263,7 +262,6 @@ public class ImportExportPreferencesFragment extends PreferenceFragmentCompat { .observeOn(AndroidSchedulers.mainThread()) .subscribe(() -> { showDatabaseImportSuccessDialog(); - UserPreferences.unsetUsageCountingDate(); progressDialog.dismiss(); }, this::showExportErrorDialog); } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java index 891d3737b..e2c5036df 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java @@ -29,7 +29,6 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat { private static final String PREF_VIEW_FORUM = "prefViewForum"; private static final String PREF_SEND_BUG_REPORT = "prefSendBugReport"; private static final String PREF_CATEGORY_PROJECT = "project"; - private static final String STATISTICS = "statistics"; private static final String PREF_ABOUT = "prefAbout"; private static final String PREF_NOTIFICATION = "notifications"; private static final String PREF_CONTRIBUTE = "prefContribute"; @@ -106,14 +105,6 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat { return true; } ); - findPreference(STATISTICS).setOnPreferenceClickListener( - preference -> { - getParentFragmentManager().beginTransaction() - .replace(R.id.settingsContainer, new StatisticsFragment()) - .addToBackStack(getString(R.string.statistics_label)).commit(); - return true; - } - ); findPreference(PREF_DOCUMENTATION).setOnPreferenceClickListener(preference -> { IntentUtils.openInBrowser(getContext(), "https://antennapod.org/documentation/"); return true; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java index 7fa2ed4d1..933a7d456 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java @@ -13,7 +13,6 @@ import de.danoeh.antennapod.activity.PreferenceActivity; import de.danoeh.antennapod.event.UnreadItemsUpdateEvent; import de.danoeh.antennapod.core.preferences.UsageStatistics; import de.danoeh.antennapod.core.preferences.UserPreferences; -import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil; import de.danoeh.antennapod.dialog.SkipPreferenceDialog; import de.danoeh.antennapod.dialog.VariableSpeedDialog; import java.util.Map; @@ -54,11 +53,6 @@ public class PlaybackPreferencesFragment extends PreferenceFragmentCompat { SkipPreferenceDialog.showSkipPreference(activity, SkipPreferenceDialog.SkipDirection.SKIP_FORWARD, null); return true; }); - if (!PictureInPictureUtil.supportsPictureInPicture(activity)) { - ListPreference behaviour = findPreference(UserPreferences.PREF_VIDEO_BEHAVIOR); - behaviour.setEntries(R.array.video_background_behavior_options_without_pip); - behaviour.setEntryValues(R.array.video_background_behavior_values_without_pip); - } findPreference(PREF_PLAYBACK_PREFER_STREAMING).setOnPreferenceChangeListener((preference, newValue) -> { // Update all visible lists to reflect new streaming action button EventBus.getDefault().post(new UnreadItemsUpdateEvent()); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackStatisticsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackStatisticsFragment.java deleted file mode 100644 index ba6164212..000000000 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackStatisticsFragment.java +++ /dev/null @@ -1,197 +0,0 @@ -package de.danoeh.antennapod.fragment.preferences; - -import android.content.Context; -import android.content.DialogInterface; -import android.content.SharedPreferences; -import android.os.Bundle; -import android.util.Log; -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.ProgressBar; -import android.widget.RadioButton; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; -import androidx.fragment.app.Fragment; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import de.danoeh.antennapod.R; -import de.danoeh.antennapod.adapter.PlaybackStatisticsListAdapter; -import de.danoeh.antennapod.core.dialog.ConfirmationDialog; -import de.danoeh.antennapod.core.preferences.UserPreferences; -import de.danoeh.antennapod.core.storage.DBReader; -import de.danoeh.antennapod.core.storage.DBWriter; -import de.danoeh.antennapod.core.storage.StatisticsItem; -import io.reactivex.Completable; -import io.reactivex.Observable; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.Disposable; -import io.reactivex.schedulers.Schedulers; - -import java.util.Collections; -import java.util.List; - -/** - * Displays the 'playback statistics' screen - */ -public class PlaybackStatisticsFragment extends Fragment { - private static final String TAG = PlaybackStatisticsFragment.class.getSimpleName(); - private static final String PREF_NAME = "StatisticsActivityPrefs"; - private static final String PREF_COUNT_ALL = "countAll"; - - private Disposable disposable; - private RecyclerView feedStatisticsList; - private ProgressBar progressBar; - private PlaybackStatisticsListAdapter listAdapter; - private boolean countAll = false; - private SharedPreferences prefs; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - prefs = getContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); - countAll = prefs.getBoolean(PREF_COUNT_ALL, false); - setHasOptionsMenu(true); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View root = inflater.inflate(R.layout.statistics_activity, container, false); - feedStatisticsList = root.findViewById(R.id.statistics_list); - progressBar = root.findViewById(R.id.progressBar); - listAdapter = new PlaybackStatisticsListAdapter(this); - listAdapter.setCountAll(countAll); - feedStatisticsList.setLayoutManager(new LinearLayoutManager(getContext())); - feedStatisticsList.setAdapter(listAdapter); - return root; - } - - @Override - public void onStart() { - super.onStart(); - refreshStatistics(); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - if (disposable != null) { - disposable.dispose(); - } - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.statistics, menu); - menu.findItem(R.id.statistics_reset).setEnabled(!countAll); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == R.id.statistics_mode) { - selectStatisticsMode(); - return true; - } - if (item.getItemId() == R.id.statistics_reset) { - confirmResetStatistics(); - return true; - } - return super.onOptionsItemSelected(item); - } - - private void selectStatisticsMode() { - View contentView = View.inflate(getContext(), R.layout.statistics_mode_select_dialog, null); - AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); - builder.setView(contentView); - builder.setTitle(R.string.statistics_mode); - - if (countAll) { - ((RadioButton) contentView.findViewById(R.id.statistics_mode_count_all)).setChecked(true); - } else { - ((RadioButton) contentView.findViewById(R.id.statistics_mode_normal)).setChecked(true); - } - - builder.setPositiveButton(android.R.string.ok, (dialog, which) -> { - countAll = ((RadioButton) contentView.findViewById(R.id.statistics_mode_count_all)).isChecked(); - listAdapter.setCountAll(countAll); - prefs.edit().putBoolean(PREF_COUNT_ALL, countAll).apply(); - refreshStatistics(); - getActivity().invalidateOptionsMenu(); - }); - - builder.show(); - } - - private void confirmResetStatistics() { - if (!countAll) { - ConfirmationDialog conDialog = new ConfirmationDialog( - getActivity(), - R.string.statistics_reset_data, - R.string.statistics_reset_data_msg) { - - @Override - public void onConfirmButtonPressed(DialogInterface dialog) { - dialog.dismiss(); - doResetStatistics(); - } - }; - conDialog.createNewDialog().show(); - } - } - - private void doResetStatistics() { - progressBar.setVisibility(View.VISIBLE); - feedStatisticsList.setVisibility(View.GONE); - if (disposable != null) { - disposable.dispose(); - } - - disposable = Completable.fromFuture(DBWriter.resetStatistics()) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(() -> { - refreshStatistics(); - UserPreferences.resetUsageCountingDate(); - }, error -> Log.e(TAG, Log.getStackTraceString(error))); - } - - private void refreshStatistics() { - progressBar.setVisibility(View.VISIBLE); - feedStatisticsList.setVisibility(View.GONE); - loadStatistics(); - } - - private void loadStatistics() { - if (disposable != null) { - disposable.dispose(); - } - disposable = Observable.fromCallable(this::fetchStatistics) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(result -> { - listAdapter.update(result); - progressBar.setVisibility(View.GONE); - feedStatisticsList.setVisibility(View.VISIBLE); - }, error -> Log.e(TAG, Log.getStackTraceString(error))); - } - - private List<StatisticsItem> fetchStatistics() { - List<StatisticsItem> statisticsData = DBReader.getStatistics(); - if (countAll) { - Collections.sort(statisticsData, (item1, item2) -> - Long.compare(item2.timePlayedCountAll, item1.timePlayedCountAll)); - } else { - Collections.sort(statisticsData, (item1, item2) -> - Long.compare(item2.timePlayed, item1.timePlayed)); - } - return statisticsData; - } -} diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StatisticsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StatisticsFragment.java index 2c72ab75b..1c5a6acd4 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StatisticsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StatisticsFragment.java @@ -16,28 +16,25 @@ import com.google.android.material.tabs.TabLayoutMediator; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.PreferenceActivity; +import de.danoeh.antennapod.fragment.PagedToolbarFragment; /** * Displays the 'statistics' screen */ -public class StatisticsFragment extends Fragment { +public class StatisticsFragment extends PagedToolbarFragment { public static final String TAG = "StatisticsFragment"; - private static final int POS_LISTENED_HOURS = 0; - private static final int POS_SPACE_TAKEN = 1; - private static final int TOTAL_COUNT = 2; - + private static final int POS_SUBSCRIPTIONS = 0; + private static final int POS_YEARS = 1; + private static final int POS_SPACE_TAKEN = 2; + private static final int TOTAL_COUNT = 3; private TabLayout tabLayout; private ViewPager2 viewPager; private Toolbar toolbar; @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - } - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); @@ -46,29 +43,28 @@ public class StatisticsFragment extends Fragment { View rootView = inflater.inflate(R.layout.pager_fragment, container, false); viewPager = rootView.findViewById(R.id.viewpager); toolbar = rootView.findViewById(R.id.toolbar); + toolbar.setTitle(getString(R.string.statistics_label)); + toolbar.inflateMenu(R.menu.statistics); + toolbar.setNavigationOnClickListener(v -> getParentFragmentManager().popBackStack()); viewPager.setAdapter(new StatisticsPagerAdapter(this)); // Give the TabLayout the ViewPager tabLayout = rootView.findViewById(R.id.sliding_tabs); + super.setupPagedToolbar(toolbar, viewPager); new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> { switch (position) { - case POS_LISTENED_HOURS: - tab.setText(R.string.playback_statistics_label); + case POS_SUBSCRIPTIONS: + tab.setText(R.string.subscriptions_label); + break; + case POS_YEARS: + tab.setText(R.string.years_statistics_label); break; case POS_SPACE_TAKEN: - tab.setText(R.string.download_statistics_label); + tab.setText(R.string.downloads_label); break; default: break; } }).attach(); - - if (getActivity().getClass() == PreferenceActivity.class) { - rootView.findViewById(R.id.toolbar).setVisibility(View.GONE); - } else { - toolbar.setTitle(getString(R.string.statistics_label)); - toolbar.setNavigationOnClickListener(v -> getParentFragmentManager().popBackStack()); - } - return rootView; } @@ -90,8 +86,10 @@ public class StatisticsFragment extends Fragment { @Override public Fragment createFragment(int position) { switch (position) { - case POS_LISTENED_HOURS: - return new PlaybackStatisticsFragment(); + case POS_SUBSCRIPTIONS: + return new SubscriptionStatisticsFragment(); + case POS_YEARS: + return new YearsStatisticsFragment(); default: case POS_SPACE_TAKEN: return new DownloadStatisticsFragment(); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java index e5617b8ea..00ff9fb64 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java @@ -10,7 +10,6 @@ import de.danoeh.antennapod.dialog.ChooseDataFolderDialog; import java.io.File; public class StoragePreferencesFragment extends PreferenceFragmentCompat { - private static final String TAG = "StoragePrefFragment"; private static final String PREF_CHOOSE_DATA_DIR = "prefChooseDataDir"; private static final String PREF_IMPORT_EXPORT = "prefImportExport"; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/SubscriptionStatisticsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/SubscriptionStatisticsFragment.java new file mode 100644 index 000000000..ef701d35c --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/SubscriptionStatisticsFragment.java @@ -0,0 +1,279 @@ +package de.danoeh.antennapod.fragment.preferences; + +import android.content.Context; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ProgressBar; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.core.util.Pair; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.adapter.PlaybackStatisticsListAdapter; +import de.danoeh.antennapod.core.dialog.ConfirmationDialog; +import de.danoeh.antennapod.core.storage.DBReader; +import de.danoeh.antennapod.core.storage.DBWriter; +import de.danoeh.antennapod.databinding.StatisticsFilterDialogBinding; +import io.reactivex.Completable; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.Locale; + +/** + * Displays the 'playback statistics' screen + */ +public class SubscriptionStatisticsFragment extends Fragment { + private static final String TAG = SubscriptionStatisticsFragment.class.getSimpleName(); + private static final String PREF_NAME = "StatisticsActivityPrefs"; + private static final String PREF_INCLUDE_MARKED_PLAYED = "countAll"; + private static final String PREF_FILTER_FROM = "filterFrom"; + private static final String PREF_FILTER_TO = "filterTo"; + + private Disposable disposable; + private RecyclerView feedStatisticsList; + private ProgressBar progressBar; + private PlaybackStatisticsListAdapter listAdapter; + private boolean includeMarkedAsPlayed = false; + private long timeFilterFrom = 0; + private long timeFilterTo = Long.MAX_VALUE; + private SharedPreferences prefs; + private DBReader.StatisticsResult statisticsResult; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + prefs = getContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); + includeMarkedAsPlayed = prefs.getBoolean(PREF_INCLUDE_MARKED_PLAYED, false); + timeFilterFrom = prefs.getLong(PREF_FILTER_FROM, 0); + timeFilterTo = prefs.getLong(PREF_FILTER_TO, Long.MAX_VALUE); + setHasOptionsMenu(true); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + View root = inflater.inflate(R.layout.statistics_activity, container, false); + feedStatisticsList = root.findViewById(R.id.statistics_list); + progressBar = root.findViewById(R.id.progressBar); + listAdapter = new PlaybackStatisticsListAdapter(this); + feedStatisticsList.setLayoutManager(new LinearLayoutManager(getContext())); + feedStatisticsList.setAdapter(listAdapter); + return root; + } + + @Override + public void onStart() { + super.onStart(); + refreshStatistics(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + if (disposable != null) { + disposable.dispose(); + } + } + + @Override + public void onPrepareOptionsMenu(@NonNull Menu menu) { + super.onPrepareOptionsMenu(menu); + menu.findItem(R.id.statistics_reset).setVisible(true); + menu.findItem(R.id.statistics_filter).setVisible(true); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.statistics_filter) { + selectStatisticsFilter(); + return true; + } else if (item.getItemId() == R.id.statistics_reset) { + confirmResetStatistics(); + return true; + } + return super.onOptionsItemSelected(item); + } + + private void selectStatisticsFilter() { + if (statisticsResult == null) { + return; + } + StatisticsFilterDialogBinding dialogBinding = StatisticsFilterDialogBinding.inflate(getLayoutInflater()); + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setView(dialogBinding.getRoot()); + builder.setTitle(R.string.filter); + dialogBinding.includeMarkedCheckbox.setOnCheckedChangeListener((compoundButton, checked) -> { + dialogBinding.timeToSpinner.setEnabled(!checked); + dialogBinding.timeFromSpinner.setEnabled(!checked); + dialogBinding.lastYearButton.setEnabled(!checked); + dialogBinding.allTimeButton.setEnabled(!checked); + dialogBinding.dateSelectionContainer.setAlpha(checked ? 0.5f : 1f); + }); + dialogBinding.includeMarkedCheckbox.setChecked(includeMarkedAsPlayed); + + Pair<String[], Long[]> filterDates = makeMonthlyList(statisticsResult.oldestDate); + + ArrayAdapter<String> adapterFrom = new ArrayAdapter<>(getContext(), + android.R.layout.simple_spinner_item, filterDates.first); + adapterFrom.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + dialogBinding.timeFromSpinner.setAdapter(adapterFrom); + for (int i = 0; i < filterDates.second.length; i++) { + if (filterDates.second[i] >= timeFilterFrom) { + dialogBinding.timeFromSpinner.setSelection(i); + break; + } + } + + ArrayAdapter<String> adapterTo = new ArrayAdapter<>(getContext(), + android.R.layout.simple_spinner_item, filterDates.first); + adapterTo.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + dialogBinding.timeToSpinner.setAdapter(adapterTo); + for (int i = 0; i < filterDates.second.length; i++) { + if (filterDates.second[i] >= timeFilterTo) { + dialogBinding.timeToSpinner.setSelection(i); + break; + } + } + + dialogBinding.allTimeButton.setOnClickListener(v -> { + dialogBinding.timeFromSpinner.setSelection(0); + dialogBinding.timeToSpinner.setSelection(filterDates.first.length - 1); + }); + dialogBinding.lastYearButton.setOnClickListener(v -> { + dialogBinding.timeFromSpinner.setSelection(Math.max(0, filterDates.first.length - 14)); + dialogBinding.timeToSpinner.setSelection(filterDates.first.length - 2); + }); + + builder.setPositiveButton(android.R.string.ok, (dialog, which) -> { + includeMarkedAsPlayed = dialogBinding.includeMarkedCheckbox.isChecked(); + if (includeMarkedAsPlayed) { + // We do not know the date at which something was marked as played, so filtering does not make sense + timeFilterFrom = 0; + timeFilterTo = Long.MAX_VALUE; + } else { + timeFilterFrom = filterDates.second[dialogBinding.timeFromSpinner.getSelectedItemPosition()]; + timeFilterTo = filterDates.second[dialogBinding.timeToSpinner.getSelectedItemPosition()]; + } + prefs.edit() + .putBoolean(PREF_INCLUDE_MARKED_PLAYED, includeMarkedAsPlayed) + .putLong(PREF_FILTER_FROM, timeFilterFrom) + .putLong(PREF_FILTER_TO, timeFilterTo) + .apply(); + refreshStatistics(); + }); + builder.show(); + } + + private Pair<String[], Long[]> makeMonthlyList(long oldestDate) { + Calendar date = Calendar.getInstance(); + date.setTimeInMillis(oldestDate); + date.set(Calendar.DAY_OF_MONTH, 1); + ArrayList<String> names = new ArrayList<>(); + ArrayList<Long> timestamps = new ArrayList<>(); + SimpleDateFormat dateFormat = new SimpleDateFormat("MMM yyyy", Locale.getDefault()); + while (date.getTimeInMillis() < System.currentTimeMillis()) { + names.add(dateFormat.format(new Date(date.getTimeInMillis()))); + timestamps.add(date.getTimeInMillis()); + if (date.get(Calendar.MONTH) == Calendar.DECEMBER) { + date.set(Calendar.MONTH, Calendar.JANUARY); + date.set(Calendar.YEAR, date.get(Calendar.YEAR) + 1); + } else { + date.set(Calendar.MONTH, date.get(Calendar.MONTH) + 1); + } + } + names.add(getString(R.string.statistics_today)); + timestamps.add(Long.MAX_VALUE); + return new Pair<>(names.toArray(new String[0]), timestamps.toArray(new Long[0])); + } + + private void confirmResetStatistics() { + ConfirmationDialog conDialog = new ConfirmationDialog( + getActivity(), + R.string.statistics_reset_data, + R.string.statistics_reset_data_msg) { + + @Override + public void onConfirmButtonPressed(DialogInterface dialog) { + dialog.dismiss(); + doResetStatistics(); + } + }; + conDialog.createNewDialog().show(); + } + + private void doResetStatistics() { + progressBar.setVisibility(View.VISIBLE); + feedStatisticsList.setVisibility(View.GONE); + if (disposable != null) { + disposable.dispose(); + } + + includeMarkedAsPlayed = false; + timeFilterFrom = 0; + timeFilterTo = Long.MAX_VALUE; + prefs.edit() + .putBoolean(PREF_INCLUDE_MARKED_PLAYED, includeMarkedAsPlayed) + .putLong(PREF_FILTER_FROM, timeFilterFrom) + .putLong(PREF_FILTER_TO, timeFilterTo) + .apply(); + + disposable = Completable.fromFuture(DBWriter.resetStatistics()) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(this::refreshStatistics, error -> Log.e(TAG, Log.getStackTraceString(error))); + } + + private void refreshStatistics() { + progressBar.setVisibility(View.VISIBLE); + feedStatisticsList.setVisibility(View.GONE); + loadStatistics(); + } + + private void loadStatistics() { + if (disposable != null) { + disposable.dispose(); + } + disposable = Observable.fromCallable( + () -> { + DBReader.StatisticsResult statisticsData = DBReader.getStatistics( + includeMarkedAsPlayed, timeFilterFrom, timeFilterTo); + Collections.sort(statisticsData.feedTime, (item1, item2) -> + Long.compare(item2.timePlayed, item1.timePlayed)); + return statisticsData; + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(result -> { + statisticsResult = result; + // When "from" is "today", set it to today + listAdapter.setTimeFilter(includeMarkedAsPlayed, Math.max( + Math.min(timeFilterFrom, System.currentTimeMillis()), result.oldestDate), + Math.min(timeFilterTo, System.currentTimeMillis())); + listAdapter.update(result.feedTime); + progressBar.setVisibility(View.GONE); + feedStatisticsList.setVisibility(View.VISIBLE); + }, error -> Log.e(TAG, Log.getStackTraceString(error))); + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/SwipePreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/SwipePreferencesFragment.java index 3d9709f74..19099a380 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/SwipePreferencesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/SwipePreferencesFragment.java @@ -11,8 +11,6 @@ import de.danoeh.antennapod.fragment.QueueFragment; public class SwipePreferencesFragment extends PreferenceFragmentCompat { private static final String PREF_SWIPE_FEED = "prefSwipeFeed"; private static final String PREF_SWIPE_QUEUE = "prefSwipeQueue"; - //private static final String PREF_SWIPE_INBOX = "prefSwipeInbox"; - //private static final String PREF_SWIPE_EPISODES = "prefSwipeEpisodes"; @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/YearsStatisticsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/YearsStatisticsFragment.java new file mode 100644 index 000000000..c58a59801 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/YearsStatisticsFragment.java @@ -0,0 +1,87 @@ +package de.danoeh.antennapod.fragment.preferences; + +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ProgressBar; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.adapter.YearStatisticsListAdapter; +import de.danoeh.antennapod.core.storage.DBReader; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; + +/** + * Displays the yearly statistics screen + */ +public class YearsStatisticsFragment extends Fragment { + private static final String TAG = YearsStatisticsFragment.class.getSimpleName(); + + private Disposable disposable; + private RecyclerView yearStatisticsList; + private ProgressBar progressBar; + private YearStatisticsListAdapter listAdapter; + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + View root = inflater.inflate(R.layout.statistics_activity, container, false); + yearStatisticsList = root.findViewById(R.id.statistics_list); + progressBar = root.findViewById(R.id.progressBar); + listAdapter = new YearStatisticsListAdapter(getContext()); + yearStatisticsList.setLayoutManager(new LinearLayoutManager(getContext())); + yearStatisticsList.setAdapter(listAdapter); + return root; + } + + @Override + public void onStart() { + super.onStart(); + refreshStatistics(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + if (disposable != null) { + disposable.dispose(); + } + } + + @Override + public void onPrepareOptionsMenu(@NonNull Menu menu) { + super.onPrepareOptionsMenu(menu); + menu.findItem(R.id.statistics_reset).setVisible(false); + menu.findItem(R.id.statistics_filter).setVisible(false); + } + + private void refreshStatistics() { + progressBar.setVisibility(View.VISIBLE); + yearStatisticsList.setVisibility(View.GONE); + loadStatistics(); + } + + private void loadStatistics() { + if (disposable != null) { + disposable.dispose(); + } + disposable = Observable.fromCallable(DBReader::getMonthlyTimeStatistics) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(result -> { + listAdapter.update(result); + progressBar.setVisibility(View.GONE); + yearStatisticsList.setVisibility(View.VISIBLE); + }, error -> Log.e(TAG, Log.getStackTraceString(error))); + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/ContributorsPagerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/ContributorsPagerFragment.java index 20cef1313..caa8031ae 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/ContributorsPagerFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/about/ContributorsPagerFragment.java @@ -17,19 +17,12 @@ import de.danoeh.antennapod.activity.PreferenceActivity; * Displays the 'about->Contributors' pager screen. */ public class ContributorsPagerFragment extends Fragment { - - public static final String TAG = "StatisticsFragment"; - private static final int POS_DEVELOPERS = 0; private static final int POS_TRANSLATORS = 1; private static final int POS_SPECIAL_THANKS = 2; private static final int TOTAL_COUNT = 3; @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - } - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/synchronization/GpodderAuthenticationFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/synchronization/GpodderAuthenticationFragment.java index 9dfe6840c..358985cea 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/synchronization/GpodderAuthenticationFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/synchronization/GpodderAuthenticationFragment.java @@ -250,18 +250,6 @@ public class GpodderAuthenticationFragment extends DialogFragment { return false; } - private GpodnetDevice findDevice(String id) { - if (devices == null) { - return null; - } - for (GpodnetDevice device : devices) { - if (device.getId().equals(id)) { - return device; - } - } - return null; - } - private void setupFinishView(View view) { final Button sync = view.findViewById(R.id.butSyncNow); diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java index af35bbac9..a2c5ca3ff 100644 --- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java +++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java @@ -39,9 +39,6 @@ public class PreferenceUpgrader { private static void upgrade(int oldVersion, Context context) { if (oldVersion == -1) { //New installation - if (UserPreferences.getUsageCountingDateMillis() < 0) { - UserPreferences.resetUsageCountingDate(); - } return; } if (oldVersion < 1070196) { @@ -93,9 +90,6 @@ public class PreferenceUpgrader { UserPreferences.setEnqueueLocation(enqueueLocation); } } - if (oldVersion < 1080100) { - prefs.edit().putString(UserPreferences.PREF_VIDEO_BEHAVIOR, "pip").apply(); - } if (oldVersion < 2010300) { // Migrate hardware button preferences if (prefs.getBoolean("prefHardwareForwardButtonSkips", false)) { diff --git a/app/src/main/java/de/danoeh/antennapod/view/LineChartView.java b/app/src/main/java/de/danoeh/antennapod/view/LineChartView.java new file mode 100644 index 000000000..0eb225e8e --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/view/LineChartView.java @@ -0,0 +1,138 @@ +package de.danoeh.antennapod.view; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.DashPathEffect; +import android.graphics.LinearGradient; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PixelFormat; +import android.graphics.Shader; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import androidx.annotation.NonNull; +import androidx.appcompat.widget.AppCompatImageView; +import androidx.appcompat.widget.ThemeUtils; +import de.danoeh.antennapod.R; +import io.reactivex.annotations.Nullable; + +public class LineChartView extends AppCompatImageView { + private LineChartDrawable drawable; + + public LineChartView(Context context) { + super(context); + setup(); + } + + public LineChartView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + setup(); + } + + public LineChartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setup(); + } + + @SuppressLint("ClickableViewAccessibility") + private void setup() { + drawable = new LineChartDrawable(); + setImageDrawable(drawable); + } + + /** + * Set of data values to display. + */ + public void setData(LineChartData data) { + drawable.data = data; + } + + public static class LineChartData { + private final long valueMax; + private final long[] values; + private final long[] verticalLines; + + public LineChartData(long[] values, long[] verticalLines) { + this.values = values; + long valueMax = 0; + for (long datum : values) { + valueMax = Math.max(datum, valueMax); + } + this.valueMax = valueMax; + this.verticalLines = verticalLines; + } + + public float getHeight(int item) { + return (float) values[item] / valueMax; + } + } + + private class LineChartDrawable extends Drawable { + private LineChartData data; + private final Paint paintLine; + private final Paint paintBackground; + private final Paint paintVerticalLines; + + private LineChartDrawable() { + paintLine = new Paint(); + paintLine.setFlags(Paint.ANTI_ALIAS_FLAG); + paintLine.setStyle(Paint.Style.STROKE); + paintLine.setStrokeJoin(Paint.Join.ROUND); + paintLine.setStrokeCap(Paint.Cap.ROUND); + paintLine.setColor(ThemeUtils.getThemeAttrColor(getContext(), R.attr.colorAccent)); + paintBackground = new Paint(); + paintBackground.setStyle(Paint.Style.FILL); + paintVerticalLines = new Paint(); + paintVerticalLines.setStyle(Paint.Style.STROKE); + paintVerticalLines.setPathEffect(new DashPathEffect(new float[] {10f, 10f}, 0f)); + paintVerticalLines.setColor(0x66777777); + } + + @Override + public void draw(@NonNull Canvas canvas) { + float width = getBounds().width(); + float height = getBounds().height(); + float usableHeight = height * 0.9f; + float stepSize = width / (data.values.length + 1); + + paintVerticalLines.setStrokeWidth(height * 0.005f); + for (long line : data.verticalLines) { + canvas.drawLine((line + 1) * stepSize, 0, (line + 1) * stepSize, height, paintVerticalLines); + } + + paintLine.setStrokeWidth(height * 0.015f); + Path path = new Path(); + for (int i = 0; i < data.values.length; i++) { + if (i == 0) { + path.moveTo((i + 1) * stepSize, (1 - data.getHeight(i)) * usableHeight + height * 0.05f); + } else { + path.lineTo((i + 1) * stepSize, (1 - data.getHeight(i)) * usableHeight + height * 0.05f); + } + } + canvas.drawPath(path, paintLine); + + path.lineTo(data.values.length * stepSize, height); + path.lineTo(stepSize, height); + paintBackground.setShader(new LinearGradient(0, 0, 0, height, + (ThemeUtils.getThemeAttrColor(getContext(), R.attr.colorAccent) & 0xffffff) + 0x66000000, + Color.TRANSPARENT, Shader.TileMode.CLAMP)); + canvas.drawPath(path, paintBackground); + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } + + @Override + public void setAlpha(int alpha) { + } + + @Override + public void setColorFilter(ColorFilter cf) { + } + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java b/app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java index d231e522f..c1ab3a7a6 100644 --- a/app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java +++ b/app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java @@ -104,7 +104,7 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder { title.setText(item.getTitle()); leftPadding.setContentDescription(item.getTitle()); pubDate.setText(DateFormatter.formatAbbrev(activity, item.getPubDate())); - pubDate.setContentDescription(DateFormatter.formatForAccessibility(activity, item.getPubDate())); + pubDate.setContentDescription(DateFormatter.formatForAccessibility(item.getPubDate())); isNew.setVisibility(item.isNew() ? View.VISIBLE : View.GONE); isFavorite.setVisibility(item.isTagged(FeedItem.TAG_FAVORITE) ? View.VISIBLE : View.GONE); isInQueue.setVisibility(item.isTagged(FeedItem.TAG_QUEUE) ? View.VISIBLE : View.GONE); diff --git a/app/src/main/res/layout/feed_statistics.xml b/app/src/main/res/layout/feed_statistics.xml index f8f5ac555..7897a7d5f 100644 --- a/app/src/main/res/layout/feed_statistics.xml +++ b/app/src/main/res/layout/feed_statistics.xml @@ -46,24 +46,6 @@ <TextView android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="@string/statistics_duration_played_episodes" /> - - <TextView - android:id="@+id/durationPlayedLabel" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginLeft="8dp" - android:layout_marginStart="8dp" - tools:text="0 min" /> - - </TableRow> - - <TableRow - android:tag="detailed"> - - <TextView - android:layout_width="match_parent" - android:layout_height="wrap_content" android:text="@string/statistics_total_duration" /> <TextView diff --git a/app/src/main/res/layout/statistics_filter_dialog.xml b/app/src/main/res/layout/statistics_filter_dialog.xml new file mode 100644 index 000000000..d37226c07 --- /dev/null +++ b/app/src/main/res/layout/statistics_filter_dialog.xml @@ -0,0 +1,95 @@ +<?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="wrap_content" + android:orientation="vertical" + android:padding="16dp"> + + <CheckBox + android:id="@+id/includeMarkedCheckbox" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/statistics_include_marked" + android:layout_marginBottom="8dp" /> + + <LinearLayout + android:id="@+id/dateSelectionContainer" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <TextView + android:layout_width="0dp" + android:layout_height="wrap_content" + android:text="@string/statistics_from" + android:padding="4dp" + android:layout_weight="1" /> + + <TextView + android:layout_width="0dp" + android:layout_height="wrap_content" + android:text="@string/statistics_to" + android:padding="4dp" + android:layout_weight="1" /> + + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <Spinner + android:id="@+id/timeFromSpinner" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" /> + + <Spinner + android:id="@+id/timeToSpinner" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" /> + + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <Button + android:id="@+id/lastYearButton" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:text="@string/statistics_filter_last_year" + android:layout_weight="1" + android:layout_marginEnd="4dp" + style="@style/Widget.MaterialComponents.Button.OutlinedButton" /> + + <Button + android:id="@+id/allTimeButton" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:text="@string/statistics_filter_all_time" + android:layout_weight="1" + android:layout_marginStart="4dp" + style="@style/Widget.MaterialComponents.Button.OutlinedButton" /> + + </LinearLayout> + + </LinearLayout> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/statistics_speed_not_counted" + android:layout_marginTop="16dp" /> + +</LinearLayout> diff --git a/app/src/main/res/layout/statistics_listitem_linechart.xml b/app/src/main/res/layout/statistics_listitem_linechart.xml new file mode 100644 index 000000000..0794a1c09 --- /dev/null +++ b/app/src/main/res/layout/statistics_listitem_linechart.xml @@ -0,0 +1,22 @@ +<?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="wrap_content" + android:orientation="vertical" + android:padding="16dp"> + + <de.danoeh.antennapod.view.LineChartView + android:id="@+id/lineChart" + android:layout_width="match_parent" + android:layout_height="200dp" + android:layout_marginStart="8dp" + android:layout_marginEnd="8dp" /> + + <View + android:layout_width="match_parent" + android:layout_height="1dp" + android:layout_marginTop="16dp" + android:background="?android:attr/dividerVertical" /> + +</LinearLayout> diff --git a/app/src/main/res/layout/statistics_listitem_total.xml b/app/src/main/res/layout/statistics_listitem_total.xml index 628e26c1f..11ff24977 100644 --- a/app/src/main/res/layout/statistics_listitem_total.xml +++ b/app/src/main/res/layout/statistics_listitem_total.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" +<RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" @@ -16,33 +17,32 @@ android:layout_marginLeft="8dp" /> <TextView - android:id="@+id/total_description" - android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:layout_centerHorizontal="true" - android:textAlignment="center" - android:layout_marginLeft="56dp" - android:layout_marginRight="56dp" - android:maxLines="3" - android:layout_above="@id/total_time" - android:textSize="14sp" /> - - <TextView + android:id="@+id/total_time" android:layout_width="match_parent" android:layout_height="wrap_content" - android:id="@+id/total_time" android:textColor="?android:attr/textColorPrimary" android:gravity="center_horizontal" android:textSize="28sp" + android:layout_marginBottom="4dp" + android:layout_above="@id/total_description" + tools:text="10.0 hours" /> + + <TextView + android:id="@+id/total_description" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:textAlignment="center" + android:maxLines="3" + android:textSize="14sp" android:layout_marginBottom="16dp" - android:layout_alignBottom="@id/pie_chart" - tools:text="10.0 hours"/> + android:layout_alignBottom="@id/pie_chart" /> <View android:layout_width="match_parent" android:layout_height="1dp" android:layout_marginTop="16dp" android:background="?android:attr/dividerVertical" - android:layout_below="@+id/pie_chart"/> + android:layout_below="@+id/pie_chart" /> -</RelativeLayout>
\ No newline at end of file +</RelativeLayout> diff --git a/app/src/main/res/layout/statistics_mode_select_dialog.xml b/app/src/main/res/layout/statistics_mode_select_dialog.xml deleted file mode 100644 index 8f8e1e657..000000000 --- a/app/src/main/res/layout/statistics_mode_select_dialog.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<RadioGroup xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - android:padding="16dp"> - - <TextView - android:text="@string/statistics_speed_not_counted" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginBottom="16dp"/> - - <RadioButton - android:id="@+id/statistics_mode_normal" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:text="@string/statistics_mode_normal"/> - - <RadioButton - android:id="@+id/statistics_mode_count_all" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:text="@string/statistics_mode_count_all"/> -</RadioGroup> diff --git a/app/src/main/res/layout/statistics_year_listitem.xml b/app/src/main/res/layout/statistics_year_listitem.xml new file mode 100644 index 000000000..48b910c7f --- /dev/null +++ b/app/src/main/res/layout/statistics_year_listitem.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:paddingLeft="16dp" + android:paddingRight="16dp" + android:paddingTop="16dp" + android:paddingBottom="8dp" + android:background="?android:attr/selectableItemBackground"> + + <TextView + android:id="@+id/yearLabel" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:lines="1" + android:textColor="?android:attr/textColorPrimary" + android:textSize="16sp" + tools:text="2020" /> + + <TextView + android:id="@+id/hoursLabel" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:lines="1" + android:textColor="?android:attr/textColorTertiary" + android:textSize="14sp" + tools:text="23 hours" /> + +</LinearLayout> diff --git a/app/src/main/res/menu/mediaplayer.xml b/app/src/main/res/menu/mediaplayer.xml index a9f15317b..e25872ade 100644 --- a/app/src/main/res/menu/mediaplayer.xml +++ b/app/src/main/res/menu/mediaplayer.xml @@ -52,10 +52,10 @@ </item> <item - android:id="@+id/player_go_to_picture_in_picture" - custom:showAsAction="collapseActionView" - android:title="@string/player_go_to_picture_in_picture" - android:visible="false"> + android:id="@+id/player_switch_to_audio_only" + custom:showAsAction="collapseActionView" + android:title="@string/player_switch_to_audio_only" + android:visible="false"> </item> <item diff --git a/app/src/main/res/menu/statistics.xml b/app/src/main/res/menu/statistics.xml index 9e4b7fab1..71bd12b63 100644 --- a/app/src/main/res/menu/statistics.xml +++ b/app/src/main/res/menu/statistics.xml @@ -5,14 +5,13 @@ <item android:id="@+id/statistics_reset" android:title="@string/statistics_reset_data" - custom:showAsAction="never" - /> + custom:showAsAction="never" /> <item - android:id="@+id/statistics_mode" + android:id="@+id/statistics_filter" android:icon="@drawable/ic_filter" - android:title="@string/statistics_mode" - custom:showAsAction="never"> + android:title="@string/filter" + custom:showAsAction="always"> </item> </menu> diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 91ba649d2..9967d7fd1 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -44,11 +44,6 @@ android:title="@string/notification_pref_fragment" android:icon="@drawable/ic_notifications"/> - <Preference - android:icon="@drawable/chart_box_outline" - android:key="statistics" - android:title="@string/statistics_label" /> - <PreferenceCategory android:key="project" android:title="@string/project_pref"> diff --git a/app/src/main/res/xml/preferences_playback.xml b/app/src/main/res/xml/preferences_playback.xml index add9e8d4c..969013056 100644 --- a/app/src/main/res/xml/preferences_playback.xml +++ b/app/src/main/res/xml/preferences_playback.xml @@ -34,13 +34,6 @@ android:key="prefResumeAfterCall" android:summary="@string/pref_resumeAfterCall_sum" android:title="@string/pref_resumeAfterCall_title"/> - <ListPreference - android:defaultValue="pip" - android:entries="@array/video_background_behavior_options" - android:entryValues="@array/video_background_behavior_values" - android:key="prefVideoBehavior" - android:summary="@string/pref_videoBehavior_sum" - android:title="@string/pref_videoBehavior_title"/> </PreferenceCategory> <PreferenceCategory android:title="@string/playback_control"> diff --git a/core/src/main/java/de/danoeh/antennapod/core/dialog/ConfirmationDialog.java b/core/src/main/java/de/danoeh/antennapod/core/dialog/ConfirmationDialog.java index c9fe886fb..47b5dcd09 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/dialog/ConfirmationDialog.java +++ b/core/src/main/java/de/danoeh/antennapod/core/dialog/ConfirmationDialog.java @@ -13,50 +13,43 @@ import de.danoeh.antennapod.core.R; */ public abstract class ConfirmationDialog { - private static final String TAG = ConfirmationDialog.class.getSimpleName(); + private static final String TAG = ConfirmationDialog.class.getSimpleName(); - private final Context context; - private final int titleId; - private final String message; + private final Context context; + private final int titleId; + private final String message; - private int positiveText; - private int negativeText; + private int positiveText; - public ConfirmationDialog(Context context, int titleId, int messageId) { - this(context, titleId, context.getString(messageId)); - } + public ConfirmationDialog(Context context, int titleId, int messageId) { + this(context, titleId, context.getString(messageId)); + } - public ConfirmationDialog(Context context, int titleId, String message) { - this.context = context; - this.titleId = titleId; - this.message = message; - } + public ConfirmationDialog(Context context, int titleId, String message) { + this.context = context; + this.titleId = titleId; + this.message = message; + } - private void onCancelButtonPressed(DialogInterface dialog) { - Log.d(TAG, "Dialog was cancelled"); - dialog.dismiss(); - } + private void onCancelButtonPressed(DialogInterface dialog) { + Log.d(TAG, "Dialog was cancelled"); + dialog.dismiss(); + } public void setPositiveText(int id) { this.positiveText = id; } - public void setNegativeText(int id) { - this.negativeText = id; + public abstract void onConfirmButtonPressed(DialogInterface dialog); + + public final AlertDialog createNewDialog() { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(titleId); + builder.setMessage(message); + builder.setPositiveButton(positiveText != 0 ? positiveText : R.string.confirm_label, + (dialog, which) -> onConfirmButtonPressed(dialog)); + builder.setNegativeButton(R.string.cancel_label, (dialog, which) -> onCancelButtonPressed(dialog)); + builder.setOnCancelListener(ConfirmationDialog.this::onCancelButtonPressed); + return builder.create(); } - - - public abstract void onConfirmButtonPressed(DialogInterface dialog); - - public final AlertDialog createNewDialog() { - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(titleId); - builder.setMessage(message); - builder.setPositiveButton(positiveText != 0 ? positiveText : R.string.confirm_label, - (dialog, which) -> onConfirmButtonPressed(dialog)); - builder.setNegativeButton(negativeText != 0 ? negativeText : R.string.cancel_label, - (dialog, which) -> onCancelButtonPressed(dialog)); - builder.setOnCancelListener(ConfirmationDialog.this::onCancelButtonPressed); - return builder.create(); - } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/AudioCoverFetcher.java b/core/src/main/java/de/danoeh/antennapod/core/glide/AudioCoverFetcher.java index b6b607904..9d270360a 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/glide/AudioCoverFetcher.java +++ b/core/src/main/java/de/danoeh/antennapod/core/glide/AudioCoverFetcher.java @@ -16,9 +16,6 @@ import java.io.InputStream; // see https://github.com/bumptech/glide/issues/699 class AudioCoverFetcher implements DataFetcher<InputStream> { - - private static final String TAG = "AudioCoverFetcher"; - private final String path; private final Context context; diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java index 74f89a039..82e1fb55a 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java +++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java @@ -23,7 +23,6 @@ import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.util.ArrayList; import java.util.Arrays; -import java.util.Calendar; import java.util.HashSet; import java.util.List; import java.util.Locale; @@ -88,7 +87,6 @@ public class UserPreferences { 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_RESUME_AFTER_CALL = "prefResumeAfterCall"; - public static final String PREF_VIDEO_BEHAVIOR = "prefVideoBehavior"; private static final String PREF_TIME_RESPECTS_SPEED = "prefPlaybackTimeRespectsSpeed"; public static final String PREF_STREAM_OVER_DOWNLOAD = "prefStreamOverDownload"; @@ -127,12 +125,9 @@ public class UserPreferences { private static final String PREF_FAST_FORWARD_SECS = "prefFastForwardSecs"; private static final String PREF_REWIND_SECS = "prefRewindSecs"; private static final String PREF_QUEUE_LOCKED = "prefQueueLocked"; - private static final String PREF_LEFT_VOLUME = "prefLeftVolume"; - private static final String PREF_RIGHT_VOLUME = "prefRightVolume"; // Experimental private static final String PREF_STEREO_TO_MONO = "PrefStereoToMono"; - public static final String PREF_CAST_ENABLED = "prefCast"; //Used for enabling Chromecast support public static final int EPISODE_CLEANUP_QUEUE = -1; public static final int EPISODE_CLEANUP_NULL = -2; public static final int EPISODE_CLEANUP_EXCEPT_FAVORITE = -3; @@ -829,10 +824,6 @@ public class UserPreferences { return getMediaPlayer().equals(PREF_MEDIA_PLAYER_EXOPLAYER); } - public static void enableSonic() { - prefs.edit().putString(PREF_MEDIA_PLAYER, "sonic").apply(); - } - public static void enableExoplayer() { prefs.edit().putString(PREF_MEDIA_PLAYER, PREF_MEDIA_PLAYER_EXOPLAYER).apply(); } @@ -847,15 +838,6 @@ public class UserPreferences { .apply(); } - public static VideoBackgroundBehavior getVideoBackgroundBehavior() { - switch (prefs.getString(PREF_VIDEO_BEHAVIOR, "pip")) { - case "stop": return VideoBackgroundBehavior.STOP; - case "continue": return VideoBackgroundBehavior.CONTINUE_PLAYING; - case "pip": //Deliberate fall-through - default: return VideoBackgroundBehavior.PICTURE_IN_PICTURE; - } - } - public static EpisodeCleanupAlgorithm getEpisodeCleanupAlgorithm() { if (!isEnableAutodownload()) { return new APNullCleanupAlgorithm(); @@ -967,17 +949,6 @@ public class UserPreferences { return getUpdateTimeOfDay().length == 2; } - /** - * Evaluates whether Cast support (Chromecast, Audio Cast, etc) is enabled on the preferences. - */ - public static boolean isCastEnabled() { - return prefs.getBoolean(PREF_CAST_ENABLED, false); - } - - public enum VideoBackgroundBehavior { - STOP, PICTURE_IN_PICTURE, CONTINUE_PLAYING - } - public enum BackButtonBehavior { DEFAULT, OPEN_DRAWER, DOUBLE_TAP, SHOW_PROMPT, GO_TO_PAGE } @@ -1071,28 +1042,7 @@ public class UserPreferences { .apply(); } - public static long getUsageCountingDateMillis() { - return prefs.getLong(PREF_USAGE_COUNTING_DATE, -1); - } - - private static void setUsageCountingDateMillis(long value) { - prefs.edit().putLong(PREF_USAGE_COUNTING_DATE, value).apply(); - } - - public static void resetUsageCountingDate() { - setUsageCountingDateMillis(Calendar.getInstance().getTimeInMillis()); - } - - public static void unsetUsageCountingDate() { - setUsageCountingDateMillis(-1); - } - public static boolean shouldShowSubscriptionTitle() { return prefs.getBoolean(PREF_SUBSCRIPTION_TITLE, false); } - - public static void setSubscriptionTitleSetting(boolean showTitle) { - prefs.edit().putBoolean(PREF_SUBSCRIPTION_TITLE, showTitle).apply(); - } - } diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedSyncTask.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedSyncTask.java index 9760c57b1..8f559a889 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedSyncTask.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedSyncTask.java @@ -9,7 +9,6 @@ import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.parser.feed.FeedHandlerResult; public class FeedSyncTask { - private static final String TAG = "FeedParserTask"; private final DownloadRequest request; private final Context context; private DownloadStatus downloadStatus; diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java index d54d10dc6..f1b9510d9 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java @@ -713,7 +713,6 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer { return stream; } - /** * Releases internally used resources. This method should only be called when the object is not used anymore. */ @@ -738,16 +737,6 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer { releaseWifiLockIfNecessary(); } - /** - * Releases internally used resources. This method should only be called when the object is not used anymore. - * This method is executed on an internal executor service. - */ - @Override - public void shutdownQuietly() { - executor.submit(this::shutdown); - executor.shutdown(); - } - @Override public void setVideoSurface(final SurfaceHolder surface) { executor.submit(() -> { diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java index d6d11bc8a..07e197752 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java @@ -82,7 +82,6 @@ import de.danoeh.antennapod.core.util.gui.NotificationUtils; import de.danoeh.antennapod.core.util.playback.PlayableUtils; import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter; import de.danoeh.antennapod.core.widget.WidgetUpdater; -import de.danoeh.antennapod.model.feed.Chapter; import de.danoeh.antennapod.model.feed.Feed; import de.danoeh.antennapod.model.feed.FeedItem; import de.danoeh.antennapod.model.feed.FeedMedia; @@ -1663,24 +1662,15 @@ public class PlaybackService extends MediaBrowserServiceCompat { mediaPlayer.setStartWhenPrepared(s); } - public void seekTo(final int t) { mediaPlayer.seekTo(t); } - private void seekDelta(final int d) { mediaPlayer.seekDelta(d); } /** - * Seek to the start of the specified chapter. - */ - public void seekToChapter(Chapter c) { - seekTo((int) c.getStart()); - } - - /** * call getDuration() on mediaplayer or return INVALID_TIME if player is in * an invalid state. */ @@ -1792,7 +1782,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { public void onPlayFromSearch(String query, Bundle extras) { Log.d(TAG, "onPlayFromSearch query=" + query + " extras=" + extras.toString()); - List<FeedItem> results = FeedSearcher.searchFeedItems(getBaseContext(), query, 0); + List<FeedItem> results = FeedSearcher.searchFeedItems(query, 0); if (results.size() > 0 && results.get(0).getMedia() != null) { FeedMedia media = results.get(0).getMedia(); startPlaying(media, false); diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceStateManager.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceStateManager.java index 83c065e0e..addc6b996 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceStateManager.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceStateManager.java @@ -36,10 +36,6 @@ class PlaybackServiceStateManager { isInForeground = false; } - boolean isInForeground() { - return isInForeground; - } - boolean hasReceivedValidStartCommand() { return hasReceivedValidStartCommand; } 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 da6987910..11ff813a9 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 @@ -771,26 +771,56 @@ public final class DBReader { } } + public static class MonthlyStatisticsItem { + public int year = 0; + public int month = 0; + public long timePlayed = 0; + } + + @NonNull + public static List<MonthlyStatisticsItem> getMonthlyTimeStatistics() { + List<MonthlyStatisticsItem> months = new ArrayList<>(); + PodDBAdapter adapter = PodDBAdapter.getInstance(); + adapter.open(); + try (Cursor cursor = adapter.getMonthlyStatisticsCursor()) { + int indexMonth = cursor.getColumnIndexOrThrow("month"); + int indexYear = cursor.getColumnIndexOrThrow("year"); + int indexTotalDuration = cursor.getColumnIndexOrThrow("total_duration"); + while (cursor.moveToNext()) { + MonthlyStatisticsItem item = new MonthlyStatisticsItem(); + item.month = Integer.parseInt(cursor.getString(indexMonth)); + item.year = Integer.parseInt(cursor.getString(indexYear)); + item.timePlayed = cursor.getLong(indexTotalDuration); + months.add(item); + } + } + adapter.close(); + return months; + } + + public static class StatisticsResult { + public List<StatisticsItem> feedTime = new ArrayList<>(); + public long oldestDate = System.currentTimeMillis(); + } + /** * Searches the DB for statistics. * * @return The list of statistics objects */ @NonNull - public static List<StatisticsItem> getStatistics() { + public static StatisticsResult getStatistics(boolean includeMarkedAsPlayed, + long timeFilterFrom, long timeFilterTo) { PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); - List<StatisticsItem> feedTime = new ArrayList<>(); - + StatisticsResult result = new StatisticsResult(); List<Feed> feeds = getFeedList(); for (Feed feed : feeds) { - long feedPlayedTimeCountAll = 0; long feedPlayedTime = 0; long feedTotalTime = 0; long episodes = 0; long episodesStarted = 0; - long episodesStartedIncludingMarked = 0; long totalDownloadSize = 0; long episodesDownloadCount = 0; List<FeedItem> items = getFeed(feed.getId()).getItems(); @@ -800,20 +830,22 @@ public final class DBReader { continue; } - feedPlayedTime += media.getPlayedDuration() / 1000; - - if (item.isPlayed()) { - feedPlayedTimeCountAll += media.getDuration() / 1000; - } else { - feedPlayedTimeCountAll += media.getPosition() / 1000; + if (media.getLastPlayedTime() > 0 && media.getPlayedDuration() != 0) { + result.oldestDate = Math.min(result.oldestDate, media.getLastPlayedTime()); } - - if (media.getPlaybackCompletionDate() != null || media.getPlayedDuration() > 0) { - episodesStarted++; + if (media.getLastPlayedTime() >= timeFilterFrom + && media.getLastPlayedTime() <= timeFilterTo) { + if (media.getPlayedDuration() != 0) { + feedPlayedTime += media.getPlayedDuration() / 1000; + } else if (includeMarkedAsPlayed && item.isPlayed()) { + feedPlayedTime += media.getDuration() / 1000; + } } - if (item.isPlayed() || media.getPosition() != 0) { - episodesStartedIncludingMarked++; + boolean markedAsStarted = item.isPlayed() || media.getPosition() != 0; + boolean hasStatistics = media.getPlaybackCompletionDate() != null || media.getPlayedDuration() > 0; + if (hasStatistics || (includeMarkedAsPlayed && markedAsStarted)) { + episodesStarted++; } feedTotalTime += media.getDuration() / 1000; @@ -825,13 +857,12 @@ public final class DBReader { episodes++; } - feedTime.add(new StatisticsItem( - feed, feedTotalTime, feedPlayedTime, feedPlayedTimeCountAll, episodes, - episodesStarted, episodesStartedIncludingMarked, totalDownloadSize, episodesDownloadCount)); + result.feedTime.add(new StatisticsItem(feed, feedTotalTime, feedPlayedTime, episodes, + episodesStarted, totalDownloadSize, episodesDownloadCount)); } adapter.close(); - return feedTime; + return result; } /** diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java index d1c041723..f447090a8 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java @@ -176,7 +176,7 @@ public final class DBTasks { media.setDownloaded(false); media.setFile_url(null); DBWriter.setFeedMedia(media); - EventBus.getDefault().post(FeedItemEvent.deletedMedia(media.getItem())); + EventBus.getDefault().post(FeedItemEvent.updated(media.getItem())); EventBus.getDefault().post(new MessageEvent(context.getString(R.string.error_file_not_found))); } @@ -230,36 +230,7 @@ public final class DBTasks { UserPreferences.getEpisodeCleanupAlgorithm().performCleanup(context); } - /** - * Returns the successor of a FeedItem in the queue. - * - * @param itemId ID of the FeedItem - * @param queue Used for determining the successor of the item. If this parameter is null, the method will load - * the queue from the database in the same thread. - * @return Successor of the FeedItem or null if the FeedItem is not in the queue or has no successor. - */ - public static FeedItem getQueueSuccessorOfItem(final long itemId, List<FeedItem> queue) { - FeedItem result = null; - if (queue == null) { - queue = DBReader.getQueue(); - } - if (queue != null) { - Iterator<FeedItem> iterator = queue.iterator(); - while (iterator.hasNext()) { - FeedItem item = iterator.next(); - if (item.getId() == itemId) { - if (iterator.hasNext()) { - result = iterator.next(); - } - break; - } - } - } - return result; - } - - private static Feed searchFeedByIdentifyingValueOrID(PodDBAdapter adapter, - Feed feed) { + private static Feed searchFeedByIdentifyingValueOrID(Feed feed) { if (feed.getId() != 0) { return DBReader.getFeed(feed.getId()); } else { @@ -322,7 +293,7 @@ public final class DBTasks { adapter.open(); // Look up feed in the feedslist - final Feed savedFeed = searchFeedByIdentifyingValueOrID(adapter, newFeed); + final Feed savedFeed = searchFeedByIdentifyingValueOrID(newFeed); if (savedFeed == null) { Log.d(TAG, "Found no existing Feed with title " + newFeed.getTitle() + ". Adding as new one."); @@ -456,7 +427,7 @@ public final class DBTasks { if (savedFeed == null) { DBWriter.addNewFeed(context, newFeed).get(); // Update with default values that are set in database - resultFeed = searchFeedByIdentifyingValueOrID(adapter, newFeed); + resultFeed = searchFeedByIdentifyingValueOrID(newFeed); } else { DBWriter.setCompleteFeed(savedFeed).get(); } @@ -487,15 +458,13 @@ public final class DBTasks { /** * Searches the FeedItems of a specific Feed for a given string. * - * @param context Used for accessing the DB. * @param feedID The id of the feed whose items should be searched. * @param query The search string. * @return A FutureTask object that executes the search request * and returns the search result as a List of FeedItems. */ - public static FutureTask<List<FeedItem>> searchFeedItems(final Context context, - final long feedID, final String query) { - return new FutureTask<>(new QueryTask<List<FeedItem>>(context) { + public static FutureTask<List<FeedItem>> searchFeedItems(final long feedID, final String query) { + return new FutureTask<>(new QueryTask<List<FeedItem>>() { @Override public void execute(PodDBAdapter adapter) { Cursor searchResult = adapter.searchItems(feedID, query); @@ -507,8 +476,8 @@ public final class DBTasks { }); } - public static FutureTask<List<Feed>> searchFeeds(final Context context, final String query) { - return new FutureTask<>(new QueryTask<List<Feed>>(context) { + public static FutureTask<List<Feed>> searchFeeds(final String query) { + return new FutureTask<>(new QueryTask<List<Feed>>() { @Override public void execute(PodDBAdapter adapter) { Cursor cursor = adapter.searchFeeds(query); @@ -533,7 +502,7 @@ public final class DBTasks { abstract static class QueryTask<T> implements Callable<T> { private T result; - public QueryTask(Context context) { + public QueryTask() { } @Override diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java index af57cbdae..e572b4d8c 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java @@ -13,7 +13,6 @@ import org.greenrobot.eventbus.EventBus; import java.io.File; import java.util.ArrayList; -import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Locale; @@ -142,7 +141,7 @@ public class DBWriter { .build(); SynchronizationQueueSink.enqueueEpisodeActionIfSynchronizationIsActive(context, action); } - EventBus.getDefault().post(FeedItemEvent.deletedMedia(Collections.singletonList(media.getItem()))); + EventBus.getDefault().post(FeedItemEvent.updated(media.getItem())); return true; } @@ -550,7 +549,7 @@ public class DBWriter { adapter.addFavoriteItem(item); adapter.close(); item.addTag(FeedItem.TAG_FAVORITE); - EventBus.getDefault().post(FavoritesEvent.added(item)); + EventBus.getDefault().post(new FavoritesEvent()); EventBus.getDefault().post(FeedItemEvent.updated(item)); }); } @@ -561,7 +560,7 @@ public class DBWriter { adapter.removeFavoriteItem(item); adapter.close(); item.removeTag(FeedItem.TAG_FAVORITE); - EventBus.getDefault().post(FavoritesEvent.removed(item)); + EventBus.getDefault().post(new FavoritesEvent()); EventBus.getDefault().post(FeedItemEvent.updated(item)); }); } diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java b/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java index c3dd52b49..68ce7b7ef 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java @@ -1,6 +1,5 @@ package de.danoeh.antennapod.core.storage; -import android.content.Context; import androidx.annotation.NonNull; import de.danoeh.antennapod.model.feed.Feed; import de.danoeh.antennapod.model.feed.FeedItem; @@ -19,9 +18,9 @@ public class FeedSearcher { } @NonNull - public static List<FeedItem> searchFeedItems(final Context context, final String query, final long selectedFeed) { + public static List<FeedItem> searchFeedItems(final String query, final long selectedFeed) { try { - FutureTask<List<FeedItem>> itemSearchTask = DBTasks.searchFeedItems(context, selectedFeed, query); + FutureTask<List<FeedItem>> itemSearchTask = DBTasks.searchFeedItems(selectedFeed, query); itemSearchTask.run(); return itemSearchTask.get(); } catch (ExecutionException | InterruptedException e) { @@ -31,9 +30,9 @@ public class FeedSearcher { } @NonNull - public static List<Feed> searchFeeds(final Context context, final String query) { + public static List<Feed> searchFeeds(final String query) { try { - FutureTask<List<Feed>> feedSearchTask = DBTasks.searchFeeds(context, query); + FutureTask<List<Feed>> feedSearchTask = DBTasks.searchFeeds(query); feedSearchTask.run(); return feedSearchTask.get(); } catch (ExecutionException | InterruptedException e) { diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java index 43d9c7f11..ea4617f16 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java @@ -1133,6 +1133,17 @@ public class PodDBAdapter { return db.rawQuery(query, null); } + public final Cursor getMonthlyStatisticsCursor() { + final String query = "SELECT SUM(" + KEY_PLAYED_DURATION + ") AS total_duration" + + ", strftime('%m', datetime(" + KEY_LAST_PLAYED_TIME + "/1000, 'unixepoch')) AS month" + + ", strftime('%Y', datetime(" + KEY_LAST_PLAYED_TIME + "/1000, 'unixepoch')) AS year" + + " FROM " + TABLE_NAME_FEED_MEDIA + + " WHERE " + KEY_LAST_PLAYED_TIME + " > 0 AND " + KEY_PLAYED_DURATION + " > 0" + + " GROUP BY year, month" + + " ORDER BY year, month"; + return db.rawQuery(query, null); + } + public int getQueueSize() { final String query = String.format("SELECT COUNT(%s) FROM %s", KEY_ID, TABLE_NAME_QUEUE); Cursor c = db.rawQuery(query, null); diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/StatisticsItem.java b/core/src/main/java/de/danoeh/antennapod/core/storage/StatisticsItem.java index 90978d6b8..1bc4997dd 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/StatisticsItem.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/StatisticsItem.java @@ -12,11 +12,6 @@ public class StatisticsItem { public final long timePlayed; /** - * Simply sums up time of podcasts that are marked as played. - */ - public final long timePlayedCountAll; - - /** * Number of episodes. */ public final long episodes; @@ -27,11 +22,6 @@ public class StatisticsItem { public final long episodesStarted; /** - * All episodes that are marked as played (or have position != 0). - */ - public final long episodesStartedIncludingMarked; - - /** * Simply sums up the size of download podcasts. */ public final long totalDownloadSize; @@ -41,16 +31,14 @@ public class StatisticsItem { */ public final long episodesDownloadCount; - public StatisticsItem(Feed feed, long time, long timePlayed, long timePlayedCountAll, - long episodes, long episodesStarted, long episodesStartedIncludingMarked, + public StatisticsItem(Feed feed, long time, long timePlayed, + long episodes, long episodesStarted, long totalDownloadSize, long episodesDownloadCount) { this.feed = feed; this.time = time; this.timePlayed = timePlayed; - this.timePlayedCountAll = timePlayedCountAll; this.episodes = episodes; this.episodesStarted = episodesStarted; - this.episodesStartedIncludingMarked = episodesStartedIncludingMarked; this.totalDownloadSize = totalDownloadSize; this.episodesDownloadCount = episodesDownloadCount; } diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/DateFormatter.java b/core/src/main/java/de/danoeh/antennapod/core/util/DateFormatter.java index 99628dfcc..dc7ed4508 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/DateFormatter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/DateFormatter.java @@ -37,7 +37,7 @@ public class DateFormatter { return android.text.format.DateUtils.formatDateTime(context, date.getTime(), format); } - public static String formatForAccessibility(final Context context, final Date date) { + public static String formatForAccessibility(final Date date) { if (date == null) { return ""; } diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java index 0bf301366..85be3c787 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java @@ -5,7 +5,6 @@ import android.net.ConnectivityManager; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkInfo; -import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Build; import android.text.TextUtils; @@ -146,18 +145,6 @@ public class NetworkUtils { return selectedNetworks.contains(Integer.toString(wm.getConnectionInfo().getNetworkId())); } - /** - * Returns the SSID of the wifi connection, or <code>null</code> if there is no wifi. - */ - public static String getWifiSsid() { - WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE); - WifiInfo wifiInfo = wifiManager.getConnectionInfo(); - if (wifiInfo != null) { - return wifiInfo.getSSID(); - } - return null; - } - public static boolean wasDownloadBlocked(Throwable throwable) { String message = throwable.getMessage(); if (message != null) { diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/PowerUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/PowerUtils.java index 39deea36a..a1fadb4dc 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/PowerUtils.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/PowerUtils.java @@ -10,8 +10,6 @@ import android.os.BatteryManager; */ public class PowerUtils { - private static final String TAG = "PowerUtils"; - private PowerUtils() { } diff --git a/core/src/main/res/values/arrays.xml b/core/src/main/res/values/arrays.xml index de83bbf59..f3cf4361d 100644 --- a/core/src/main/res/values/arrays.xml +++ b/core/src/main/res/values/arrays.xml @@ -247,28 +247,6 @@ <item>@string/skip_episode_label</item> </string-array> - <string-array name="video_background_behavior_options"> - <item>@string/stop_playback</item> - <item>@string/player_go_to_picture_in_picture</item> - <item>@string/continue_playback</item> - </string-array> - - <string-array name="video_background_behavior_values"> - <item>stop</item> - <item>pip</item> - <item>continue</item> - </string-array> - - <string-array name="video_background_behavior_options_without_pip"> - <item>@string/stop_playback</item> - <item>@string/continue_playback</item> - </string-array> - - <string-array name="video_background_behavior_values_without_pip"> - <item>stop</item> - <item>continue</item> - </string-array> - <string-array name="back_button_behavior_options"> <item>@string/back_button_default</item> <item>@string/back_button_go_to_page</item> diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index 1196c0eee..197d0c636 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -28,8 +28,7 @@ <string name="gpodnet_main_label">gpodder.net</string> <string name="episode_cache_full_title">Episode cache full</string> <string name="episode_cache_full_message">The episode cache limit has been reached. You can increase the cache size in the Settings.</string> - <string name="playback_statistics_label">Playback</string> - <string name="download_statistics_label">Downloads</string> + <string name="years_statistics_label">Years</string> <string name="notification_pref_fragment">Notifications</string> <!-- Google Assistant --> @@ -44,17 +43,20 @@ <string name="change_setting">Change</string> <!-- Statistics fragment --> - <string name="total_time_listened_to_podcasts">Total time of episodes played:</string> - <string name="statistics_mode">Statistics mode</string> - <string name="statistics_mode_normal">Calculate duration that was actually played. Playing twice is counted twice, while marking as played is not counted</string> - <string name="statistics_mode_count_all">Sum up all episodes marked as played</string> + <string name="statistics_include_marked">Include duration of episodes that are just marked as played</string> <string name="statistics_speed_not_counted">Notice: Playback speed is never taken into account.</string> + <string name="statistics_from">From</string> + <string name="statistics_to">To</string> + <string name="statistics_today">Today</string> + <string name="statistics_filter_all_time">All time</string> + <string name="statistics_filter_last_year">Last year</string> <string name="statistics_reset_data">Reset statistics data</string> <string name="statistics_reset_data_msg">This will erase the history of duration played for all episodes. Are you sure you want to proceed?</string> - <string name="statistics_counting_since">Since %s,\nyou played</string> + <string name="statistics_counting_range">Played between %1$s and %2$s</string> + <string name="statistics_counting_total">Played in total</string> <!-- Download Statistics fragment --> - <string name="total_size_downloaded_podcasts">Total size of episodes on the device:</string> + <string name="total_size_downloaded_podcasts">Total size of episodes on the device</string> <!-- Main activity --> <string name="drawer_open">Open menu</string> @@ -302,7 +304,6 @@ <string name="playback_error_unknown">Unknown Error</string> <string name="no_media_playing_label">No media playing</string> <string name="position_default_label" translatable="false">00:00:00</string> - <string name="player_go_to_picture_in_picture">Picture-in-picture mode</string> <string name="unknown_media_key">AntennaPod - Unknown media key: %1$d</string> <string name="error_file_not_found">File not found</string> <string name="no_media_label">Item does not contain a media file</string> @@ -511,10 +512,6 @@ <string name="media_player_switch_to_exoplayer">Switch to ExoPlayer</string> <string name="media_player_switched_to_exoplayer">Switched to ExoPlayer.</string> <string name="pref_skip_silence_title">Skip Silence in Audio</string> - <string name="pref_videoBehavior_title">Upon exiting video</string> - <string name="pref_videoBehavior_sum">Behavior when leaving video playback</string> - <string name="stop_playback">Stop playback</string> - <string name="continue_playback">Continue audio playback</string> <string name="behavior">Behavior</string> <string name="pref_back_button_behavior_title">Back Button Behavior</string> <string name="pref_back_button_behavior_sum">Change behavior of the back button.</string> @@ -707,7 +704,6 @@ <string name="auto_download_disabled_globally">Auto download is disabled in the main AntennaPod settings</string> <string name="statistics_time_played">Time played:</string> <string name="statistics_total_duration">Total duration (estimate):</string> - <string name="statistics_duration_played_episodes">Duration of played episodes:</string> <string name="statistics_episodes_on_device">Episodes on the device:</string> <string name="statistics_space_used">Space used:</string> <string name="statistics_episodes_started_total">Episodes started/total:</string> @@ -793,6 +789,7 @@ <string name="stereo_to_mono">Downmix: Stereo to mono</string> <string name="sonic_only">Sonic only</string> <string name="exoplayer_only">ExoPlayer only</string> + <string name="player_switch_to_audio_only">Switch to audio only</string> <!-- proxy settings --> <string name="proxy_type_label">Type</string> diff --git a/core/src/play/java/de/danoeh/antennapod/core/service/playback/WearMediaSession.java b/core/src/play/java/de/danoeh/antennapod/core/service/playback/WearMediaSession.java index 2167d9f2c..8df05d10d 100644 --- a/core/src/play/java/de/danoeh/antennapod/core/service/playback/WearMediaSession.java +++ b/core/src/play/java/de/danoeh/antennapod/core/service/playback/WearMediaSession.java @@ -6,8 +6,6 @@ import android.support.v4.media.session.PlaybackStateCompat; import android.support.wearable.media.MediaControlConstants; public class WearMediaSession { - public static final String TAG = "WearMediaSession"; - static void sessionStateAddActionForWear(PlaybackStateCompat.Builder sessionState, String actionName, CharSequence name, int icon) { PlaybackStateCompat.CustomAction.Builder actionBuilder = diff --git a/event/src/main/java/de/danoeh/antennapod/event/FavoritesEvent.java b/event/src/main/java/de/danoeh/antennapod/event/FavoritesEvent.java index 8b27f74ab..0a3c65adc 100644 --- a/event/src/main/java/de/danoeh/antennapod/event/FavoritesEvent.java +++ b/event/src/main/java/de/danoeh/antennapod/event/FavoritesEvent.java @@ -1,26 +1,7 @@ package de.danoeh.antennapod.event; -import de.danoeh.antennapod.model.feed.FeedItem; - public class FavoritesEvent { - public enum Action { - ADDED, REMOVED - } - - private final Action action; - private final FeedItem item; - - private FavoritesEvent(Action action, FeedItem item) { - this.action = action; - this.item = item; - } - - public static FavoritesEvent added(FeedItem item) { - return new FavoritesEvent(Action.ADDED, item); - } - - public static FavoritesEvent removed(FeedItem item) { - return new FavoritesEvent(Action.REMOVED, item); + public FavoritesEvent() { } } diff --git a/event/src/main/java/de/danoeh/antennapod/event/FeedItemEvent.java b/event/src/main/java/de/danoeh/antennapod/event/FeedItemEvent.java index 6c7adc2d7..125d113d6 100644 --- a/event/src/main/java/de/danoeh/antennapod/event/FeedItemEvent.java +++ b/event/src/main/java/de/danoeh/antennapod/event/FeedItemEvent.java @@ -9,33 +9,17 @@ import java.util.List; import de.danoeh.antennapod.model.feed.FeedItem; public class FeedItemEvent { - - public enum Action { - UPDATE, DELETE_MEDIA - } - - @NonNull - private final Action action; @NonNull public final List<FeedItem> items; - private FeedItemEvent(@NonNull Action action, @NonNull List<FeedItem> items) { - this.action = action; + public FeedItemEvent(@NonNull List<FeedItem> items) { this.items = items; } - public static FeedItemEvent deletedMedia(List<FeedItem> items) { - return new FeedItemEvent(Action.DELETE_MEDIA, items); - } - - public static FeedItemEvent deletedMedia(FeedItem... items) { - return deletedMedia(Arrays.asList(items)); - } - public static FeedItemEvent updated(List<FeedItem> items) { - return new FeedItemEvent(Action.UPDATE, items); + return new FeedItemEvent(items); } public static FeedItemEvent updated(FeedItem... items) { - return updated(Arrays.asList(items)); + return new FeedItemEvent(Arrays.asList(items)); } } diff --git a/model/src/main/java/de/danoeh/antennapod/model/feed/FeedMedia.java b/model/src/main/java/de/danoeh/antennapod/model/feed/FeedMedia.java index d2e4e4556..2fb1a5c0c 100644 --- a/model/src/main/java/de/danoeh/antennapod/model/feed/FeedMedia.java +++ b/model/src/main/java/de/danoeh/antennapod/model/feed/FeedMedia.java @@ -226,10 +226,6 @@ public class FeedMedia extends FeedFile implements Playable { return mime_type; } - public void setMime_type(String mime_type) { - this.mime_type = mime_type; - } - @Nullable public FeedItem getItem() { return item; diff --git a/net/sync/gpoddernet/src/main/java/de/danoeh/antennapod/net/sync/gpoddernet/GpodnetService.java b/net/sync/gpoddernet/src/main/java/de/danoeh/antennapod/net/sync/gpoddernet/GpodnetService.java index 21a362a40..7c0d7cb51 100644 --- a/net/sync/gpoddernet/src/main/java/de/danoeh/antennapod/net/sync/gpoddernet/GpodnetService.java +++ b/net/sync/gpoddernet/src/main/java/de/danoeh/antennapod/net/sync/gpoddernet/GpodnetService.java @@ -19,7 +19,6 @@ import java.net.URISyntaxException; import java.net.URL; import java.nio.charset.Charset; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Locale; @@ -213,45 +212,6 @@ public class GpodnetService implements ISyncService { } /** - * Returns synchronization status of devices. - * <p/> - * This method requires authentication. - * - * @throws GpodnetServiceAuthenticationException If there is an authentication error. - */ - public List<List<String>> getSynchronizedDevices() throws GpodnetServiceException { - requireLoggedIn(); - try { - URL url = new URI(baseScheme, null, baseHost, basePort, - String.format("/api/2/sync-devices/%s.json", username), null, null).toURL(); - Request.Builder request = new Request.Builder().url(url); - String response = executeRequest(request); - JSONObject syncStatus = new JSONObject(response); - List<List<String>> result = new ArrayList<>(); - - JSONArray synchronizedDevices = syncStatus.getJSONArray("synchronized"); - for (int i = 0; i < synchronizedDevices.length(); i++) { - JSONArray groupDevices = synchronizedDevices.getJSONArray(i); - List<String> group = new ArrayList<>(); - for (int j = 0; j < groupDevices.length(); j++) { - group.add(groupDevices.getString(j)); - } - result.add(group); - } - - JSONArray notSynchronizedDevices = syncStatus.getJSONArray("not-synchronized"); - for (int i = 0; i < notSynchronizedDevices.length(); i++) { - result.add(Collections.singletonList(notSynchronizedDevices.getString(i))); - } - - return result; - } catch (JSONException | MalformedURLException | URISyntaxException e) { - e.printStackTrace(); - throw new GpodnetServiceException(e); - } - } - - /** * Configures the device of a given user. * <p/> * This method requires authentication. @@ -288,39 +248,6 @@ public class GpodnetService implements ISyncService { } /** - * Links devices for synchronization. - * <p/> - * This method requires authentication. - * - * @throws GpodnetServiceAuthenticationException If there is an authentication error. - */ - public void linkDevices(@NonNull List<String> deviceIds) throws GpodnetServiceException { - requireLoggedIn(); - try { - final URL url = new URI(baseScheme, null, baseHost, basePort, - String.format("/api/2/sync-devices/%s.json", username), null, null).toURL(); - JSONObject jsonContent = new JSONObject(); - JSONArray group = new JSONArray(); - for (String deviceId : deviceIds) { - group.put(deviceId); - } - - JSONArray synchronizedGroups = new JSONArray(); - synchronizedGroups.put(group); - jsonContent.put("synchronize", synchronizedGroups); - jsonContent.put("stop-synchronize", new JSONArray()); - - Log.d("aaaa", jsonContent.toString()); - RequestBody body = RequestBody.create(JSON, jsonContent.toString()); - Request.Builder request = new Request.Builder().post(body).url(url); - executeRequest(request); - } catch (JSONException | MalformedURLException | URISyntaxException e) { - e.printStackTrace(); - throw new GpodnetServiceException(e); - } - } - - /** * Uploads the subscriptions of a specific device. * <p/> * This method requires authentication. diff --git a/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/DublinCore.java b/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/DublinCore.java index 003f72e9b..7e2f68a17 100644 --- a/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/DublinCore.java +++ b/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/DublinCore.java @@ -8,7 +8,6 @@ import org.xml.sax.Attributes; import de.danoeh.antennapod.model.feed.FeedItem; public class DublinCore extends Namespace { - private static final String TAG = "NSDublinCore"; public static final String NSTAG = "dc"; public static final String NSURI = "http://purl.org/dc/elements/1.1/"; diff --git a/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Namespace.java b/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Namespace.java index 5273c6731..f65d124eb 100644 --- a/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Namespace.java +++ b/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Namespace.java @@ -5,10 +5,7 @@ import de.danoeh.antennapod.parser.feed.element.SyndElement; import org.xml.sax.Attributes; public abstract class Namespace { - public static final String NSTAG = null; - public static final String NSURI = null; - - /** Called by a Feedhandler when in startElement and it detects a namespace element + /** Called by a Feedhandler when in startElement and it detects a namespace element * @return The SyndElement to push onto the stack * */ public abstract SyndElement handleElementStart(String localName, HandlerState state, Attributes attributes); diff --git a/parser/media/src/main/java/de/danoeh/antennapod/parser/media/id3/ID3Chapter.java b/parser/media/src/main/java/de/danoeh/antennapod/parser/media/id3/ID3Chapter.java index fc594ab5a..5396025e9 100644 --- a/parser/media/src/main/java/de/danoeh/antennapod/parser/media/id3/ID3Chapter.java +++ b/parser/media/src/main/java/de/danoeh/antennapod/parser/media/id3/ID3Chapter.java @@ -30,9 +30,4 @@ public class ID3Chapter extends Chapter { public int getChapterType() { return CHAPTERTYPE_ID3CHAPTER; } - - public String getId3ID() { - return id3ID; - } - } diff --git a/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentReaderException.java b/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentReaderException.java index 8de1b29c0..f1a46bda0 100644 --- a/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentReaderException.java +++ b/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentReaderException.java @@ -3,14 +3,6 @@ package de.danoeh.antennapod.parser.media.vorbis; public class VorbisCommentReaderException extends Exception { private static final long serialVersionUID = 1L; - public VorbisCommentReaderException() { - super(); - } - - public VorbisCommentReaderException(String message, Throwable cause) { - super(message, cause); - } - public VorbisCommentReaderException(String message) { super(message); } diff --git a/playback/base/src/main/java/de/danoeh/antennapod/playback/base/PlaybackServiceMediaPlayer.java b/playback/base/src/main/java/de/danoeh/antennapod/playback/base/PlaybackServiceMediaPlayer.java index d03695896..a2ccb3f88 100644 --- a/playback/base/src/main/java/de/danoeh/antennapod/playback/base/PlaybackServiceMediaPlayer.java +++ b/playback/base/src/main/java/de/danoeh/antennapod/playback/base/PlaybackServiceMediaPlayer.java @@ -179,12 +179,6 @@ public abstract class PlaybackServiceMediaPlayer { */ public abstract void shutdown(); - /** - * Releases internally used resources. This method should only be called when the object is not used anymore. - * This method is executed on an internal executor service. - */ - public abstract void shutdownQuietly(); - public abstract void setVideoSurface(SurfaceHolder surface); public abstract void resetVideoSurface(); diff --git a/playback/base/src/main/java/de/danoeh/antennapod/playback/base/PlayerStatus.java b/playback/base/src/main/java/de/danoeh/antennapod/playback/base/PlayerStatus.java index d995ae21f..154fc0c83 100644 --- a/playback/base/src/main/java/de/danoeh/antennapod/playback/base/PlayerStatus.java +++ b/playback/base/src/main/java/de/danoeh/antennapod/playback/base/PlayerStatus.java @@ -23,10 +23,6 @@ public enum PlayerStatus { statusValue = val; } - public static PlayerStatus fromOrdinal(int o) { - return fromOrdinalLookup[o]; - } - public boolean isAtLeast(PlayerStatus other) { return other == null || this.statusValue >= other.statusValue; } diff --git a/playback/cast/src/play/java/de/danoeh/antennapod/playback/cast/CastEnabledActivity.java b/playback/cast/src/play/java/de/danoeh/antennapod/playback/cast/CastEnabledActivity.java index 2cebde6a3..83abd98b7 100644 --- a/playback/cast/src/play/java/de/danoeh/antennapod/playback/cast/CastEnabledActivity.java +++ b/playback/cast/src/play/java/de/danoeh/antennapod/playback/cast/CastEnabledActivity.java @@ -13,7 +13,6 @@ import com.google.android.gms.common.GoogleApiAvailability; * network. */ public abstract class CastEnabledActivity extends AppCompatActivity { - private static final String TAG = "CastEnabledActivity"; private boolean canCast = false; @Override diff --git a/playback/cast/src/play/java/de/danoeh/antennapod/playback/cast/CastPsmp.java b/playback/cast/src/play/java/de/danoeh/antennapod/playback/cast/CastPsmp.java index 8e74154e8..d3c4f3468 100644 --- a/playback/cast/src/play/java/de/danoeh/antennapod/playback/cast/CastPsmp.java +++ b/playback/cast/src/play/java/de/danoeh/antennapod/playback/cast/CastPsmp.java @@ -456,11 +456,6 @@ public class CastPsmp extends PlaybackServiceMediaPlayer { } @Override - public void shutdownQuietly() { - shutdown(); - } - - @Override public void setVideoSurface(SurfaceHolder surface) { throw new UnsupportedOperationException("Setting Video Surface unsupported in Remote Media Player"); } |