Merge pull request #3502 from xgouchet/feature/1867_reset_statistics

Reset playback statistics
This commit is contained in:
H. Lehmann 2019-10-14 16:27:09 +02:00 committed by GitHub
commit 516fbb201a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 127 additions and 35 deletions

View File

@ -1,14 +1,9 @@
package de.danoeh.antennapod.fragment.preferences;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.appcompat.app.AlertDialog;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
@ -18,10 +13,21 @@ 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.activity.PreferenceActivity;
import de.danoeh.antennapod.adapter.StatisticsListAdapter;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import io.reactivex.Completable;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
@ -52,7 +58,9 @@ public class StatisticsFragment extends Fragment {
@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);
feedStatisticsList = root.findViewById(R.id.statistics_list);
progressBar = root.findViewById(R.id.progressBar);
@ -70,9 +78,18 @@ public class StatisticsFragment extends Fragment {
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
@ -81,6 +98,10 @@ public class StatisticsFragment extends Fragment {
selectStatisticsMode();
return true;
}
if (item.getItemId() == R.id.statistics_reset) {
confirmResetStatistics();
return true;
}
return super.onOptionsItemSelected(item);
}
@ -101,11 +122,42 @@ public class StatisticsFragment extends Fragment {
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(this::refreshStatistics, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
private void refreshStatistics() {
progressBar.setVisibility(View.VISIBLE);
feedStatisticsList.setVisibility(View.GONE);

View File

@ -2,6 +2,12 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/statistics_reset"
android:title="@string/statistics_reset_data"
custom:showAsAction="never"
/>
<item
android:id="@+id/statistics_mode"
android:icon="?attr/ic_filter"

View File

@ -2,9 +2,10 @@ package de.danoeh.antennapod.core.storage;
import android.app.backup.BackupManager;
import android.content.Context;
import androidx.annotation.NonNull;
import android.util.Log;
import androidx.annotation.NonNull;
import org.greenrobot.eventbus.EventBus;
import java.io.File;
@ -86,7 +87,8 @@ public class DBWriter {
});
}
private static boolean deleteFeedMediaSynchronous(@NonNull Context context, @NonNull FeedMedia media) {
private static boolean deleteFeedMediaSynchronous(
@NonNull Context context, @NonNull FeedMedia media) {
Log.i(TAG, String.format("Requested to delete FeedMedia [id=%d, title=%s, downloaded=%s",
media.getId(), media.getEpisodeTitle(), String.valueOf(media.isDownloaded())));
if (media.isDownloaded()) {
@ -111,7 +113,7 @@ public class DBWriter {
}
// Gpodder: queue delete action for synchronization
if(GpodnetPreferences.loggedIn()) {
if (GpodnetPreferences.loggedIn()) {
FeedItem item = media.getItem();
GpodnetEpisodeAction action = new GpodnetEpisodeAction.Builder(item, GpodnetEpisodeAction.Action.DELETE)
.currentDeviceId()
@ -159,7 +161,7 @@ public class DBWriter {
adapter.open();
if (removed.size() > 0) {
adapter.setQueue(queue);
for(FeedItem item : removed) {
for (FeedItem item : removed) {
EventBus.getDefault().post(QueueEvent.irreversibleRemoved(item));
}
}
@ -184,7 +186,6 @@ public class DBWriter {
/**
* Deletes the entire playback history.
*
*/
public static Future<?> clearPlaybackHistory() {
return dbExec.submit(() -> {
@ -215,7 +216,7 @@ public class DBWriter {
* its playback completion date is set to a non-null value. This method will set the playback completion date to the
* current date regardless of the current value.
*
* @param media FeedMedia that should be added to the playback history.
* @param media FeedMedia that should be added to the playback history.
*/
public static Future<?> addItemToPlaybackHistory(final FeedMedia media) {
return dbExec.submit(() -> {
@ -234,7 +235,7 @@ public class DBWriter {
/**
* Adds a Download status object to the download log.
*
* @param status The DownloadStatus object.
* @param status The DownloadStatus object.
*/
public static Future<?> addDownloadStatus(final DownloadStatus status) {
return dbExec.submit(() -> {
@ -304,9 +305,9 @@ public class DBWriter {
* Appends FeedItem objects to the end of the queue. The 'read'-attribute of all items will be set to true.
* If a FeedItem is already in the queue, the FeedItem will not change its position in the queue.
*
* @param context A context that is used for opening a database connection.
* @param context A context that is used for opening a database connection.
* @param performAutoDownload true if an auto-download process should be started after the operation.
* @param itemIds IDs of the FeedItem objects that should be added to the queue.
* @param itemIds IDs of the FeedItem objects that should be added to the queue.
*/
public static Future<?> addQueueItem(final Context context, final boolean performAutoDownload,
final long... itemIds) {
@ -394,7 +395,6 @@ public class DBWriter {
/**
* Removes all FeedItem objects from the queue.
*
*/
public static Future<?> clearQueue() {
return dbExec.submit(() -> {
@ -409,7 +409,8 @@ public class DBWriter {
/**
* Removes a FeedItem object from the queue.
* @param context A context that is used for opening a database connection.
*
* @param context A context that is used for opening a database connection.
* @param performAutoDownload true if an auto-download process should be started after the operation.
* @param item FeedItem that should be removed.
*/
@ -497,14 +498,15 @@ public class DBWriter {
/**
* Moves the specified item to the top of the queue.
* @param itemId The item to move to the top of the queue
*
* @param itemId The item to move to the top of the queue
* @param broadcastUpdate true if this operation should trigger a QueueUpdateBroadcast. This option should be set to
*/
public static Future<?> moveQueueItemToTop(final long itemId, final boolean broadcastUpdate) {
return dbExec.submit(() -> {
LongList queueIdList = DBReader.getQueueIDList();
int index = queueIdList.indexOf(itemId);
if (index >=0) {
if (index >= 0) {
moveQueueItemHelper(index, 0, broadcastUpdate);
} else {
Log.e(TAG, "moveQueueItemToTop: item not found");
@ -514,7 +516,8 @@ public class DBWriter {
/**
* Moves the specified item to the bottom of the queue.
* @param itemId The item to move to the bottom of the queue
*
* @param itemId The item to move to the bottom of the queue
* @param broadcastUpdate true if this operation should trigger a QueueUpdateBroadcast. This option should be set to
*/
public static Future<?> moveQueueItemToBottom(final long itemId,
@ -524,7 +527,7 @@ public class DBWriter {
int index = queueIdList.indexOf(itemId);
if (index >= 0) {
moveQueueItemHelper(index, queueIdList.size() - 1,
broadcastUpdate);
broadcastUpdate);
} else {
Log.e(TAG, "moveQueueItemToBottom: item not found");
}
@ -605,7 +608,7 @@ public class DBWriter {
adapter.open();
adapter.setFeedItemRead(played, itemIds);
adapter.close();
if(broadcastUpdate) {
if (broadcastUpdate) {
EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast();
}
});
@ -614,7 +617,8 @@ public class DBWriter {
/**
* Sets the 'read'-attribute of a FeedItem to the specified value.
* @param item The FeedItem object
*
* @param item The FeedItem object
* @param played New value of the 'read'-attribute one of FeedItem.PLAYED,
* FeedItem.NEW, FeedItem.UNPLAYED
* @param resetMediaPosition true if this method should also reset the position of the FeedItem's FeedMedia object.
@ -644,7 +648,7 @@ public class DBWriter {
/**
* Sets the 'read'-attribute of all NEW FeedItems of a specific Feed to UNPLAYED.
*
* @param feedId ID of the Feed.
* @param feedId ID of the Feed.
*/
public static Future<?> removeFeedNewFlag(final long feedId) {
return dbExec.submit(() -> {
@ -660,7 +664,7 @@ public class DBWriter {
/**
* Sets the 'read'-attribute of all FeedItems of a specific Feed to PLAYED.
*
* @param feedId ID of the Feed.
* @param feedId ID of the Feed.
*/
public static Future<?> markFeedRead(final long feedId) {
return dbExec.submit(() -> {
@ -732,7 +736,7 @@ public class DBWriter {
* Saves a FeedMedia object in the database. This method will save all attributes of the FeedMedia object. The
* contents of FeedComponent-attributes (e.g. the FeedMedia's 'item'-attribute) will not be saved.
*
* @param media The FeedMedia object.
* @param media The FeedMedia object.
*/
public static Future<?> setFeedMedia(final FeedMedia media) {
return dbExec.submit(() -> {
@ -746,7 +750,7 @@ public class DBWriter {
/**
* Saves the 'position', 'duration' and 'last played time' attributes of a FeedMedia object
*
* @param media The FeedMedia object.
* @param media The FeedMedia object.
*/
public static Future<?> setFeedMediaPlaybackInformation(final FeedMedia media) {
return dbExec.submit(() -> {
@ -761,7 +765,7 @@ public class DBWriter {
* Saves a FeedItem object in the database. This method will save all attributes of the FeedItem object including
* the content of FeedComponent-attributes.
*
* @param item The FeedItem object.
* @param item The FeedItem object.
*/
public static Future<?> setFeedItem(final FeedItem item) {
return dbExec.submit(() -> {
@ -777,7 +781,7 @@ public class DBWriter {
* Updates download URL of a feed
*/
public static Future<?> updateFeedDownloadURL(final String original, final String updated) {
Log.d(TAG, "updateFeedDownloadURL(original: " + original + ", updated: " + updated +")");
Log.d(TAG, "updateFeedDownloadURL(original: " + original + ", updated: " + updated + ")");
return dbExec.submit(() -> {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
@ -802,11 +806,11 @@ public class DBWriter {
}
private static boolean itemListContains(List<FeedItem> items, long itemId) {
return indexInItemList(items, itemId) >= 0;
return indexInItemList(items, itemId) >= 0;
}
private static int indexInItemList(List<FeedItem> items, long itemId) {
for (int i = 0; i < items.size(); i ++) {
for (int i = 0; i < items.size(); i++) {
FeedItem item = items.get(i);
if (item.getId() == itemId) {
return i;
@ -870,7 +874,7 @@ public class DBWriter {
/**
* Sets the 'auto_download'-attribute of specific FeedItem.
*
* @param feedItem FeedItem.
* @param feedItem FeedItem.
* @param autoDownload true enables auto download, false disables it
*/
public static Future<?> setFeedItemAutoDownload(final FeedItem feedItem,
@ -888,7 +892,7 @@ public class DBWriter {
return dbExec.submit(() -> {
int failedAttempts = feedItem.getFailedAutoDownloadAttempts() + 1;
long autoDownload;
if(!feedItem.getAutoDownload() || failedAttempts >= 10) {
if (!feedItem.getAutoDownload() || failedAttempts >= 10) {
autoDownload = 0; // giving up, disable auto download
feedItem.setAutoDownload(false);
} else {
@ -924,7 +928,8 @@ public class DBWriter {
/**
* Set filter of the feed
* @param feedId The feed's ID
*
* @param feedId The feed's ID
* @param filterValues Values that represent properties to filter by
*/
public static Future<?> setFeedItemsFilter(final long feedId,
@ -939,4 +944,17 @@ public class DBWriter {
});
}
/**
* Reset the statistics in DB
*/
@NonNull
public static Future<?> resetStatistics() {
return dbExec.submit(() -> {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.resetAllMediaPlayedDuration();
adapter.close();
});
}
}

View File

@ -476,6 +476,20 @@ public class PodDBAdapter {
}
}
public void resetAllMediaPlayedDuration() {
try {
db.beginTransactionNonExclusive();
ContentValues values = new ContentValues();
values.put(KEY_PLAYED_DURATION, 0);
db.update(TABLE_NAME_FEED_MEDIA, values, null, new String[0]);
db.setTransactionSuccessful();
} catch (SQLException e) {
Log.e(TAG, Log.getStackTraceString(e));
} finally {
db.endTransaction();
}
}
/**
* Insert all FeedItems of a feed and the feed object itself in a single
* transaction

View File

@ -37,6 +37,8 @@
<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 podcasts marked as played</string>
<string name="statistics_speed_not_counted">Notice: Playback speed is never taken into account.</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>
<!-- Main activity -->
<string name="drawer_open">Open menu</string>