diff options
author | thrillfall <thrillfall@users.noreply.github.com> | 2021-10-06 22:12:47 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-06 22:12:47 +0200 |
commit | bc85ebc806367d863973bc9434e7b0d9d5fd2168 (patch) | |
tree | 5a729b84f1a12c3de8d3178ad7d688eb6bb552be /app | |
parent | dab44b68436601f415edb095da605811e985eb00 (diff) | |
download | AntennaPod-bc85ebc806367d863973bc9434e7b0d9d5fd2168.zip |
Add synchronization with gPodder Nextcloud server app (#5243)
Diffstat (limited to 'app')
16 files changed, 488 insertions, 212 deletions
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 600204554..1fc16ab32 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java @@ -6,6 +6,7 @@ import android.os.Bundle; import android.provider.Settings; import android.view.Menu; import android.view.MenuItem; + import android.view.View; import android.view.inputmethod.InputMethodManager; @@ -21,13 +22,13 @@ import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.databinding.SettingsActivityBinding; import de.danoeh.antennapod.fragment.preferences.AutoDownloadPreferencesFragment; -import de.danoeh.antennapod.fragment.preferences.GpodderPreferencesFragment; import de.danoeh.antennapod.fragment.preferences.ImportExportPreferencesFragment; import de.danoeh.antennapod.fragment.preferences.MainPreferencesFragment; import de.danoeh.antennapod.fragment.preferences.NetworkPreferencesFragment; import de.danoeh.antennapod.fragment.preferences.NotificationPreferencesFragment; import de.danoeh.antennapod.fragment.preferences.PlaybackPreferencesFragment; import de.danoeh.antennapod.fragment.preferences.StoragePreferencesFragment; +import de.danoeh.antennapod.fragment.preferences.synchronization.SynchronizationPreferencesFragment; import de.danoeh.antennapod.fragment.preferences.SwipePreferencesFragment; import de.danoeh.antennapod.fragment.preferences.UserInterfacePreferencesFragment; @@ -76,8 +77,8 @@ public class PreferenceActivity extends AppCompatActivity implements SearchPrefe prefFragment = new ImportExportPreferencesFragment(); } else if (screen == R.xml.preferences_autodownload) { prefFragment = new AutoDownloadPreferencesFragment(); - } else if (screen == R.xml.preferences_gpodder) { - prefFragment = new GpodderPreferencesFragment(); + } else if (screen == R.xml.preferences_synchronization) { + prefFragment = new SynchronizationPreferencesFragment(); } else if (screen == R.xml.preferences_playback) { prefFragment = new PlaybackPreferencesFragment(); } else if (screen == R.xml.preferences_notifications) { @@ -101,8 +102,8 @@ public class PreferenceActivity extends AppCompatActivity implements SearchPrefe return R.string.import_export_pref; } else if (preferences == R.xml.preferences_user_interface) { return R.string.user_interface_label; - } else if (preferences == R.xml.preferences_gpodder) { - return R.string.gpodnet_main_label; + } else if (preferences == R.xml.preferences_synchronization) { + return R.string.synchronization_pref; } else if (preferences == R.xml.preferences_notifications) { return R.string.notification_pref_fragment; } else if (preferences == R.xml.feed_settings) { diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java b/app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java index f97c1c7ab..340783208 100644 --- a/app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java +++ b/app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java @@ -1,6 +1,6 @@ package de.danoeh.antennapod.discovery; -import de.danoeh.antennapod.core.preferences.GpodnetPreferences; +import de.danoeh.antennapod.core.sync.SynchronizationCredentials; import de.danoeh.antennapod.core.service.download.AntennapodHttpClient; import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetService; import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetServiceException; @@ -18,8 +18,8 @@ public class GpodnetPodcastSearcher implements PodcastSearcher { return Single.create((SingleOnSubscribe<List<PodcastSearchResult>>) subscriber -> { try { GpodnetService service = new GpodnetService(AntennapodHttpClient.getHttpClient(), - GpodnetPreferences.getHosturl(), GpodnetPreferences.getDeviceID(), - GpodnetPreferences.getUsername(), GpodnetPreferences.getPassword()); + SynchronizationCredentials.getHosturl(), SynchronizationCredentials.getDeviceID(), + SynchronizationCredentials.getUsername(), SynchronizationCredentials.getPassword()); List<GpodnetPodcast> gpodnetPodcasts = service.searchPodcasts(query, 0); List<PodcastSearchResult> results = new ArrayList<>(); for (GpodnetPodcast podcast : gpodnetPodcasts) { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java index c813cbf7a..af502ce13 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java @@ -15,7 +15,7 @@ import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.activity.OnlineFeedViewActivity; import de.danoeh.antennapod.adapter.gpodnet.PodcastListAdapter; -import de.danoeh.antennapod.core.preferences.GpodnetPreferences; +import de.danoeh.antennapod.core.sync.SynchronizationCredentials; import de.danoeh.antennapod.core.service.download.AntennapodHttpClient; import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetService; import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetServiceException; @@ -76,8 +76,8 @@ public abstract class PodcastListFragment extends Fragment { disposable = Observable.fromCallable( () -> { GpodnetService service = new GpodnetService(AntennapodHttpClient.getHttpClient(), - GpodnetPreferences.getHosturl(), GpodnetPreferences.getDeviceID(), - GpodnetPreferences.getUsername(), GpodnetPreferences.getPassword()); + SynchronizationCredentials.getHosturl(), SynchronizationCredentials.getDeviceID(), + SynchronizationCredentials.getUsername(), SynchronizationCredentials.getPassword()); return loadPodcastData(service); }) .subscribeOn(Schedulers.io()) diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java index f961e30bb..abdfab941 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java @@ -8,7 +8,7 @@ import androidx.annotation.NonNull; import androidx.fragment.app.ListFragment; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.adapter.gpodnet.TagListAdapter; -import de.danoeh.antennapod.core.preferences.GpodnetPreferences; +import de.danoeh.antennapod.core.sync.SynchronizationCredentials; import de.danoeh.antennapod.core.service.download.AntennapodHttpClient; import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetService; import de.danoeh.antennapod.net.sync.gpoddernet.model.GpodnetTag; @@ -51,8 +51,8 @@ public class TagListFragment extends ListFragment { disposable = Observable.fromCallable( () -> { GpodnetService service = new GpodnetService(AntennapodHttpClient.getHttpClient(), - GpodnetPreferences.getHosturl(), GpodnetPreferences.getDeviceID(), - GpodnetPreferences.getUsername(), GpodnetPreferences.getPassword()); + SynchronizationCredentials.getHosturl(), SynchronizationCredentials.getDeviceID(), + SynchronizationCredentials.getUsername(), SynchronizationCredentials.getPassword()); return service.getTopTags(COUNT); }) .subscribeOn(Schedulers.io()) diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java deleted file mode 100644 index 4fb734e17..000000000 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java +++ /dev/null @@ -1,128 +0,0 @@ -package de.danoeh.antennapod.fragment.preferences; - -import android.app.Activity; -import android.os.Bundle; -import androidx.core.text.HtmlCompat; -import androidx.preference.PreferenceFragmentCompat; - -import android.text.Spanned; -import android.text.format.DateUtils; -import com.google.android.material.snackbar.Snackbar; -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 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"; - private static final String PREF_GPODNET_SETLOGIN_INFORMATION = "pref_gpodnet_setlogin_information"; - private static final String PREF_GPODNET_SYNC = "pref_gpodnet_sync"; - private static final String PREF_GPODNET_FORCE_FULL_SYNC = "pref_gpodnet_force_full_sync"; - private static final String PREF_GPODNET_LOGOUT = "pref_gpodnet_logout"; - - @Override - public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { - addPreferencesFromResource(R.xml.preferences_gpodder); - setupGpodderScreen(); - } - - @Override - public void onStart() { - super.onStart(); - ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.gpodnet_main_label); - updateGpodnetPreferenceScreen(); - EventBus.getDefault().register(this); - } - - @Override - public void onStop() { - super.onStop(); - EventBus.getDefault().unregister(this); - ((PreferenceActivity) getActivity()).getSupportActionBar().setSubtitle(""); - } - - @Subscribe(threadMode = ThreadMode.MAIN, sticky = true) - public void syncStatusChanged(SyncServiceEvent event) { - updateGpodnetPreferenceScreen(); - if (!GpodnetPreferences.loggedIn()) { - return; - } - 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(); - - findPreference(PREF_GPODNET_LOGIN).setOnPreferenceClickListener(preference -> { - new GpodderAuthenticationFragment().show(getChildFragmentManager(), GpodderAuthenticationFragment.TAG); - return true; - }); - findPreference(PREF_GPODNET_SETLOGIN_INFORMATION) - .setOnPreferenceClickListener(preference -> { - AuthenticationDialog dialog = new AuthenticationDialog(activity, - R.string.pref_gpodnet_setlogin_information_title, false, GpodnetPreferences.getUsername(), - null) { - - @Override - protected void onConfirmed(String username, String password) { - GpodnetPreferences.setPassword(password); - } - }; - dialog.show(); - return true; - }); - findPreference(PREF_GPODNET_SYNC).setOnPreferenceClickListener(preference -> { - SyncService.syncImmediately(getActivity().getApplicationContext()); - return true; - }); - findPreference(PREF_GPODNET_FORCE_FULL_SYNC).setOnPreferenceClickListener(preference -> { - SyncService.fullSync(getContext()); - return true; - }); - findPreference(PREF_GPODNET_LOGOUT).setOnPreferenceClickListener(preference -> { - GpodnetPreferences.logout(); - Snackbar.make(getView(), R.string.pref_gpodnet_logout_toast, Snackbar.LENGTH_LONG).show(); - updateGpodnetPreferenceScreen(); - return true; - }); - } - - private void updateGpodnetPreferenceScreen() { - final boolean loggedIn = GpodnetPreferences.loggedIn(); - findPreference(PREF_GPODNET_LOGIN).setEnabled(!loggedIn); - findPreference(PREF_GPODNET_SETLOGIN_INFORMATION).setEnabled(loggedIn); - findPreference(PREF_GPODNET_SYNC).setEnabled(loggedIn); - findPreference(PREF_GPODNET_FORCE_FULL_SYNC).setEnabled(loggedIn); - findPreference(PREF_GPODNET_LOGOUT).setEnabled(loggedIn); - if (loggedIn) { - String format = getActivity().getString(R.string.pref_gpodnet_login_status); - String summary = String.format(format, GpodnetPreferences.getUsername(), - GpodnetPreferences.getDeviceID()); - Spanned formattedSummary = HtmlCompat.fromHtml(summary, HtmlCompat.FROM_HTML_MODE_LEGACY); - findPreference(PREF_GPODNET_LOGOUT).setSummary(formattedSummary); - updateLastGpodnetSyncReport(SyncService.isLastSyncSuccessful(getContext()), - SyncService.getLastSyncAttempt(getContext())); - } else { - findPreference(PREF_GPODNET_LOGOUT).setSummary(null); - } - } - - private void updateLastGpodnetSyncReport(boolean successful, long lastTime) { - 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); - } -} 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 cc09acbca..83ad3110a 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 @@ -17,12 +17,11 @@ import de.danoeh.antennapod.core.util.IntentUtils; import de.danoeh.antennapod.fragment.preferences.about.AboutFragment; public class MainPreferencesFragment extends PreferenceFragmentCompat { - private static final String TAG = "MainPreferencesFragment"; private static final String PREF_SCREEN_USER_INTERFACE = "prefScreenInterface"; private static final String PREF_SCREEN_PLAYBACK = "prefScreenPlayback"; private static final String PREF_SCREEN_NETWORK = "prefScreenNetwork"; - private static final String PREF_SCREEN_GPODDER = "prefScreenGpodder"; + private static final String PREF_SCREEN_SYNCHRONIZATION = "prefScreenSynchronization"; private static final String PREF_SCREEN_STORAGE = "prefScreenStorage"; private static final String PREF_DOCUMENTATION = "prefDocumentation"; private static final String PREF_VIEW_FORUM = "prefViewForum"; @@ -74,8 +73,8 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat { ((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_network); return true; }); - findPreference(PREF_SCREEN_GPODDER).setOnPreferenceClickListener(preference -> { - ((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_gpodder); + findPreference(PREF_SCREEN_SYNCHRONIZATION).setOnPreferenceClickListener(preference -> { + ((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_synchronization); return true; }); findPreference(PREF_SCREEN_STORAGE).setOnPreferenceClickListener(preference -> { @@ -142,8 +141,8 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat { .addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_network)) .addBreadcrumb(R.string.automation) .addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_autodownload)); - config.index(R.xml.preferences_gpodder) - .addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_gpodder)); + config.index(R.xml.preferences_synchronization) + .addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_synchronization)); config.index(R.xml.preferences_notifications) .addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_notifications)); config.index(R.xml.feed_settings) diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NotificationPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NotificationPreferencesFragment.java index 94e151f7a..ba17cedb2 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NotificationPreferencesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NotificationPreferencesFragment.java @@ -4,11 +4,10 @@ import android.os.Bundle; import androidx.preference.PreferenceFragmentCompat; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.PreferenceActivity; -import de.danoeh.antennapod.core.preferences.GpodnetPreferences; +import de.danoeh.antennapod.core.sync.SynchronizationSettings; public class NotificationPreferencesFragment extends PreferenceFragmentCompat { - private static final String TAG = "NotificationPrefFragment"; private static final String PREF_GPODNET_NOTIFICATIONS = "pref_gpodnet_notifications"; @Override @@ -24,7 +23,6 @@ public class NotificationPreferencesFragment extends PreferenceFragmentCompat { } private void setUpScreen() { - final boolean loggedIn = GpodnetPreferences.loggedIn(); - findPreference(PREF_GPODNET_NOTIFICATIONS).setEnabled(loggedIn); + findPreference(PREF_GPODNET_NOTIFICATIONS).setEnabled(SynchronizationSettings.isProviderConnected()); } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderAuthenticationFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/synchronization/GpodderAuthenticationFragment.java index c0bf3e0ea..9dfe6840c 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderAuthenticationFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/synchronization/GpodderAuthenticationFragment.java @@ -1,4 +1,4 @@ -package de.danoeh.antennapod.fragment.preferences; +package de.danoeh.antennapod.fragment.preferences.synchronization; import android.app.Dialog; import android.content.Context; @@ -15,30 +15,35 @@ import android.widget.ProgressBar; import android.widget.RadioGroup; import android.widget.TextView; import android.widget.ViewFlipper; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.DialogFragment; + import com.google.android.material.button.MaterialButton; import com.google.android.material.textfield.TextInputLayout; + +import java.util.List; +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + import de.danoeh.antennapod.R; -import de.danoeh.antennapod.core.preferences.GpodnetPreferences; +import de.danoeh.antennapod.core.sync.SynchronizationCredentials; import de.danoeh.antennapod.core.service.download.AntennapodHttpClient; import de.danoeh.antennapod.core.sync.SyncService; -import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetService; -import de.danoeh.antennapod.net.sync.gpoddernet.model.GpodnetDevice; +import de.danoeh.antennapod.core.sync.SynchronizationProviderViewData; +import de.danoeh.antennapod.core.sync.SynchronizationSettings; import de.danoeh.antennapod.core.util.FileNameGenerator; import de.danoeh.antennapod.core.util.IntentUtils; +import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetService; +import de.danoeh.antennapod.net.sync.gpoddernet.model.GpodnetDevice; import io.reactivex.Completable; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; -import java.util.List; -import java.util.Locale; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - /** * Guides the user through the authentication process. */ @@ -83,23 +88,24 @@ public class GpodderAuthenticationFragment extends DialogFragment { final RadioGroup serverRadioGroup = view.findViewById(R.id.serverRadioGroup); final EditText serverUrlText = view.findViewById(R.id.serverUrlText); - if (!GpodnetService.DEFAULT_BASE_HOST.equals(GpodnetPreferences.getHosturl())) { - serverUrlText.setText(GpodnetPreferences.getHosturl()); + if (!GpodnetService.DEFAULT_BASE_HOST.equals(SynchronizationCredentials.getHosturl())) { + serverUrlText.setText(SynchronizationCredentials.getHosturl()); } final TextInputLayout serverUrlTextInput = view.findViewById(R.id.serverUrlTextInput); serverRadioGroup.setOnCheckedChangeListener((group, checkedId) -> { serverUrlTextInput.setVisibility(checkedId == R.id.customServerRadio ? View.VISIBLE : View.GONE); }); selectHost.setOnClickListener(v -> { + SynchronizationCredentials.clear(getContext()); if (serverRadioGroup.getCheckedRadioButtonId() == R.id.customServerRadio) { - GpodnetPreferences.setHosturl(serverUrlText.getText().toString()); + SynchronizationCredentials.setHosturl(serverUrlText.getText().toString()); } else { - GpodnetPreferences.setHosturl(GpodnetService.DEFAULT_BASE_HOST); + SynchronizationCredentials.setHosturl(GpodnetService.DEFAULT_BASE_HOST); } service = new GpodnetService(AntennapodHttpClient.getHttpClient(), - GpodnetPreferences.getHosturl(), GpodnetPreferences.getDeviceID(), - GpodnetPreferences.getUsername(), GpodnetPreferences.getPassword()); - getDialog().setTitle(GpodnetPreferences.getHosturl()); + SynchronizationCredentials.getHosturl(), SynchronizationCredentials.getDeviceID(), + SynchronizationCredentials.getUsername(), SynchronizationCredentials.getPassword()); + getDialog().setTitle(SynchronizationCredentials.getHosturl()); advance(); }); } @@ -116,7 +122,7 @@ public class GpodderAuthenticationFragment extends DialogFragment { createAccount.setPaintFlags(createAccount.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); createAccount.setOnClickListener(v -> IntentUtils.openInBrowser(getContext(), "https://gpodder.net/register/")); - if (GpodnetPreferences.getHosturl().startsWith("http://")) { + if (SynchronizationCredentials.getHosturl().startsWith("http://")) { createAccountWarning.setVisibility(View.VISIBLE); } password.setOnEditorActionListener((v, actionID, event) -> @@ -265,15 +271,8 @@ public class GpodderAuthenticationFragment extends DialogFragment { }); } - private void writeLoginCredentials() { - GpodnetPreferences.setUsername(username); - GpodnetPreferences.setPassword(password); - GpodnetPreferences.setDeviceID(selectedDevice.getId()); - } - private void advance() { if (currentStep < STEP_FINISH) { - View view = viewFlipper.getChildAt(currentStep + 1); if (currentStep == STEP_DEFAULT) { setupHostView(view); @@ -289,7 +288,10 @@ public class GpodderAuthenticationFragment extends DialogFragment { if (selectedDevice == null) { throw new IllegalStateException("Device must not be null here"); } else { - writeLoginCredentials(); + SynchronizationSettings.setSelectedSyncProvider(SynchronizationProviderViewData.GPODDER_NET); + SynchronizationCredentials.setUsername(username); + SynchronizationCredentials.setPassword(password); + SynchronizationCredentials.setDeviceID(selectedDevice.getId()); setupFinishView(view); } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/synchronization/NextcloudAuthenticationFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/synchronization/NextcloudAuthenticationFragment.java new file mode 100644 index 000000000..2e9260c1d --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/synchronization/NextcloudAuthenticationFragment.java @@ -0,0 +1,92 @@ +package de.danoeh.antennapod.fragment.preferences.synchronization; + +import android.app.Dialog; +import android.content.DialogInterface; +import android.os.Bundle; +import android.view.View; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.DialogFragment; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.service.download.AntennapodHttpClient; +import de.danoeh.antennapod.core.sync.SyncService; +import de.danoeh.antennapod.core.sync.SynchronizationCredentials; +import de.danoeh.antennapod.core.sync.SynchronizationProviderViewData; +import de.danoeh.antennapod.core.sync.SynchronizationSettings; +import de.danoeh.antennapod.databinding.NextcloudAuthDialogBinding; +import de.danoeh.antennapod.net.sync.nextcloud.NextcloudLoginFlow; + +/** + * Guides the user through the authentication process. + */ +public class NextcloudAuthenticationFragment extends DialogFragment + implements NextcloudLoginFlow.AuthenticationCallback { + public static final String TAG = "NextcloudAuthenticationFragment"; + private NextcloudAuthDialogBinding viewBinding; + private NextcloudLoginFlow nextcloudLoginFlow; + private boolean shouldDismiss = false; + + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + AlertDialog.Builder dialog = new AlertDialog.Builder(getContext()); + dialog.setTitle(R.string.gpodnetauth_login_butLabel); + dialog.setNegativeButton(R.string.cancel_label, null); + dialog.setCancelable(false); + this.setCancelable(false); + + viewBinding = NextcloudAuthDialogBinding.inflate(getLayoutInflater()); + dialog.setView(viewBinding.getRoot()); + + viewBinding.loginButton.setOnClickListener(v -> { + viewBinding.errorText.setVisibility(View.GONE); + viewBinding.loginButton.setVisibility(View.GONE); + viewBinding.loginProgressContainer.setVisibility(View.VISIBLE); + nextcloudLoginFlow = new NextcloudLoginFlow(AntennapodHttpClient.getHttpClient(), + viewBinding.serverUrlText.getText().toString(), getContext(), this); + nextcloudLoginFlow.start(); + }); + + return dialog.create(); + } + + @Override + public void onDismiss(@NonNull DialogInterface dialog) { + super.onDismiss(dialog); + if (nextcloudLoginFlow != null) { + nextcloudLoginFlow.cancel(); + } + } + + @Override + public void onResume() { + super.onResume(); + if (shouldDismiss) { + dismiss(); + } + } + + @Override + public void onNextcloudAuthenticated(String server, String username, String password) { + SynchronizationSettings.setSelectedSyncProvider(SynchronizationProviderViewData.NEXTCLOUD_GPODDER); + SynchronizationCredentials.clear(getContext()); + SynchronizationCredentials.setPassword(password); + SynchronizationCredentials.setHosturl(server); + SynchronizationCredentials.setUsername(username); + SyncService.fullSync(getContext()); + if (isVisible()) { + dismiss(); + } else { + shouldDismiss = true; + } + } + + @Override + public void onNextcloudAuthError(String errorMessage) { + viewBinding.loginProgressContainer.setVisibility(View.GONE); + viewBinding.errorText.setVisibility(View.VISIBLE); + viewBinding.errorText.setText(errorMessage); + viewBinding.loginButton.setVisibility(View.VISIBLE); + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/synchronization/SynchronizationPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/synchronization/SynchronizationPreferencesFragment.java new file mode 100644 index 000000000..9b63b38ec --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/synchronization/SynchronizationPreferencesFragment.java @@ -0,0 +1,222 @@ +package de.danoeh.antennapod.fragment.preferences.synchronization; + +import android.app.Activity; +import android.os.Bundle; +import android.text.Spanned; +import android.text.format.DateUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.ListAdapter; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.core.text.HtmlCompat; +import androidx.preference.Preference; +import androidx.preference.PreferenceFragmentCompat; + +import com.google.android.material.snackbar.Snackbar; + +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; + +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.PreferenceActivity; +import de.danoeh.antennapod.core.event.SyncServiceEvent; +import de.danoeh.antennapod.core.sync.SynchronizationCredentials; +import de.danoeh.antennapod.core.sync.SyncService; +import de.danoeh.antennapod.core.sync.SynchronizationProviderViewData; +import de.danoeh.antennapod.core.sync.SynchronizationSettings; +import de.danoeh.antennapod.dialog.AuthenticationDialog; + +public class SynchronizationPreferencesFragment extends PreferenceFragmentCompat { + private static final String PREFERENCE_SYNCHRONIZATION_DESCRIPTION = "preference_synchronization_description"; + private static final String PREFERENCE_GPODNET_SETLOGIN_INFORMATION = "pref_gpodnet_setlogin_information"; + private static final String PREFERENCE_SYNC = "pref_synchronization_sync"; + private static final String PREFERENCE_FORCE_FULL_SYNC = "pref_synchronization_force_full_sync"; + private static final String PREFERENCE_LOGOUT = "pref_synchronization_logout"; + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + addPreferencesFromResource(R.xml.preferences_synchronization); + setupScreen(); + updateScreen(); + } + + @Override + public void onStart() { + super.onStart(); + ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.synchronization_pref); + updateScreen(); + EventBus.getDefault().register(this); + } + + @Override + public void onStop() { + super.onStop(); + EventBus.getDefault().unregister(this); + ((PreferenceActivity) getActivity()).getSupportActionBar().setSubtitle(""); + } + + @Subscribe(threadMode = ThreadMode.MAIN, sticky = true) + public void syncStatusChanged(SyncServiceEvent event) { + if (!SynchronizationSettings.isProviderConnected()) { + return; + } + updateScreen(); + if (event.getMessageResId() == R.string.sync_status_error + || event.getMessageResId() == R.string.sync_status_success) { + updateLastSyncReport(SynchronizationSettings.isLastSyncSuccessful(), + SynchronizationSettings.getLastSyncAttempt()); + } else { + ((PreferenceActivity) getActivity()).getSupportActionBar().setSubtitle(event.getMessageResId()); + } + } + + private void setupScreen() { + final Activity activity = getActivity(); + findPreference(PREFERENCE_GPODNET_SETLOGIN_INFORMATION) + .setOnPreferenceClickListener(preference -> { + AuthenticationDialog dialog = new AuthenticationDialog(activity, + R.string.pref_gpodnet_setlogin_information_title, + false, SynchronizationCredentials.getUsername(), null) { + @Override + protected void onConfirmed(String username, String password) { + SynchronizationCredentials.setPassword(password); + } + }; + dialog.show(); + return true; + }); + findPreference(PREFERENCE_SYNC).setOnPreferenceClickListener(preference -> { + SyncService.syncImmediately(getActivity().getApplicationContext()); + return true; + }); + findPreference(PREFERENCE_FORCE_FULL_SYNC).setOnPreferenceClickListener(preference -> { + SyncService.fullSync(getContext()); + return true; + }); + findPreference(PREFERENCE_LOGOUT).setOnPreferenceClickListener(preference -> { + SynchronizationCredentials.clear(getContext()); + Snackbar.make(getView(), R.string.pref_synchronization_logout_toast, Snackbar.LENGTH_LONG).show(); + SynchronizationSettings.setSelectedSyncProvider(null); + updateScreen(); + return true; + }); + } + + private void updateScreen() { + final boolean loggedIn = SynchronizationSettings.isProviderConnected(); + Preference preferenceHeader = findPreference(PREFERENCE_SYNCHRONIZATION_DESCRIPTION); + if (loggedIn) { + SynchronizationProviderViewData selectedProvider = + SynchronizationProviderViewData.fromIdentifier(getSelectedSyncProviderKey()); + preferenceHeader.setTitle(""); + preferenceHeader.setSummary(selectedProvider.getSummaryResource()); + preferenceHeader.setIcon(selectedProvider.getIconResource()); + preferenceHeader.setOnPreferenceClickListener(null); + } else { + preferenceHeader.setTitle(R.string.synchronization_choose_title); + preferenceHeader.setSummary(R.string.synchronization_summary_unchoosen); + preferenceHeader.setIcon(R.drawable.ic_cloud); + preferenceHeader.setOnPreferenceClickListener((preference) -> { + chooseProviderAndLogin(); + return true; + }); + } + + Preference gpodnetSetLoginPreference = findPreference(PREFERENCE_GPODNET_SETLOGIN_INFORMATION); + gpodnetSetLoginPreference.setVisible(isProviderSelected(SynchronizationProviderViewData.GPODDER_NET)); + gpodnetSetLoginPreference.setEnabled(loggedIn); + findPreference(PREFERENCE_SYNC).setEnabled(loggedIn); + findPreference(PREFERENCE_FORCE_FULL_SYNC).setEnabled(loggedIn); + findPreference(PREFERENCE_LOGOUT).setEnabled(loggedIn); + if (loggedIn) { + String summary = getString(R.string.synchronization_login_status, + SynchronizationCredentials.getUsername(), SynchronizationCredentials.getHosturl()); + Spanned formattedSummary = HtmlCompat.fromHtml(summary, HtmlCompat.FROM_HTML_MODE_LEGACY); + findPreference(PREFERENCE_LOGOUT).setSummary(formattedSummary); + updateLastSyncReport(SynchronizationSettings.isLastSyncSuccessful(), + SynchronizationSettings.getLastSyncAttempt()); + } else { + findPreference(PREFERENCE_LOGOUT).setSummary(null); + ((PreferenceActivity) getActivity()).getSupportActionBar().setSubtitle(null); + } + } + + private void chooseProviderAndLogin() { + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setTitle(R.string.dialog_choose_sync_service_title); + + SynchronizationProviderViewData[] providers = SynchronizationProviderViewData.values(); + ListAdapter adapter = new ArrayAdapter<SynchronizationProviderViewData>( + getContext(), R.layout.alertdialog_sync_provider_chooser, providers) { + + ViewHolder holder; + + class ViewHolder { + ImageView icon; + TextView title; + } + + public View getView(int position, View convertView, ViewGroup parent) { + final LayoutInflater inflater = LayoutInflater.from(getContext()); + if (convertView == null) { + convertView = inflater.inflate( + R.layout.alertdialog_sync_provider_chooser, null); + + holder = new ViewHolder(); + holder.icon = (ImageView) convertView.findViewById(R.id.icon); + holder.title = (TextView) convertView.findViewById(R.id.title); + convertView.setTag(holder); + } else { + holder = (ViewHolder) convertView.getTag(); + } + SynchronizationProviderViewData synchronizationProviderViewData = getItem(position); + holder.title.setText(synchronizationProviderViewData.getSummaryResource()); + holder.icon.setImageResource(synchronizationProviderViewData.getIconResource()); + return convertView; + } + }; + + builder.setAdapter(adapter, (dialog, which) -> { + switch (providers[which]) { + case GPODDER_NET: + new GpodderAuthenticationFragment() + .show(getChildFragmentManager(), GpodderAuthenticationFragment.TAG); + break; + case NEXTCLOUD_GPODDER: + new NextcloudAuthenticationFragment() + .show(getChildFragmentManager(), NextcloudAuthenticationFragment.TAG); + break; + default: + break; + } + updateScreen(); + }); + + AlertDialog dialog = builder.create(); + dialog.show(); + } + + private boolean isProviderSelected(@NonNull SynchronizationProviderViewData provider) { + String selectedSyncProviderKey = getSelectedSyncProviderKey(); + return provider.getIdentifier().equals(selectedSyncProviderKey); + } + + private String getSelectedSyncProviderKey() { + return SynchronizationSettings.getSelectedSyncProviderKey(); + } + + private void updateLastSyncReport(boolean successful, long lastTime) { + 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); + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java index c272af7d5..23fdb86de 100644 --- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java +++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java @@ -13,12 +13,12 @@ import com.google.android.material.snackbar.Snackbar; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; -import de.danoeh.antennapod.core.preferences.GpodnetPreferences; import de.danoeh.antennapod.core.preferences.PlaybackPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.playback.PlaybackService; import de.danoeh.antennapod.core.storage.DBWriter; -import de.danoeh.antennapod.core.sync.SyncService; +import de.danoeh.antennapod.core.sync.SynchronizationSettings; +import de.danoeh.antennapod.core.sync.queue.SynchronizationQueueSink; import de.danoeh.antennapod.core.util.FeedItemUtil; import de.danoeh.antennapod.core.util.IntentUtils; import de.danoeh.antennapod.core.util.ShareUtils; @@ -151,7 +151,7 @@ public class FeedItemMenuHandler { } else if (menuItemId == R.id.mark_read_item) { selectedItem.setPlayed(true); DBWriter.markItemPlayed(selectedItem, FeedItem.PLAYED, true); - if (GpodnetPreferences.loggedIn()) { + if (SynchronizationSettings.isProviderConnected()) { FeedMedia media = selectedItem.getMedia(); // not all items have media, Gpodder only cares about those that do if (media != null) { @@ -161,17 +161,17 @@ public class FeedItemMenuHandler { .position(media.getDuration() / 1000) .total(media.getDuration() / 1000) .build(); - SyncService.enqueueEpisodeAction(context, actionPlay); + SynchronizationQueueSink.enqueueEpisodeActionIfSynchronizationIsActive(context, actionPlay); } } } else if (menuItemId == R.id.mark_unread_item) { selectedItem.setPlayed(false); DBWriter.markItemPlayed(selectedItem, FeedItem.UNPLAYED, false); - if (GpodnetPreferences.loggedIn() && selectedItem.getMedia() != null) { + if (selectedItem.getMedia() != null) { EpisodeAction actionNew = new EpisodeAction.Builder(selectedItem, EpisodeAction.NEW) .currentTimestamp() .build(); - SyncService.enqueueEpisodeAction(context, actionNew); + SynchronizationQueueSink.enqueueEpisodeActionIfSynchronizationIsActive(context, actionNew); } } else if (menuItemId == R.id.add_to_queue_item) { DBWriter.addQueueItem(context, selectedItem); diff --git a/app/src/main/res/layout/alertdialog_sync_provider_chooser.xml b/app/src/main/res/layout/alertdialog_sync_provider_chooser.xml new file mode 100644 index 000000000..9b4d62804 --- /dev/null +++ b/app/src/main/res/layout/alertdialog_sync_provider_chooser.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:padding="16dp"> + + <ImageView + android:id="@+id/icon" + android:layout_width="48dp" + android:layout_height="48dp" + android:layout_marginRight="16dip" + android:layout_marginEnd="16dip" + android:layout_gravity="center_vertical" /> + + <TextView + android:id="@+id/title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="" + android:layout_gravity="center" /> + +</LinearLayout> diff --git a/app/src/main/res/layout/nextcloud_auth_dialog.xml b/app/src/main/res/layout/nextcloud_auth_dialog.xml new file mode 100644 index 000000000..345eec88b --- /dev/null +++ b/app/src/main/res/layout/nextcloud_auth_dialog.xml @@ -0,0 +1,63 @@ +<?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:padding="16dp" + android:orientation="vertical" + android:clipToPadding="false"> + + <com.google.android.material.textfield.TextInputLayout + android:id="@+id/serverUrlTextInput" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="16dp" + style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"> + + <com.google.android.material.textfield.TextInputEditText + android:id="@+id/serverUrlText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:hint="@string/gpodnetauth_host" + android:inputType="textNoSuggestions" + android:lines="1" + android:imeOptions="actionNext|flagNoFullscreen" /> + + </com.google.android.material.textfield.TextInputLayout> + + <LinearLayout + android:id="@+id/loginProgressContainer" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:visibility="gone" + android:orientation="horizontal" + android:layout_gravity="center_vertical"> + + <ProgressBar + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="8dp" + android:layout_marginRight="8dp" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/synchronization_nextcloud_authenticate_browser" /> + + </LinearLayout> + + <TextView + android:id="@+id/errorText" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="gone" + android:textColor="@color/download_failed_red" + android:layout_marginBottom="16dp" /> + + <Button + android:id="@+id/loginButton" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/gpodnetauth_login_butLabel" /> + +</LinearLayout> diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index d528945c7..7c5012899 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -28,7 +28,7 @@ android:icon="@drawable/ic_network" /> <Preference - android:key="prefScreenGpodder" + android:key="prefScreenSynchronization" android:title="@string/synchronization_pref" android:summary="@string/synchronization_sum" android:icon="@drawable/ic_cloud" /> diff --git a/app/src/main/res/xml/preferences_gpodder.xml b/app/src/main/res/xml/preferences_gpodder.xml deleted file mode 100644 index a210b8e11..000000000 --- a/app/src/main/res/xml/preferences_gpodder.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<PreferenceScreen - xmlns:android="http://schemas.android.com/apk/res/android"> - <Preference - android:key="pref_gpodnet_description" - android:icon="@drawable/gpodder_icon" - android:summary="@string/gpodnet_description"/> - <Preference - android:key="pref_gpodnet_authenticate" - android:title="@string/pref_gpodnet_authenticate_title" - android:summary="@string/pref_gpodnet_authenticate_sum"/> - <Preference - android:key="pref_gpodnet_setlogin_information" - android:title="@string/pref_gpodnet_setlogin_information_title" - android:summary="@string/pref_gpodnet_setlogin_information_sum"/> - <Preference - android:key="pref_gpodnet_sync" - android:title="@string/pref_gpodnet_sync_changes_title" - android:summary="@string/pref_gpodnet_sync_changes_sum"/> - <Preference - android:key="pref_gpodnet_force_full_sync" - android:title="@string/pref_gpodnet_full_sync_title" - android:summary="@string/pref_gpodnet_full_sync_sum"/> - <Preference - android:key="pref_gpodnet_logout" - android:title="@string/pref_gpodnet_logout_title"/> - -</PreferenceScreen> diff --git a/app/src/main/res/xml/preferences_synchronization.xml b/app/src/main/res/xml/preferences_synchronization.xml new file mode 100644 index 000000000..fbd4ccc79 --- /dev/null +++ b/app/src/main/res/xml/preferences_synchronization.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<PreferenceScreen + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> + + <Preference + android:key="preference_synchronization_description" + android:icon="@drawable/ic_notification_sync" + android:summary="@string/synchronization_summary_unchoosen"/> + + <Preference + android:key="pref_gpodnet_setlogin_information" + android:title="@string/pref_gpodnet_setlogin_information_title" + android:summary="@string/pref_gpodnet_setlogin_information_sum" + app:isPreferenceVisible="false"/> + + <Preference + android:key="pref_synchronization_sync" + android:title="@string/synchronization_sync_changes_title" + android:summary="@string/synchronization_sync_summary"/> + + <Preference + android:key="pref_synchronization_force_full_sync" + android:title="@string/synchronization_full_sync_title" + android:summary="@string/synchronization_force_sync_summary"/> + + <Preference + android:key="pref_synchronization_logout" + android:title="@string/synchronization_logout"/> + +</PreferenceScreen> |