Refresh timeline when resetting statistics

This commit is contained in:
ByteHamster 2022-03-05 10:43:31 +01:00
parent 42e36328e3
commit 32770fe817
7 changed files with 225 additions and 162 deletions

View File

@ -0,0 +1,7 @@
package de.danoeh.antennapod.event;
public class StatisticsEvent {
public StatisticsEvent() {
}
}

View File

@ -29,6 +29,9 @@ public abstract class PagedToolbarFragment extends Fragment {
this.viewPager = viewPager;
toolbar.setOnMenuItemClickListener(item -> {
if (this.onOptionsItemSelected(item)) {
return true;
}
Fragment child = getChildFragmentManager().findFragmentByTag("f" + viewPager.getCurrentItem());
if (child != null) {
return child.onOptionsItemSelected(item);

View File

@ -13,6 +13,7 @@ android {
dependencies {
implementation project(":core")
implementation project(':event')
implementation project(":model")
implementation project(":ui:common")
@ -24,6 +25,7 @@ dependencies {
implementation "androidx.viewpager2:viewpager2:$viewPager2Version"
implementation "com.google.android.material:material:$googleMaterialVersion"
implementation "org.greenrobot:eventbus:$eventbusVersion"
implementation "com.github.bumptech.glide:glide:$glideVersion"
annotationProcessor "com.github.bumptech.glide:compiler:$glideVersion"
implementation "io.reactivex.rxjava2:rxandroid:$rxAndroidVersion"

View File

@ -1,9 +1,13 @@
package de.danoeh.antennapod.ui.statistics;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.Toolbar;
@ -13,17 +17,29 @@ import androidx.viewpager2.widget.ViewPager2;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.event.StatisticsEvent;
import de.danoeh.antennapod.ui.common.PagedToolbarFragment;
import de.danoeh.antennapod.ui.statistics.downloads.DownloadStatisticsFragment;
import de.danoeh.antennapod.ui.statistics.subscriptions.SubscriptionStatisticsFragment;
import de.danoeh.antennapod.ui.statistics.years.YearsStatisticsFragment;
import io.reactivex.Completable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import org.greenrobot.eventbus.EventBus;
/**
* Displays the 'statistics' screen
*/
public class StatisticsFragment extends PagedToolbarFragment {
public static final String TAG = "StatisticsFragment";
public static final String PREF_NAME = "StatisticsActivityPrefs";
public static final String PREF_INCLUDE_MARKED_PLAYED = "countAll";
public static final String PREF_FILTER_FROM = "filterFrom";
public static final String PREF_FILTER_TO = "filterTo";
private static final int POS_SUBSCRIPTIONS = 0;
private static final int POS_YEARS = 1;
@ -68,6 +84,44 @@ public class StatisticsFragment extends PagedToolbarFragment {
return rootView;
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == R.id.statistics_reset) {
confirmResetStatistics();
return true;
}
return super.onOptionsItemSelected(item);
}
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() {
getContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE).edit()
.putBoolean(PREF_INCLUDE_MARKED_PLAYED, false)
.putLong(PREF_FILTER_FROM, 0)
.putLong(PREF_FILTER_TO, Long.MAX_VALUE)
.apply();
Disposable disposable = Completable.fromFuture(DBWriter.resetStatistics())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(() -> EventBus.getDefault().post(new StatisticsEvent()),
error -> Log.e(TAG, Log.getStackTraceString(error)));
}
public static class StatisticsPagerAdapter extends FragmentStateAdapter {
StatisticsPagerAdapter(@NonNull Fragment fragment) {

View File

@ -0,0 +1,127 @@
package de.danoeh.antennapod.ui.statistics.subscriptions;
import android.content.Context;
import android.content.SharedPreferences;
import android.view.LayoutInflater;
import android.widget.ArrayAdapter;
import androidx.appcompat.app.AlertDialog;
import androidx.core.util.Pair;
import de.danoeh.antennapod.event.StatisticsEvent;
import de.danoeh.antennapod.ui.statistics.R;
import de.danoeh.antennapod.ui.statistics.StatisticsFragment;
import de.danoeh.antennapod.ui.statistics.databinding.StatisticsFilterDialogBinding;
import org.greenrobot.eventbus.EventBus;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
public class StatisticsFilterDialog {
private final Context context;
private final long oldestDate;
private final SharedPreferences prefs;
private boolean includeMarkedAsPlayed;
private long timeFilterFrom;
private long timeFilterTo;
public StatisticsFilterDialog(Context context, long oldestDate) {
this.context = context;
this.oldestDate = oldestDate;
prefs = context.getSharedPreferences(StatisticsFragment.PREF_NAME, Context.MODE_PRIVATE);
includeMarkedAsPlayed = prefs.getBoolean(StatisticsFragment.PREF_INCLUDE_MARKED_PLAYED, false);
timeFilterFrom = prefs.getLong(StatisticsFragment.PREF_FILTER_FROM, 0);
timeFilterTo = prefs.getLong(StatisticsFragment.PREF_FILTER_TO, Long.MAX_VALUE);
}
public void show() {
StatisticsFilterDialogBinding dialogBinding = StatisticsFilterDialogBinding.inflate(
LayoutInflater.from(context));
AlertDialog.Builder builder = new AlertDialog.Builder(context);
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(oldestDate);
ArrayAdapter<String> adapterFrom = new ArrayAdapter<>(context,
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<>(context,
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(StatisticsFragment.PREF_INCLUDE_MARKED_PLAYED, includeMarkedAsPlayed)
.putLong(StatisticsFragment.PREF_FILTER_FROM, timeFilterFrom)
.putLong(StatisticsFragment.PREF_FILTER_TO, timeFilterTo)
.apply();
EventBus.getDefault().post(new StatisticsEvent());
});
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(context.getString(R.string.statistics_today));
timestamps.add(Long.MAX_VALUE);
return new Pair<>(names.toArray(new String[0]), timestamps.toArray(new Long[0]));
}
}

View File

@ -1,7 +1,6 @@
package de.danoeh.antennapod.ui.statistics.subscriptions;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
@ -10,62 +9,41 @@ 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.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.event.StatisticsEvent;
import de.danoeh.antennapod.ui.statistics.R;
import de.danoeh.antennapod.ui.statistics.databinding.StatisticsFilterDialogBinding;
import io.reactivex.Completable;
import de.danoeh.antennapod.ui.statistics.StatisticsFragment;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
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);
}
@ -79,6 +57,7 @@ public class SubscriptionStatisticsFragment extends Fragment {
listAdapter = new PlaybackStatisticsListAdapter(this);
feedStatisticsList.setLayoutManager(new LinearLayoutManager(getContext()));
feedStatisticsList.setAdapter(listAdapter);
EventBus.getDefault().register(this);
return root;
}
@ -91,11 +70,17 @@ public class SubscriptionStatisticsFragment extends Fragment {
@Override
public void onDestroyView() {
super.onDestroyView();
EventBus.getDefault().unregister(this);
if (disposable != null) {
disposable.dispose();
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void statisticsEvent(StatisticsEvent event) {
refreshStatistics();
}
@Override
public void onPrepareOptionsMenu(@NonNull Menu menu) {
super.onPrepareOptionsMenu(menu);
@ -106,144 +91,14 @@ public class SubscriptionStatisticsFragment extends Fragment {
@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();
if (statisticsResult != null) {
new StatisticsFilterDialog(getContext(), statisticsResult.oldestDate).show();
}
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);
@ -254,6 +109,10 @@ public class SubscriptionStatisticsFragment extends Fragment {
if (disposable != null) {
disposable.dispose();
}
SharedPreferences prefs = getContext().getSharedPreferences(StatisticsFragment.PREF_NAME, Context.MODE_PRIVATE);
boolean includeMarkedAsPlayed = prefs.getBoolean(StatisticsFragment.PREF_INCLUDE_MARKED_PLAYED, false);
long timeFilterFrom = prefs.getLong(StatisticsFragment.PREF_FILTER_FROM, 0);
long timeFilterTo = prefs.getLong(StatisticsFragment.PREF_FILTER_TO, Long.MAX_VALUE);
disposable = Observable.fromCallable(
() -> {
DBReader.StatisticsResult statisticsData = DBReader.getStatistics(

View File

@ -13,11 +13,15 @@ import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.event.StatisticsEvent;
import de.danoeh.antennapod.ui.statistics.R;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
/**
* Displays the yearly statistics screen
@ -40,6 +44,7 @@ public class YearsStatisticsFragment extends Fragment {
listAdapter = new YearStatisticsListAdapter(getContext());
yearStatisticsList.setLayoutManager(new LinearLayoutManager(getContext()));
yearStatisticsList.setAdapter(listAdapter);
EventBus.getDefault().register(this);
return root;
}
@ -52,15 +57,21 @@ public class YearsStatisticsFragment extends Fragment {
@Override
public void onDestroyView() {
super.onDestroyView();
EventBus.getDefault().unregister(this);
if (disposable != null) {
disposable.dispose();
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void statisticsEvent(StatisticsEvent event) {
refreshStatistics();
}
@Override
public void onPrepareOptionsMenu(@NonNull Menu menu) {
super.onPrepareOptionsMenu(menu);
menu.findItem(R.id.statistics_reset).setVisible(false);
menu.findItem(R.id.statistics_reset).setVisible(true);
menu.findItem(R.id.statistics_filter).setVisible(false);
}