Skip to content

Commit

Permalink
Code refactoring + added Privacy Policy
Browse files Browse the repository at this point in the history
  • Loading branch information
0x7c13 committed Dec 7, 2023
1 parent bf11623 commit 15e4184
Show file tree
Hide file tree
Showing 11 changed files with 99 additions and 81 deletions.
22 changes: 22 additions & 0 deletions PRIVACY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Privacy Policy
----------------

### Introduction
Our privacy policy will help you understand what information we collect by *[Notepads](https://github.com/0x7c13/Notepads)* app, how *[Notepads](https://github.com/0x7c13/Notepads)* app uses it, and what choices you have.
*[0x7c13](https://github.com/0x7c13)* with the help of the Github Notepads App community built the *[Notepads](https://github.com/0x7c13/Notepads)* app as a free app. This APP is provided by *[0x7c13](https://github.com/0x7c13)* at no cost and is intended for use as is.
If you choose to use this app, then you agree to the collection and use of information in relation with this policy. The data that we collect are used for providing and improving the app service. We will not use or share your information or usage data with anyone except as described in this Privacy Policy.

### Information Collection and Use
For a better experience while using this app, certain usage data and errors are collected for identifying issues or improving the user experience of the app. *[Visual Studio AppCenter](https://visualstudio.microsoft.com/app-center/)* analytics service is used in this app to collect basic usage data plus some minimum telemetry to help debug runtime errors. See thread [#334](https://github.com/0x7c13/Notepads/issues/334) for more details.
The app does use third party services that may collect information used to identify you.

### Data Security
We value your trust in providing us your Personal Information, thus we are striving to use commercially acceptable means of protecting it. But remember that no method of transmission over the internet, or method of electronic storage is 100% secure and reliable, and we cannot guarantee its absolute security.

### Changes to This Privacy Policy
We may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes. We will notify you of any changes by posting the new Privacy Policy on this page. These changes are effective immediately, after they are posted on this page.

### Contact Us
If you have any questions or suggestions about our Privacy Policy, do not hesitate to contact us.
Contact Information:
Email: [email protected]
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ I am using analytics service "AppCenter" to collect basic usage data plus some m

Feel free to review the source code or build your own version of Notepads since it is 100% open sourced.

You might notice that I work for Microsoft but Notepads is my personal project that I accomplish during free time (to empower every person and every organization on the planet to achieve more😃). I do not work for the Windows team, nor do I work for a Microsoft UX/App team. I am not expert on creating Windows apps either. I learned how to code UWP as soon as I started this project, so don’t put too much hope on me or treat it as a project sponsored by Microsoft.
#### More to read here: [[Privacy Policy](PRIVACY.md)]

TL;DR: You might notice that I work for Microsoft but Notepads is my personal project that I accomplish during free time (to empower every person and every organization on the planet to achieve more😃). I do not work for the Windows team, nor do I work for a Microsoft UX/App team. I am not expert on creating Windows apps either. I learned how to code UWP as soon as I started this project, so don’t put too much hope on me or treat it as a project sponsored by Microsoft.

## Contributing:

Expand Down
4 changes: 2 additions & 2 deletions src/Notepads.Controls/Notepads.Controls.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
Expand All @@ -11,7 +11,7 @@
<AssemblyName>Notepads.Controls</AssemblyName>
<DefaultLanguage>en-US</DefaultLanguage>
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
<TargetPlatformVersion Condition=" '$(TargetPlatformVersion)' == '' ">10.0.19041.0</TargetPlatformVersion>
<TargetPlatformVersion Condition=" '$(TargetPlatformVersion)' == '' ">10.0.22621.0</TargetPlatformVersion>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
<FileAlignment>512</FileAlignment>
Expand Down
19 changes: 12 additions & 7 deletions src/Notepads/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,11 @@ private async Task ActivateAsync(IActivatedEventArgs e)

try
{
var services = new Type[] { typeof(Crashes), typeof(Analytics) };
AppCenter.Start(AppCenterSecret, services);
if (!string.IsNullOrEmpty(AppCenterSecret))
{
var services = new Type[] { typeof(Crashes), typeof(Analytics) };
AppCenter.Start(AppCenterSecret, services);
}
}
catch (Exception ex)
{
Expand Down Expand Up @@ -217,10 +220,10 @@ void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
/// of memory still intact.
/// </summary>
/// <param name="sender">The source of the suspend request.</param>
/// <param name="e">Details about the suspend request.</param>
private void OnSuspending(object sender, SuspendingEventArgs e)
/// <param name="args">Details about the suspend request.</param>
private void OnSuspending(object sender, SuspendingEventArgs args)
{
var deferral = e.SuspendingOperation.GetDeferral();
var deferral = args.SuspendingOperation.GetDeferral();

try
{
Expand All @@ -232,8 +235,10 @@ private void OnSuspending(object sender, SuspendingEventArgs e)
{
// Best efforts
}

deferral.Complete();
finally
{
deferral.Complete();
}
}

// Occurs when an exception is not handled on the UI thread.
Expand Down
9 changes: 7 additions & 2 deletions src/Notepads/Controls/TextEditor/TextEditor.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ private set

private readonly int _fileStatusCheckerPollingRateInSec = 6;

private readonly double _fileStatusCheckerDelayInSec = 0.2;
private readonly double _fileStatusCheckerDelayInSec = 0.3;

private readonly SemaphoreSlim _fileStatusSemaphoreSlim = new SemaphoreSlim(1, 1);

Expand Down Expand Up @@ -392,6 +392,10 @@ await Task.Run(async () =>
{
// ignore
}
catch (ObjectDisposedException)
{
// ignore
}
catch (Exception ex)
{
LoggingService.LogError($"[{nameof(TextEditor)}] Failed to check status for file [{EditingFile?.Path}]: {ex.Message}");
Expand Down Expand Up @@ -426,7 +430,8 @@ private async Task CheckAndUpdateFileStatusAsync(CancellationToken cancellationT
}
else
{
newState = await FileSystemUtility.GetDateModifiedAsync(EditingFile) != LastSavedSnapshot.DateModifiedFileTime ?
long fileModifiedTime = await FileSystemUtility.GetDateModifiedAsync(EditingFile);
newState = fileModifiedTime != LastSavedSnapshot.DateModifiedFileTime ?
FileModificationState.Modified :
FileModificationState.Untouched;
}
Expand Down
47 changes: 26 additions & 21 deletions src/Notepads/Core/SessionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,28 +90,9 @@ public async Task<int> LoadLastSessionAsync()
LoggingService.LogError($"[{nameof(SessionManager)}] Failed to load last session metadata: {ex.Message}");
Analytics.TrackEvent("SessionManager_FailedToLoadLastSession", new Dictionary<string, string>() { { "Exception", ex.Message } });

// Last session data is corrupted, clear it first
await ClearSessionDataAsync();

// Rename all backup files to indicate they are corrupted with an extension of ".txt" to avoid being deleted
foreach (StorageFile backupFile in await SessionUtility.GetAllFilesInBackupFolderAsync(_backupFolderName))
{
if (backupFile.Name.Contains(".")) continue; // Skip files with extension

try
{
await backupFile.RenameAsync(backupFile.Name + "-Corrupted.txt");
}
catch (Exception)
{
// Best effort
}
}
await HandleMetaDataFileCorruptionAsync();

// TODO: Open backup folder and notify user with a special dialog
// await Launcher.LaunchFolderAsync(await SessionUtility.GetBackupFolderAsync(_backupFolderName));

return 0;
return 0; // Failed to load session data
}

IList<ITextEditor> recoveredEditor = new List<ITextEditor>();
Expand Down Expand Up @@ -148,6 +129,30 @@ public async Task<int> LoadLastSessionAsync()
return _sessionDataCache.Count;
}

private async Task HandleMetaDataFileCorruptionAsync()
{
// Last session data is corrupted, clear it first
await ClearSessionDataAsync();

// Rename all backup files to indicate they are corrupted with an extension of ".txt" to avoid being deleted
foreach (StorageFile backupFile in await SessionUtility.GetAllFilesInBackupFolderAsync(_backupFolderName))
{
if (backupFile.Name.Contains(".")) continue; // Skip files with extension

try
{
await backupFile.RenameAsync(backupFile.Name + "-Corrupted.txt");
}
catch (Exception)
{
// Best effort
}
}

// TODO: Open backup folder and notify user with a special dialog
// await Launcher.LaunchFolderAsync(await SessionUtility.GetBackupFolderAsync(_backupFolderName));
}

public async Task SaveSessionAsync(Action actionAfterSaving = null)
{
if (!IsBackupEnabled)
Expand Down
4 changes: 2 additions & 2 deletions src/Notepads/Notepads.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
Expand All @@ -11,7 +11,7 @@
<AssemblyName>Notepads</AssemblyName>
<DefaultLanguage>en-US</DefaultLanguage>
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
<TargetPlatformVersion Condition=" '$(TargetPlatformVersion)' == '' ">10.0.19041.0</TargetPlatformVersion>
<TargetPlatformVersion Condition=" '$(TargetPlatformVersion)' == '' ">10.0.22621.0</TargetPlatformVersion>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
<FileAlignment>512</FileAlignment>
Expand Down
1 change: 1 addition & 0 deletions src/Notepads/Services/LoggingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ private static async Task<bool> InitializeLogFileWriterBackgroundTaskAsync()
{
SemaphoreSlim.Release();
}

return false;
}

Expand Down
65 changes: 22 additions & 43 deletions src/Notepads/Utilities/FileSystemUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
using Windows.ApplicationModel.Resources;
using Windows.Storage;
using Windows.Storage.FileProperties;
using Windows.Storage.Provider;
using UtfUnknown;

public enum InvalidFilenameError
Expand All @@ -29,6 +28,12 @@ public enum InvalidFilenameError

public static class FileSystemUtility
{
// Retriable FileIO errors
private const Int32 ERROR_ACCESS_DENIED = unchecked((Int32)0x80070005);
private const Int32 ERROR_SHARING_VIOLATION = unchecked((Int32)0x80070020);
private const Int32 ERROR_UNABLE_TO_REMOVE_REPLACED = unchecked((Int32)0x80070497);
private const Int32 ERROR_FAIL = unchecked((Int32)0x80004005);

private static readonly ResourceLoader ResourceLoader = ResourceLoader.GetForCurrentView();

private const string WslRootPath = "\\\\wsl$\\";
Expand Down Expand Up @@ -555,24 +560,7 @@ private static Encoding FixUtf8Bom(Encoding encoding, byte[] bom)
/// </summary>
public static async Task WriteTextToFileAsync(StorageFile file, string text, Encoding encoding)
{
bool usedDeferUpdates = true;

try
{
// Prevent updates to the remote version of the file until we
// finish making changes and call CompleteUpdatesAsync.
CachedFileManager.DeferUpdates(file);
}
catch (Exception)
{
// If DeferUpdates fails, just ignore it and try to save the file anyway
usedDeferUpdates = false;
}

// Write to file
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

try
await ExecuteFileIOWithRetries(file, async () =>
{
if (IsFileReadOnly(file) || !await IsFileWritableAsync(file))
{
Expand All @@ -594,42 +582,32 @@ public static async Task WriteTextToFileAsync(StorageFile file, string text, Enc
stream.SetLength(stream.Position); // Truncate
}
}
}
finally
{
if (usedDeferUpdates)
{
// Let Windows know that we're finished changing the file so the
// other app can update the remote version of the file.
_ = await CachedFileManager.CompleteUpdatesAsync(file);
}
}
});
}

/// <summary>
/// Save text to a file with UTF-8 encoding using FileIO API with retries for retriable errors
/// </summary>
public static async Task WriteUtf8TextToFileWithRetriesAsync(StorageFile storageFile, string text, int maxRetryAttempts)
public static async Task WriteTextToFileAsync(StorageFile storageFile, string text)
{
// Retriable errors
const int ERROR_ACCESS_DENIED = unchecked((Int32)0x80070005);
const int ERROR_SHARING_VIOLATION = unchecked((Int32)0x80070020);
const int ERROR_UNABLE_TO_REMOVE_REPLACED = unchecked((Int32)0x80070497);
const int ERROR_FAIL = unchecked((Int32)0x80004005);
await ExecuteFileIOWithRetries(storageFile, async () => await FileIO.WriteTextAsync(storageFile, text));
}

bool usedDeferUpdates = true;
private static async Task ExecuteFileIOWithRetries(StorageFile file, Func<Task> action, int maxRetryAttempts = 5)
{
bool deferUpdatesUsed = true;
int retryAttempts = 0;

try
{
// Prevent updates to the remote version of the file until we
// finish making changes and call CompleteUpdatesAsync.
CachedFileManager.DeferUpdates(storageFile);
CachedFileManager.DeferUpdates(file);
}
catch (Exception)
{
// If DeferUpdates fails, just ignore it and try to save the file anyway
usedDeferUpdates = false;
deferUpdatesUsed = false;
}

try
Expand All @@ -639,7 +617,7 @@ public static async Task WriteUtf8TextToFileWithRetriesAsync(StorageFile storage
try
{
retryAttempts++;
await FileIO.WriteTextAsync(storageFile, text, Windows.Storage.Streams.UnicodeEncoding.Utf8);
await action.Invoke(); // Execute FileIO action
break;
}
catch (Exception ex) when ((ex.HResult == ERROR_ACCESS_DENIED) ||
Expand All @@ -660,15 +638,16 @@ public static async Task WriteUtf8TextToFileWithRetriesAsync(StorageFile storage
// File name, path and content are not included to respect/protect user privacy
Analytics.TrackEvent("SaveSerializedSessionMetaDataAsync_RetryAttempts", new Dictionary<string, string>()
{
{ "RetryAttempts", (retryAttempts - 1).ToString() }
{ "RetryAttempts", (retryAttempts - 1).ToString() },
{ "DeferUpdatesUsed" , deferUpdatesUsed.ToString() }
});
}
if (usedDeferUpdates)

if (deferUpdatesUsed)
{
// Let Windows know that we're finished changing the file so the
// other app can update the remote version of the file.
_ = await CachedFileManager.CompleteUpdatesAsync(storageFile);
_ = await CachedFileManager.CompleteUpdatesAsync(file);
}
}
}
Expand Down
3 changes: 1 addition & 2 deletions src/Notepads/Utilities/SessionUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ internal static class SessionUtility
{
private const string BackupFolderDefaultName = "BackupFiles";
private const string SessionMetaDataFileDefaultName = "NotepadsSessionData.json";
private const int SessionMetaDataFileWriteMaxRetryCount = 7;

private static readonly ConcurrentDictionary<INotepadsCore, ISessionManager> SessionManagers = new ConcurrentDictionary<INotepadsCore, ISessionManager>();

Expand Down Expand Up @@ -74,7 +73,7 @@ public static async Task SaveSerializedSessionMetaDataAsync(string serializedDat
{
StorageFolder localFolder = ApplicationData.Current.LocalFolder;
StorageFile metaDataFile = await FileSystemUtility.GetOrCreateFileAsync(localFolder, sessionMetaDataFileName);
await FileSystemUtility.WriteUtf8TextToFileWithRetriesAsync(metaDataFile, serializedData, SessionMetaDataFileWriteMaxRetryCount);
await FileSystemUtility.WriteTextToFileAsync(metaDataFile, serializedData);
}

public static async Task DeleteSerializedSessionMetaDataAsync(string sessionMetaDataFileName)
Expand Down
2 changes: 1 addition & 1 deletion src/Notepads/Views/Settings/AboutPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@
<LineBreak /> Copyright (c) 2020-2023 Jackie (Jiaqi) Liu.
</Paragraph>
<Paragraph Margin="0,10,0,10">
<Hyperlink NavigateUri="https://github.com/0x7c13/Notepads" >
<Hyperlink NavigateUri="https://github.com/0x7c13/Notepads/blob/master/PRIVACY.md" >
<Run x:Uid="/Settings/AboutPage_PrivacyStatementTitle"/>
</Hyperlink>
</Paragraph>
Expand Down

0 comments on commit 15e4184

Please sign in to comment.