diff --git a/NextcloudApp/App.xaml.cs b/NextcloudApp/App.xaml.cs index d68418e..fa6bc99 100644 --- a/NextcloudApp/App.xaml.cs +++ b/NextcloudApp/App.xaml.cs @@ -44,6 +44,7 @@ public App() } public IActivatedEventArgs ActivatedEventArgs { get; private set; } + private readonly LocalSettings SettingsLocal = SettingsService.Default.Value.LocalSettings; private async void TaskSchedulerOnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs args) { @@ -316,11 +317,22 @@ protected override Task OnSuspendingApplicationAsync() var task = base.OnSuspendingApplicationAsync(); // Stop Background Sync Tasks var activeSyncs = SyncDbUtils.GetActiveSyncInfos(); - foreach (var fsi in activeSyncs) + + if (SettingsLocal.PauseSyncInBackground) + { + foreach (var fsi in activeSyncs) + { + ToastNotificationService.ShowSyncSuspendedNotification(fsi); + SyncDbUtils.UnlockFolderSyncInfo(fsi); + } + } else { - ToastNotificationService.ShowSyncSuspendedNotification(fsi); - SyncDbUtils.UnlockFolderSyncInfo(fsi); + foreach (var fsi in activeSyncs) + { + ToastNotificationService.ShowSyncInBackgroundNotification(fsi); + } } + return task; } diff --git a/NextcloudApp/Models/LocalSettings.cs b/NextcloudApp/Models/LocalSettings.cs index 54b620f..5acd576 100644 --- a/NextcloudApp/Models/LocalSettings.cs +++ b/NextcloudApp/Models/LocalSettings.cs @@ -58,6 +58,22 @@ public PreviewImageDownloadMode PreviewImageDownloadMode } } + [DefaultSettingValue(Value = SyncMode.LocalToRemote)] + public SyncMode SyncMode + { + get + { + var strVal = Get(); + + return string.IsNullOrEmpty(strVal) ? SyncMode.LocalToRemote : JsonConvert.DeserializeObject(strVal); + } + set + { + var strVal = JsonConvert.SerializeObject(value); + Set(strVal); + } + } + [DefaultSettingValue(Value = 0)] public int AppTotalRuns { @@ -100,6 +116,20 @@ public bool IgnoreServerCertificateErrors set => Set(value); } + [DefaultSettingValue(Value = false)] + public bool SyncDeletions + { + get => Get(); + set => Set(value); + } + + [DefaultSettingValue(Value = false)] + public bool PauseSyncInBackground + { + get => Get(); + set => Set(value); + } + [DefaultSettingValue(Value = false)] public bool ExpertMode { diff --git a/NextcloudApp/NextcloudApp.csproj b/NextcloudApp/NextcloudApp.csproj index 15df3ff..474adc0 100644 --- a/NextcloudApp/NextcloudApp.csproj +++ b/NextcloudApp/NextcloudApp.csproj @@ -210,6 +210,7 @@ + @@ -442,9 +443,6 @@ Visual C++ 2015 Runtime for Universal Windows Platform Apps - - SQLite for Universal Windows Platform %28SQLite.UWP.2015, Version=3.21.0%29 - Windows Mobile Extensions for the UWP @@ -477,6 +475,9 @@ 3.1.1 + + 3.25.3 + 14.0 diff --git a/NextcloudApp/Services/SyncService.cs b/NextcloudApp/Services/SyncService.cs index c78962f..399f680 100644 --- a/NextcloudApp/Services/SyncService.cs +++ b/NextcloudApp/Services/SyncService.cs @@ -24,6 +24,7 @@ public class SyncService private NextcloudClient.NextcloudClient _client; private readonly List _sidList; private readonly IResourceLoader _resourceLoader; + private readonly LocalSettings SettingsLocal = SettingsService.Default.Value.LocalSettings; public SyncService(StorageFolder startFolder, ResourceInfo resourceInfo, FolderSyncInfo syncInfo, IResourceLoader resourceLoader) { @@ -162,15 +163,24 @@ private async Task SyncFolder(ResourceInfo resourceInfo, StorageFolder fold if (subSid != null) { Debug.WriteLine("Sync folder (delete remotely) " + subInfo.Path); - if (await _client.Delete(subInfo.Path)) - { - SyncDbUtils.DeleteSyncInfoDetail(subSid, true); + + if (SettingsLocal.SyncDeletions) + { + if (await _client.Delete(subInfo.Path)) + { + SyncDbUtils.DeleteSyncInfoDetail(subSid, true); + } + else + { + sid.Error = string.Format(_resourceLoader.GetString(ResourceConstants.SyncService_Error_DeleteFolderRemotely), subInfo.Path); + // Error could be overridden by other errors + } } - else - { - sid.Error = string.Format(_resourceLoader.GetString(ResourceConstants.SyncService_Error_DeleteFolderRemotely), subInfo.Path); - // Error could be overridden by other errors + else + { + SyncDbUtils.DeleteSyncInfoDetail(subSid, true); } + } else { @@ -241,10 +251,15 @@ private async Task SyncFolder(ResourceInfo resourceInfo, StorageFolder fold var subSid = SyncDbUtils.GetSyncInfoDetail(localFolder, _folderSyncInfo); if (subSid != null) { - // Delete all sids and local folder - Debug.WriteLine("Sync folder (delete locally) " + localFolder.Path); - await localFolder.DeleteAsync(); - SyncDbUtils.DeleteSyncInfoDetail(subSid, true); + if (SettingsLocal.SyncDeletions) + { + // Delete all sids and local folder + Debug.WriteLine("Sync folder (delete locally) " + localFolder.Path); + await localFolder.DeleteAsync(); + } + + SyncDbUtils.DeleteSyncInfoDetail(subSid, true); + } else { @@ -341,7 +356,7 @@ private async Task SyncFile(ResourceInfo info, StorageFile file, ResourceIn sid.Error = string.Format(_resourceLoader.GetString(ResourceConstants.SyncService_Error_UploadFile), file.Name); } } - else if (info != null) + else if (info != null && (SettingsLocal.SyncMode == SyncMode.RemoteToLocal || SettingsLocal.SyncMode == SyncMode.TwoWay)) { // Create sid and download file var localFile = await parentFolder.CreateFileAsync(info.Name); @@ -370,9 +385,12 @@ private async Task SyncFile(ResourceInfo info, StorageFile file, ResourceIn { Debug.WriteLine("Sync file (Delete locally) " + sid.Path); // Remove sid and local file - if (file != null) - { - await file.DeleteAsync(); + if (SettingsLocal.SyncDeletions) + { + if (file != null) + { + await file.DeleteAsync(); + } } SyncDbUtils.DeleteSyncInfoDetail(sid, false); changed = true; @@ -425,12 +443,16 @@ private async Task SyncFile(ResourceInfo info, StorageFile file, ResourceIn { if (sid.ETag == null || info.ETag.Equals(sid.ETag)) { - Debug.WriteLine("Sync file (Delete remotely) " + sid.Path); - // Remove sid and remote file - await _client.Delete(info.Path + "/" + info.Name); - SyncDbUtils.DeleteSyncInfoDetail(sid, false); - deleted = true; - changed = true; + if (SettingsLocal.SyncDeletions) + { + Debug.WriteLine("Sync file (Delete remotely) " + sid.Path); + // Remove sid and remote file + if (SettingsLocal.SyncDeletions) + await _client.Delete(info.Path + "/" + info.Name); + SyncDbUtils.DeleteSyncInfoDetail(sid, false); + deleted = true; + changed = true; + } } else { @@ -474,7 +496,7 @@ private async Task SyncFile(ResourceInfo info, StorageFile file, ResourceIn { if (currentModified.Equals(sid.DateModified)) { - if (!info.ETag.Equals(sid.ETag)) + if (!info.ETag.Equals(sid.ETag) && (SettingsLocal.SyncMode == SyncMode.RemoteToLocal || SettingsLocal.SyncMode == SyncMode.TwoWay)) { // Update local file Debug.WriteLine("Sync file (update locally) " + info.Path + "/" + info.Name); @@ -490,7 +512,7 @@ private async Task SyncFile(ResourceInfo info, StorageFile file, ResourceIn } } } - else if (info.ETag.Equals(sid.ETag)) + else if (info.ETag.Equals(sid.ETag) && (SettingsLocal.SyncMode == SyncMode.LocalToRemote || SettingsLocal.SyncMode == SyncMode.TwoWay)) { // update file on nextcloud Debug.WriteLine("Sync file (update remotely) " + info.Path + "/" + info.Name); @@ -600,6 +622,8 @@ private async Task UploadFile(IStorageFile localFile, string path) IProgress progress = new Progress(ProgressHandler); result = await _client.Upload(path, targetStream, localFile.ContentType, progress, cts.Token); } + + ToastNotificationService.ShowSyncedFileNotification(localFile.Name); } catch (ResponseError e2) { diff --git a/NextcloudApp/Services/ToastNotificationService.cs b/NextcloudApp/Services/ToastNotificationService.cs index 4c4eb85..35aab2e 100644 --- a/NextcloudApp/Services/ToastNotificationService.cs +++ b/NextcloudApp/Services/ToastNotificationService.cs @@ -119,5 +119,103 @@ internal static void ShowSyncSuspendedNotification(FolderSyncInfo fsi) // TODO groups/tags? ToastNotificationManager.CreateToastNotifier().Show(toast); } + + internal static void ShowSyncInBackgroundNotification(FolderSyncInfo fsi) + { + if (fsi == null) + { + return; + } + var loader = new ResourceLoader(); + var title = loader.GetString("SyncInBackgroundTitle"); + var content = string.Format(loader.GetString("SyncInBackgroundDescription"), fsi.Path); + const string action = SyncAction; + + // Construct the visuals of the toast + var visual = new ToastVisual + { + BindingGeneric = new ToastBindingGeneric + { + Children = + { + new AdaptiveText + { + Text = title + }, + new AdaptiveText + { + Text = content + } + } + } + }; + var toastContent = new ToastContent + { + Visual = visual, + + // Arguments when the user taps body of toast + Launch = new QueryString + { + { "action", action } + }.ToString() + }; + var toast = new ToastNotification(toastContent.GetXml()) + { + ExpirationTime = DateTime.Now.AddMinutes(30), + Group = action + }; + // TODO Replace with syncinterval from settings. + // TODO groups/tags? + ToastNotificationManager.CreateToastNotifier().Show(toast); + } + + internal static void ShowSyncedFileNotification(string fileName) + { + if (fileName == null) + { + return; + } + var loader = new ResourceLoader(); + var title = loader.GetString("SyncedFileTitle"); + var content = fileName; + const string action = SyncAction; + + // Construct the visuals of the toast + var visual = new ToastVisual + { + BindingGeneric = new ToastBindingGeneric + { + Children = + { + new AdaptiveText + { + Text = title + }, + new AdaptiveText + { + Text = content + } + } + } + }; + var toastContent = new ToastContent + { + Visual = visual, + + // Arguments when the user taps body of toast + Launch = new QueryString + { + { "action", action } + }.ToString() + }; + var toast = new ToastNotification(toastContent.GetXml()) + { + ExpirationTime = DateTime.Now.AddMinutes(30), + Group = action + }; + // TODO Replace with syncinterval from settings. + // TODO groups/tags? + ToastNotificationManager.CreateToastNotifier().Show(toast); + } } } diff --git a/NextcloudApp/Strings/en/Resources.resw b/NextcloudApp/Strings/en/Resources.resw index 34b660b..08c123a 100644 --- a/NextcloudApp/Strings/en/Resources.resw +++ b/NextcloudApp/Strings/en/Resources.resw @@ -688,4 +688,13 @@ Please try again later. Are you sure, you want to delete the selected files? - + + File synced + + + Synchronization in background + + + Synchronization of folder {0} is running in background, yay! + + \ No newline at end of file diff --git a/NextcloudApp/Utils/SyncMode.cs b/NextcloudApp/Utils/SyncMode.cs new file mode 100644 index 0000000..a0f114d --- /dev/null +++ b/NextcloudApp/Utils/SyncMode.cs @@ -0,0 +1,9 @@ +namespace NextcloudApp.Utils +{ + public enum SyncMode + { + LocalToRemote, + RemoteToLocal, + TwoWay + } +} diff --git a/NextcloudApp/ViewModels/SettingsPageViewModel.cs b/NextcloudApp/ViewModels/SettingsPageViewModel.cs index 6e0e1c2..ccd7ba9 100644 --- a/NextcloudApp/ViewModels/SettingsPageViewModel.cs +++ b/NextcloudApp/ViewModels/SettingsPageViewModel.cs @@ -26,6 +26,8 @@ public class SettingsPageViewModel : ViewModel private string _serverVersion; private bool _ignoreServerCertificateErrors; private bool _expertMode; + private bool _syncDeletions; + private bool _pauseSyncInBackground; public ICommand ResetCommand { get; } public ICommand ShowHelpExpertModeCommand { get; } @@ -42,6 +44,7 @@ public SettingsPageViewModel(INavigationService navigationService, IResourceLoad UseWindowsHello = SettingsLocal.UseWindowsHello; IgnoreServerCertificateErrors = SettingsLocal.IgnoreServerCertificateErrors; ExpertMode = SettingsLocal.ExpertMode; + SyncDeletions = SettingsLocal.SyncDeletions; ResetCommand = new DelegateCommand(Reset); ShowHelpExpertModeCommand = new DelegateCommand(ShowHelpExpertMode); @@ -125,6 +128,30 @@ public bool ExpertMode } } + public bool SyncDeletions + { + get => _syncDeletions; + set + { + if (!SetProperty(ref _syncDeletions, value)) + return; + + SettingsLocal.SyncDeletions = value; + } + } + + public bool PauseSyncInBackground + { + get => _pauseSyncInBackground; + set + { + if (!SetProperty(ref _pauseSyncInBackground, value)) + return; + + SettingsLocal.PauseSyncInBackground = value; + } + } + public bool UseWindowsHello { get => _useWindowsHello; @@ -273,6 +300,63 @@ public bool PreviewImageDownloadModeAsNever } } + public bool SyncModeAsLocalToRemote + { + get => SyncMode.Equals(SyncMode.LocalToRemote); + set + { + if (value) + { + SyncMode = SyncMode.LocalToRemote; + } + } + } + + public bool SyncModeAsRemoteToLocal + { + get => SyncMode.Equals(SyncMode.RemoteToLocal); + set + { + if (value) + { + SyncMode = SyncMode.RemoteToLocal; + } + } + } + + public bool SyncModeAsTwoWay + { + get => SyncMode.Equals(SyncMode.TwoWay); + set + { + if (value) + { + SyncMode = SyncMode.TwoWay; + } + } + } + + public SyncMode SyncMode + { + get => SettingsLocal.SyncMode; + set + { + if (SettingsLocal.SyncMode.Equals(value)) + { + return; + } + + SettingsLocal.SyncMode = value; + + // ReSharper disable once ExplicitCallerInfoArgument + RaisePropertyChanged(nameof(SyncModeAsLocalToRemote)); + // ReSharper disable once ExplicitCallerInfoArgument + RaisePropertyChanged(nameof(SyncModeAsRemoteToLocal)); + // ReSharper disable once ExplicitCallerInfoArgument + RaisePropertyChanged(nameof(SyncModeAsTwoWay)); + } + } + public Theme Theme { get => SettingsRoaming.Theme; diff --git a/NextcloudApp/Views/SettingsPage.xaml b/NextcloudApp/Views/SettingsPage.xaml index 196dc72..5c199a9 100644 --- a/NextcloudApp/Views/SettingsPage.xaml +++ b/NextcloudApp/Views/SettingsPage.xaml @@ -54,7 +54,54 @@ Content="Never" IsChecked="{Binding PreviewImageDownloadModeAsNever, Mode=TwoWay}" GroupName="PreviewImageDownloadModeNever"/> + + + + + + + + + + + + + + + + + - +