Report sync progress
This commit is contained in:
parent
a4409c7c5c
commit
67de5de8c4
|
@ -10,10 +10,15 @@ import android.text.format.DateUtils;
|
|||
import android.widget.Toast;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.activity.PreferenceActivity;
|
||||
import de.danoeh.antennapod.core.event.SyncServiceEvent;
|
||||
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
|
||||
import de.danoeh.antennapod.core.sync.SyncService;
|
||||
import de.danoeh.antennapod.dialog.AuthenticationDialog;
|
||||
import de.danoeh.antennapod.dialog.GpodnetSetHostnameDialog;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
||||
|
||||
public class GpodderPreferencesFragment extends PreferenceFragmentCompat {
|
||||
private static final String PREF_GPODNET_LOGIN = "pref_gpodnet_authenticate";
|
||||
|
@ -34,28 +39,27 @@ public class GpodderPreferencesFragment extends PreferenceFragmentCompat {
|
|||
public void onStart() {
|
||||
super.onStart();
|
||||
((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.gpodnet_main_label);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
GpodnetPreferences.registerOnSharedPreferenceChangeListener(gpoddernetListener);
|
||||
updateGpodnetPreferenceScreen();
|
||||
EventBus.getDefault().register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
GpodnetPreferences.unregisterOnSharedPreferenceChangeListener(gpoddernetListener);
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
EventBus.getDefault().unregister(this);
|
||||
((PreferenceActivity) getActivity()).getSupportActionBar().setSubtitle("");
|
||||
}
|
||||
|
||||
private final SharedPreferences.OnSharedPreferenceChangeListener gpoddernetListener =
|
||||
(sharedPreferences, key) -> {
|
||||
//if (GpodnetPreferences.PREF_LAST_SYNC_ATTEMPT_TIMESTAMP.equals(key)) {
|
||||
// updateLastGpodnetSyncReport(GpodnetPreferences.getLastSyncAttemptResult(),
|
||||
// GpodnetPreferences.getLastSyncAttemptTimestamp());
|
||||
//}
|
||||
};
|
||||
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
|
||||
public void syncStatusChanged(SyncServiceEvent event) {
|
||||
if (event.getMessageResId() == R.string.sync_status_error
|
||||
|| event.getMessageResId() == R.string.sync_status_success) {
|
||||
updateLastGpodnetSyncReport(SyncService.isLastSyncSuccessful(getContext()),
|
||||
SyncService.getLastSyncAttempt(getContext()));
|
||||
} else {
|
||||
((PreferenceActivity) getActivity()).getSupportActionBar().setSubtitle(event.getMessageResId());
|
||||
}
|
||||
}
|
||||
|
||||
private void setupGpodderScreen() {
|
||||
final Activity activity = getActivity();
|
||||
|
@ -76,12 +80,10 @@ public class GpodderPreferencesFragment extends PreferenceFragmentCompat {
|
|||
});
|
||||
findPreference(PREF_GPODNET_SYNC).setOnPreferenceClickListener(preference -> {
|
||||
SyncService.sync(getActivity().getApplicationContext());
|
||||
Toast.makeText(getActivity(), R.string.pref_gpodnet_sync_started, Toast.LENGTH_SHORT).show();
|
||||
return true;
|
||||
});
|
||||
findPreference(PREF_GPODNET_FORCE_FULL_SYNC).setOnPreferenceClickListener(preference -> {
|
||||
SyncService.fullSync(getContext());
|
||||
Toast.makeText(getActivity(), R.string.pref_gpodnet_sync_started, Toast.LENGTH_SHORT).show();
|
||||
return true;
|
||||
});
|
||||
findPreference(PREF_GPODNET_LOGOUT).setOnPreferenceClickListener(preference -> {
|
||||
|
@ -106,13 +108,13 @@ public class GpodderPreferencesFragment extends PreferenceFragmentCompat {
|
|||
findPreference(PREF_GPODNET_FORCE_FULL_SYNC).setEnabled(loggedIn);
|
||||
findPreference(PREF_GPODNET_LOGOUT).setEnabled(loggedIn);
|
||||
findPreference(PREF_GPODNET_NOTIFICATIONS).setEnabled(loggedIn);
|
||||
if(loggedIn) {
|
||||
if (loggedIn) {
|
||||
String format = getActivity().getString(R.string.pref_gpodnet_login_status);
|
||||
String summary = String.format(format, GpodnetPreferences.getUsername(),
|
||||
GpodnetPreferences.getDeviceID());
|
||||
findPreference(PREF_GPODNET_LOGOUT).setSummary(Html.fromHtml(summary));
|
||||
//updateLastGpodnetSyncReport(GpodnetPreferences.getLastSyncAttemptResult(),
|
||||
// GpodnetPreferences.getLastSyncAttemptTimestamp());
|
||||
updateLastGpodnetSyncReport(SyncService.isLastSyncSuccessful(getContext()),
|
||||
SyncService.getLastSyncAttempt(getContext()));
|
||||
} else {
|
||||
findPreference(PREF_GPODNET_LOGOUT).setSummary(null);
|
||||
updateLastGpodnetSyncReport(false, 0);
|
||||
|
@ -121,20 +123,10 @@ public class GpodderPreferencesFragment extends PreferenceFragmentCompat {
|
|||
}
|
||||
|
||||
private void updateLastGpodnetSyncReport(boolean successful, long lastTime) {
|
||||
Preference sync = findPreference(PREF_GPODNET_SYNC);
|
||||
if (lastTime != 0) {
|
||||
sync.setSummary(getActivity().getString(R.string.pref_gpodnet_sync_changes_sum) + "\n" +
|
||||
getActivity().getString(R.string.pref_gpodnet_sync_sum_last_sync_line,
|
||||
getActivity().getString(successful ?
|
||||
R.string.gpodnetsync_pref_report_successful :
|
||||
R.string.gpodnetsync_pref_report_failed),
|
||||
DateUtils.getRelativeDateTimeString(getActivity(),
|
||||
lastTime,
|
||||
DateUtils.MINUTE_IN_MILLIS,
|
||||
DateUtils.WEEK_IN_MILLIS,
|
||||
DateUtils.FORMAT_SHOW_TIME)));
|
||||
} else {
|
||||
sync.setSummary(getActivity().getString(R.string.pref_gpodnet_sync_changes_sum));
|
||||
}
|
||||
String status = String.format("%1$s (%2$s)", getString(successful
|
||||
? R.string.gpodnetsync_pref_report_successful : R.string.gpodnetsync_pref_report_failed),
|
||||
DateUtils.getRelativeDateTimeString(getContext(),
|
||||
lastTime, DateUtils.MINUTE_IN_MILLIS, DateUtils.WEEK_IN_MILLIS, DateUtils.FORMAT_SHOW_TIME));
|
||||
((PreferenceActivity) getActivity()).getSupportActionBar().setSubtitle(status);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
package de.danoeh.antennapod.core.event;
|
||||
|
||||
public class SyncServiceEvent {
|
||||
private final int messageResId;
|
||||
|
||||
public SyncServiceEvent(int messageResId) {
|
||||
this.messageResId = messageResId;
|
||||
}
|
||||
|
||||
public int getMessageResId() {
|
||||
return messageResId;
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ import androidx.work.WorkManager;
|
|||
import androidx.work.Worker;
|
||||
import androidx.work.WorkerParameters;
|
||||
import de.danoeh.antennapod.core.R;
|
||||
import de.danoeh.antennapod.core.event.SyncServiceEvent;
|
||||
import de.danoeh.antennapod.core.feed.Feed;
|
||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||
|
@ -39,6 +40,7 @@ import de.danoeh.antennapod.core.sync.model.UploadChangesResponse;
|
|||
import de.danoeh.antennapod.core.util.URLChecker;
|
||||
import de.danoeh.antennapod.core.util.gui.NotificationUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
|
@ -55,6 +57,7 @@ public class SyncService extends Worker {
|
|||
private static final String PREF_QUEUED_FEEDS_REMOVED = "sync_removed";
|
||||
private static final String PREF_QUEUED_EPISODE_ACTIONS = "sync_queued_episode_actions";
|
||||
private static final String PREF_LAST_SYNC_ATTEMPT_TIMESTAMP = "last_sync_attempt_timestamp";
|
||||
private static final String PREF_LAST_SYNC_ATTEMPT_SUCCESS = "last_sync_attempt_success";
|
||||
private static final String TAG = "SyncService";
|
||||
private static final String WORK_ID_SYNC = "SyncServiceWorkId";
|
||||
private static final Object lock = new Object();
|
||||
|
@ -69,20 +72,26 @@ public class SyncService extends Worker {
|
|||
@NonNull
|
||||
public Result doWork() {
|
||||
syncServiceImpl = new GpodnetService(AntennapodHttpClient.getHttpClient(), GpodnetService.DEFAULT_BASE_HOST);
|
||||
SharedPreferences.Editor prefs = getApplicationContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
||||
.edit();
|
||||
prefs.putLong(PREF_LAST_SYNC_ATTEMPT_TIMESTAMP, System.currentTimeMillis()).apply();
|
||||
try {
|
||||
syncServiceImpl.login();
|
||||
EventBus.getDefault().post(new SyncServiceEvent(R.string.sync_status_subscriptions));
|
||||
syncSubscriptions();
|
||||
EventBus.getDefault().post(new SyncServiceEvent(R.string.sync_status_episodes));
|
||||
syncEpisodeActions();
|
||||
syncServiceImpl.logout();
|
||||
clearErrorNotifications();
|
||||
EventBus.getDefault().post(new SyncServiceEvent(R.string.sync_status_success));
|
||||
prefs.putBoolean(PREF_LAST_SYNC_ATTEMPT_SUCCESS, true).apply();
|
||||
return Result.success();
|
||||
} catch (SyncServiceException e) {
|
||||
EventBus.getDefault().post(new SyncServiceEvent(R.string.sync_status_error));
|
||||
prefs.putBoolean(PREF_LAST_SYNC_ATTEMPT_SUCCESS, false).apply();
|
||||
e.printStackTrace();
|
||||
updateErrorNotification(e);
|
||||
return Result.failure();
|
||||
} finally {
|
||||
getApplicationContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE).edit()
|
||||
.putLong(PREF_LAST_SYNC_ATTEMPT_TIMESTAMP, System.currentTimeMillis()).apply();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,18 +154,9 @@ public class SyncService extends Worker {
|
|||
}
|
||||
|
||||
public static void sync(Context context) {
|
||||
Constraints.Builder constraints = new Constraints.Builder();
|
||||
if (UserPreferences.isAllowMobileFeedRefresh()) {
|
||||
constraints.setRequiredNetworkType(NetworkType.CONNECTED);
|
||||
} else {
|
||||
constraints.setRequiredNetworkType(NetworkType.UNMETERED);
|
||||
}
|
||||
|
||||
OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(SyncService.class)
|
||||
.setConstraints(constraints.build())
|
||||
.setInitialDelay(5L, TimeUnit.SECONDS) // Give it some time, so other actions can be queued
|
||||
.build();
|
||||
OneTimeWorkRequest workRequest = getWorkRequest().build();
|
||||
WorkManager.getInstance().enqueueUniqueWork(WORK_ID_SYNC, ExistingWorkPolicy.REPLACE, workRequest);
|
||||
EventBus.getDefault().post(new SyncServiceEvent(R.string.sync_status_started));
|
||||
}
|
||||
|
||||
public static void fullSync(Context context) {
|
||||
|
@ -167,9 +167,35 @@ public class SyncService extends Worker {
|
|||
.putLong(PREF_LAST_SYNC_ATTEMPT_TIMESTAMP, 0)
|
||||
.apply();
|
||||
}
|
||||
sync(context);
|
||||
OneTimeWorkRequest workRequest = getWorkRequest()
|
||||
.setInitialDelay(0L, TimeUnit.SECONDS)
|
||||
.build();
|
||||
WorkManager.getInstance().enqueueUniqueWork(WORK_ID_SYNC, ExistingWorkPolicy.REPLACE, workRequest);
|
||||
EventBus.getDefault().post(new SyncServiceEvent(R.string.sync_status_started));
|
||||
}
|
||||
|
||||
private static OneTimeWorkRequest.Builder getWorkRequest() {
|
||||
Constraints.Builder constraints = new Constraints.Builder();
|
||||
if (UserPreferences.isAllowMobileFeedRefresh()) {
|
||||
constraints.setRequiredNetworkType(NetworkType.CONNECTED);
|
||||
} else {
|
||||
constraints.setRequiredNetworkType(NetworkType.UNMETERED);
|
||||
}
|
||||
|
||||
return new OneTimeWorkRequest.Builder(SyncService.class)
|
||||
.setConstraints(constraints.build())
|
||||
.setInitialDelay(5L, TimeUnit.SECONDS); // Give it some time, so other actions can be queued
|
||||
}
|
||||
|
||||
public static boolean isLastSyncSuccessful(Context context) {
|
||||
return context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
||||
.getBoolean(PREF_LAST_SYNC_ATTEMPT_SUCCESS, false);
|
||||
}
|
||||
|
||||
public static long getLastSyncAttempt(Context context) {
|
||||
return context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
||||
.getLong(PREF_LAST_SYNC_ATTEMPT_TIMESTAMP, 0);
|
||||
}
|
||||
|
||||
private List<EpisodeAction> getQueuedEpisodeActions() {
|
||||
ArrayList<EpisodeAction> actions = new ArrayList<>();
|
||||
|
@ -281,6 +307,7 @@ public class SyncService extends Worker {
|
|||
// upload local actions
|
||||
List<EpisodeAction> queuedEpisodeActions = getQueuedEpisodeActions();
|
||||
if (lastSync == 0) {
|
||||
EventBus.getDefault().post(new SyncServiceEvent(R.string.sync_status_upload_played));
|
||||
List<FeedItem> readItems = DBReader.getPlayedItems();
|
||||
Log.d(TAG, "First sync. Upload state for all " + readItems.size() + " played episodes");
|
||||
for (FeedItem item : readItems) {
|
||||
|
@ -300,7 +327,8 @@ public class SyncService extends Worker {
|
|||
}
|
||||
if (queuedEpisodeActions.size() > 0) {
|
||||
synchronized (lock) {
|
||||
Log.d(TAG, "Uploading actions: " + StringUtils.join(queuedEpisodeActions, ", "));
|
||||
Log.d(TAG, "Uploading " + queuedEpisodeActions.size() + " actions: "
|
||||
+ StringUtils.join(queuedEpisodeActions, ", "));
|
||||
UploadChangesResponse postResponse = syncServiceImpl.uploadEpisodeActions(queuedEpisodeActions);
|
||||
newTimeStamp = postResponse.timestamp;
|
||||
Log.d(TAG, "Upload episode response: " + postResponse);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package de.danoeh.antennapod.core.sync.gpoddernet;
|
||||
|
||||
import android.util.Log;
|
||||
import androidx.annotation.NonNull;
|
||||
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
|
||||
import de.danoeh.antennapod.core.sync.gpoddernet.model.GpodnetDevice;
|
||||
|
@ -42,8 +43,10 @@ import java.util.List;
|
|||
* Communicates with the gpodder.net service.
|
||||
*/
|
||||
public class GpodnetService implements ISyncService {
|
||||
public static final String TAG = "GpodnetService";
|
||||
public static final String DEFAULT_BASE_HOST = "gpodder.net";
|
||||
private static final String BASE_SCHEME = "https";
|
||||
private static final int UPLOAD_BULK_SIZE = 30;
|
||||
private static final MediaType TEXT = MediaType.parse("plain/text; charset=utf-8");
|
||||
private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
|
||||
private final String baseHost;
|
||||
|
@ -415,12 +418,24 @@ public class GpodnetService implements ISyncService {
|
|||
@Override
|
||||
public UploadChangesResponse uploadEpisodeActions(List<EpisodeAction> episodeActions) throws SyncServiceException {
|
||||
requireLoggedIn();
|
||||
UploadChangesResponse response = null;
|
||||
for (int i = 0; i < episodeActions.size(); i += UPLOAD_BULK_SIZE) {
|
||||
response = uploadEpisodeActionsPartial(episodeActions,
|
||||
i, Math.min(episodeActions.size(), i + UPLOAD_BULK_SIZE));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
private UploadChangesResponse uploadEpisodeActionsPartial(List<EpisodeAction> episodeActions, int from, int to)
|
||||
throws SyncServiceException {
|
||||
try {
|
||||
Log.d(TAG, "Uploading partial actions " + from + " to " + to + " of " + episodeActions.size());
|
||||
URL url = new URI(BASE_SCHEME, baseHost, String.format(
|
||||
"/api/2/episodes/%s.json", username), null).toURL();
|
||||
|
||||
final JSONArray list = new JSONArray();
|
||||
for (EpisodeAction episodeAction : episodeActions) {
|
||||
for (int i = from; i < to; i++) {
|
||||
EpisodeAction episodeAction = episodeActions.get(i);
|
||||
JSONObject obj = episodeAction.writeToJSONObject();
|
||||
if (obj != null) {
|
||||
obj.put("device", GpodnetPreferences.getDeviceID());
|
||||
|
@ -437,7 +452,6 @@ public class GpodnetService implements ISyncService {
|
|||
e.printStackTrace();
|
||||
throw new SyncServiceException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -441,8 +441,6 @@
|
|||
<string name="pref_gpodnet_sync_changes_sum">Sync subscription and episode state changes with gpodder.net.</string>
|
||||
<string name="pref_gpodnet_full_sync_title">Full sync now</string>
|
||||
<string name="pref_gpodnet_full_sync_sum">Sync all subscriptions and episode states with gpodder.net.</string>
|
||||
<string name="pref_gpodnet_sync_sum_last_sync_line">Last sync attempt: %1$s (%2$s)</string>
|
||||
<string name="pref_gpodnet_sync_started">Sync started</string>
|
||||
<string name="pref_gpodnet_login_status"><![CDATA[Logged in as <i>%1$s</i> with device <i>%2$s</i>]]></string>
|
||||
<string name="pref_gpodnet_notifications_title">Show sync error notifications</string>
|
||||
<string name="pref_gpodnet_notifications_sum">This setting does not apply to authentication errors.</string>
|
||||
|
@ -536,6 +534,14 @@
|
|||
<string name="search_label">Search</string>
|
||||
<string name="no_results_for_query">No results were found for \"%1$s\"</string>
|
||||
|
||||
<!-- Synchronization -->
|
||||
<string name="sync_status_started">Sync started</string>
|
||||
<string name="sync_status_episodes">Synchronizing episodes…</string>
|
||||
<string name="sync_status_upload_played">Uploading played status…</string>
|
||||
<string name="sync_status_subscriptions">Synchronizing subscriptions…</string>
|
||||
<string name="sync_status_success">Synchronization successful</string>
|
||||
<string name="sync_status_error">Synchronization failed</string>
|
||||
|
||||
<!-- import and export -->
|
||||
<string name="import_export_summary">Move subscriptions and queue to another device</string>
|
||||
<string name="database">Database</string>
|
||||
|
|
Loading…
Reference in New Issue