private static async Task DownloadRoamingDataFromServerAsync( IRemoteStorageProvider remoteStorageProvider, IEnumerable <RemoteFileInfo> allOtherRoamingFiles, IEnumerable <StorageFile> allOtherLocalFiles, CancellationToken cancellationToken) { // Download the user data bundle file from the server. HandleDownloadResult(await remoteStorageProvider.DownloadFileAsync(Constants.UserDataBundleFileName, cancellationToken).ConfigureAwait(false)); // Delete local data files that don't exist remotely foreach (StorageFile localFile in allOtherLocalFiles) { if (!allOtherRoamingFiles.Any(roamingFile => string.Equals(Path.GetFileName(roamingFile.FullPath), localFile.Name, StringComparison.Ordinal))) { await localFile.DeleteAsync(StorageDeleteOption.PermanentDelete); } } // Download remote data files that don't exist locally foreach (RemoteFileInfo roamingFile in allOtherRoamingFiles) { if (!allOtherLocalFiles.Any(localFile => string.Equals(Path.GetFileName(roamingFile.FullPath), localFile.Name, StringComparison.Ordinal))) { HandleDownloadResult(await remoteStorageProvider.DownloadFileAsync(roamingFile.FullPath, cancellationToken).ConfigureAwait(false)); } } }
private static async Task UploadLocalDataToServerAsync( IRemoteStorageProvider remoteStorageProvider, StorageFile localUserDataBundleFile, IEnumerable <RemoteFileInfo> allOtherRoamingFiles, IEnumerable <StorageFile> allOtherLocalFiles, CancellationToken cancellationToken) { // Upload the user data bundle file to the server. HandleUploadResult(await remoteStorageProvider.UploadFileAsync(localUserDataBundleFile, cancellationToken).ConfigureAwait(false)); // Delete roaming data files that don't exist locally foreach (RemoteFileInfo roamingFile in allOtherRoamingFiles) { if (!allOtherLocalFiles.Any(localFile => string.Equals(Path.GetFileName(roamingFile.FullPath), localFile.Name, StringComparison.Ordinal))) { await remoteStorageProvider.DeleteFileAsync(roamingFile.FullPath, cancellationToken).ConfigureAwait(false); } } // Upload local data files that don't exist remotely foreach (StorageFile localFile in allOtherLocalFiles) { if (!allOtherRoamingFiles.Any(roamingFile => string.Equals(Path.GetFileName(roamingFile.FullPath), localFile.Name, StringComparison.Ordinal))) { HandleUploadResult(await remoteStorageProvider.UploadFileAsync(localFile, cancellationToken).ConfigureAwait(false)); } } }
private async Task SynchronizeAsync(CancellationToken cancellationToken) { // The semaphore acts like queue. using (await _sempahore.WaitAsync(cancellationToken).ConfigureAwait(false)) { var synchronizationStarted = false; var succeeded = false; var requiresReloadLocalData = false; try { if (!_settingsProvider.GetSetting(SettingsDefinitions.SyncDataWithCloud)) { return; } string targetterProviderName = _settingsProvider.GetSetting(SettingsDefinitions.RemoteStorageProviderName); IRemoteStorageProvider remoteStorageProvider = _remoteStorageProviders.SingleOrDefault(m => string.Equals(m.Metadata.ProviderName, targetterProviderName, StringComparison.Ordinal))?.Value; if (remoteStorageProvider == null) { return; } if (!CoreHelper.IsInternetAccess()) { return; } SynchronizationStarted?.Invoke(this, EventArgs.Empty); synchronizationStarted = true; if (!await remoteStorageProvider.SignInAsync(interactive: false, cancellationToken).ConfigureAwait(false)) { // If fails to authenticate, disables synchronization and sign out. _settingsProvider.SetSetting(SettingsDefinitions.SyncDataWithCloud, false); await remoteStorageProvider.SignOutAsync().ConfigureAwait(false); // TODO: Add a log to notify to let the user know it signed out and he should re-authenticate. // returning here will still trigger the Finally block. return; } // Retrieve the list of online files. IReadOnlyList <RemoteFileInfo> roamingFiles = await remoteStorageProvider.GetFilesAsync(Constants.DataFileCountLimit, cancellationToken).ConfigureAwait(false); RemoteFileInfo roamingUserDataBundleFile = roamingFiles.FirstOrDefault(file => string.Equals(Path.GetFileName(file.FullPath), Constants.UserDataBundleFileName, StringComparison.Ordinal)); IEnumerable <RemoteFileInfo> allOtherRoamingFiles = roamingFiles.Where(file => !string.Equals(Path.GetFileName(file.FullPath), Constants.UserDataBundleFileName, StringComparison.Ordinal)); // Retrieve the list of local files. var localUserDataFolder = await CoreHelper.GetOrCreateUserDataStorageFolderAsync().ConfigureAwait(false); StorageFile localUserDataBundleFile = await localUserDataFolder.TryGetItemAsync(Constants.UserDataBundleFileName) as StorageFile; IEnumerable <StorageFile> allOtherLocalFiles = (await localUserDataFolder.GetFilesAsync()) .Where(file => !string.Equals(file.Name, Constants.UserDataBundleFileName, StringComparison.Ordinal)); if (localUserDataBundleFile == null && roamingUserDataBundleFile == RemoteFileInfo.Empty) { // Nothing locally and remotely? succeeded = true; return; } if (localUserDataBundleFile == null || (roamingUserDataBundleFile != RemoteFileInfo.Empty && roamingUserDataBundleFile.CreatedDateTime.ToUniversalTime() > (await localUserDataBundleFile.GetBasicPropertiesAsync()).DateModified.ToUniversalTime())) { // If there is no local user data file, or that the file on the server is more recent than the local one, // then we want to merge by taking the version from the server. await DownloadRoamingDataFromServerAsync( remoteStorageProvider, allOtherRoamingFiles, allOtherLocalFiles, cancellationToken).ConfigureAwait(false); // The local file changed, since we downloaded the one from the server, so let's indicate // that we want to reload (and merge) the local data. requiresReloadLocalData = true; } else { // Else, then it means the local file is more recent than the one on the server, // or that there is simply no file on the server, // so we want to merge by taking the version from the local file. await UploadLocalDataToServerAsync( remoteStorageProvider, localUserDataBundleFile, allOtherRoamingFiles, allOtherLocalFiles, cancellationToken).ConfigureAwait(false); } succeeded = true; } catch (OperationCanceledException) { _logger.LogEvent(SynchronizeCanceledEvent, "The synchronization with the cloud has been canceled."); } catch (Exception ex) { _logger.LogFault(SynchronizeFaultEvent, "Failed to synchronize the data with the cloud.", ex); } finally { if (synchronizationStarted) { RaiseSynchronizationCompleted(succeeded, requiresReloadLocalData); } } } }