mirror of
https://github.com/wismna/ModernKeePass.git
synced 2025-10-03 15:40:18 -04:00
WIP open a database
This commit is contained in:
269
ModernKeePass/Common/SuspensionManager.cs
Normal file
269
ModernKeePass/Common/SuspensionManager.cs
Normal file
@@ -0,0 +1,269 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.ApplicationModel;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.Streams;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace ModernKeePass.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// SuspensionManager captures global session state to simplify process lifetime management
|
||||
/// for an application. Note that session state will be automatically cleared under a variety
|
||||
/// of conditions and should only be used to store information that would be convenient to
|
||||
/// carry across sessions, but that should be discarded when an application crashes or is
|
||||
/// upgraded.
|
||||
/// </summary>
|
||||
internal sealed class SuspensionManager
|
||||
{
|
||||
private static Dictionary<string, object> _sessionState = new Dictionary<string, object>();
|
||||
private static List<Type> _knownTypes = new List<Type>();
|
||||
private const string sessionStateFilename = "_sessionState.xml";
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to global session state for the current session. This state is
|
||||
/// serialized by <see cref="SaveAsync"/> and restored by
|
||||
/// <see cref="RestoreAsync"/>, so values must be serializable by
|
||||
/// <see cref="DataContractSerializer"/> and should be as compact as possible. Strings
|
||||
/// and other self-contained data types are strongly recommended.
|
||||
/// </summary>
|
||||
public static Dictionary<string, object> SessionState
|
||||
{
|
||||
get { return _sessionState; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List of custom types provided to the <see cref="DataContractSerializer"/> when
|
||||
/// reading and writing session state. Initially empty, additional types may be
|
||||
/// added to customize the serialization process.
|
||||
/// </summary>
|
||||
public static List<Type> KnownTypes
|
||||
{
|
||||
get { return _knownTypes; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save the current <see cref="SessionState"/>. Any <see cref="Frame"/> instances
|
||||
/// registered with <see cref="RegisterFrame"/> will also preserve their current
|
||||
/// navigation stack, which in turn gives their active <see cref="Page"/> an opportunity
|
||||
/// to save its state.
|
||||
/// </summary>
|
||||
/// <returns>An asynchronous task that reflects when session state has been saved.</returns>
|
||||
public static async Task SaveAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Save the navigation state for all registered frames
|
||||
foreach (var weakFrameReference in _registeredFrames)
|
||||
{
|
||||
Frame frame;
|
||||
if (weakFrameReference.TryGetTarget(out frame))
|
||||
{
|
||||
SaveFrameNavigationState(frame);
|
||||
}
|
||||
}
|
||||
|
||||
// Serialize the session state synchronously to avoid asynchronous access to shared
|
||||
// state
|
||||
MemoryStream sessionData = new MemoryStream();
|
||||
DataContractSerializer serializer = new DataContractSerializer(typeof(Dictionary<string, object>), _knownTypes);
|
||||
serializer.WriteObject(sessionData, _sessionState);
|
||||
|
||||
// Get an output stream for the SessionState file and write the state asynchronously
|
||||
StorageFile file = await ApplicationData.Current.LocalFolder.CreateFileAsync(sessionStateFilename, CreationCollisionOption.ReplaceExisting);
|
||||
using (Stream fileStream = await file.OpenStreamForWriteAsync())
|
||||
{
|
||||
sessionData.Seek(0, SeekOrigin.Begin);
|
||||
await sessionData.CopyToAsync(fileStream);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new SuspensionManagerException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restores previously saved <see cref="SessionState"/>. Any <see cref="Frame"/> instances
|
||||
/// registered with <see cref="RegisterFrame"/> will also restore their prior navigation
|
||||
/// state, which in turn gives their active <see cref="Page"/> an opportunity restore its
|
||||
/// state.
|
||||
/// </summary>
|
||||
/// <param name="sessionBaseKey">An optional key that identifies the type of session.
|
||||
/// This can be used to distinguish between multiple application launch scenarios.</param>
|
||||
/// <returns>An asynchronous task that reflects when session state has been read. The
|
||||
/// content of <see cref="SessionState"/> should not be relied upon until this task
|
||||
/// completes.</returns>
|
||||
public static async Task RestoreAsync(String sessionBaseKey = null)
|
||||
{
|
||||
_sessionState = new Dictionary<String, Object>();
|
||||
|
||||
try
|
||||
{
|
||||
// Get the input stream for the SessionState file
|
||||
StorageFile file = await ApplicationData.Current.LocalFolder.GetFileAsync(sessionStateFilename);
|
||||
using (IInputStream inStream = await file.OpenSequentialReadAsync())
|
||||
{
|
||||
// Deserialize the Session State
|
||||
DataContractSerializer serializer = new DataContractSerializer(typeof(Dictionary<string, object>), _knownTypes);
|
||||
_sessionState = (Dictionary<string, object>)serializer.ReadObject(inStream.AsStreamForRead());
|
||||
}
|
||||
|
||||
// Restore any registered frames to their saved state
|
||||
foreach (var weakFrameReference in _registeredFrames)
|
||||
{
|
||||
Frame frame;
|
||||
if (weakFrameReference.TryGetTarget(out frame) && (string)frame.GetValue(FrameSessionBaseKeyProperty) == sessionBaseKey)
|
||||
{
|
||||
frame.ClearValue(FrameSessionStateProperty);
|
||||
RestoreFrameNavigationState(frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new SuspensionManagerException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static DependencyProperty FrameSessionStateKeyProperty =
|
||||
DependencyProperty.RegisterAttached("_FrameSessionStateKey", typeof(String), typeof(SuspensionManager), null);
|
||||
private static DependencyProperty FrameSessionBaseKeyProperty =
|
||||
DependencyProperty.RegisterAttached("_FrameSessionBaseKeyParams", typeof(String), typeof(SuspensionManager), null);
|
||||
private static DependencyProperty FrameSessionStateProperty =
|
||||
DependencyProperty.RegisterAttached("_FrameSessionState", typeof(Dictionary<String, Object>), typeof(SuspensionManager), null);
|
||||
private static List<WeakReference<Frame>> _registeredFrames = new List<WeakReference<Frame>>();
|
||||
|
||||
/// <summary>
|
||||
/// Registers a <see cref="Frame"/> instance to allow its navigation history to be saved to
|
||||
/// and restored from <see cref="SessionState"/>. Frames should be registered once
|
||||
/// immediately after creation if they will participate in session state management. Upon
|
||||
/// registration if state has already been restored for the specified key
|
||||
/// the navigation history will immediately be restored. Subsequent invocations of
|
||||
/// <see cref="RestoreAsync"/> will also restore navigation history.
|
||||
/// </summary>
|
||||
/// <param name="frame">An instance whose navigation history should be managed by
|
||||
/// <see cref="SuspensionManager"/></param>
|
||||
/// <param name="sessionStateKey">A unique key into <see cref="SessionState"/> used to
|
||||
/// store navigation-related information.</param>
|
||||
/// <param name="sessionBaseKey">An optional key that identifies the type of session.
|
||||
/// This can be used to distinguish between multiple application launch scenarios.</param>
|
||||
public static void RegisterFrame(Frame frame, String sessionStateKey, String sessionBaseKey = null)
|
||||
{
|
||||
if (frame.GetValue(FrameSessionStateKeyProperty) != null)
|
||||
{
|
||||
throw new InvalidOperationException("Frames can only be registered to one session state key");
|
||||
}
|
||||
|
||||
if (frame.GetValue(FrameSessionStateProperty) != null)
|
||||
{
|
||||
throw new InvalidOperationException("Frames must be either be registered before accessing frame session state, or not registered at all");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(sessionBaseKey))
|
||||
{
|
||||
frame.SetValue(FrameSessionBaseKeyProperty, sessionBaseKey);
|
||||
sessionStateKey = sessionBaseKey + "_" + sessionStateKey;
|
||||
}
|
||||
|
||||
// Use a dependency property to associate the session key with a frame, and keep a list of frames whose
|
||||
// navigation state should be managed
|
||||
frame.SetValue(FrameSessionStateKeyProperty, sessionStateKey);
|
||||
_registeredFrames.Add(new WeakReference<Frame>(frame));
|
||||
|
||||
// Check to see if navigation state can be restored
|
||||
RestoreFrameNavigationState(frame);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disassociates a <see cref="Frame"/> previously registered by <see cref="RegisterFrame"/>
|
||||
/// from <see cref="SessionState"/>. Any navigation state previously captured will be
|
||||
/// removed.
|
||||
/// </summary>
|
||||
/// <param name="frame">An instance whose navigation history should no longer be
|
||||
/// managed.</param>
|
||||
public static void UnregisterFrame(Frame frame)
|
||||
{
|
||||
// Remove session state and remove the frame from the list of frames whose navigation
|
||||
// state will be saved (along with any weak references that are no longer reachable)
|
||||
SessionState.Remove((String)frame.GetValue(FrameSessionStateKeyProperty));
|
||||
_registeredFrames.RemoveAll((weakFrameReference) =>
|
||||
{
|
||||
Frame testFrame;
|
||||
return !weakFrameReference.TryGetTarget(out testFrame) || testFrame == frame;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides storage for session state associated with the specified <see cref="Frame"/>.
|
||||
/// Frames that have been previously registered with <see cref="RegisterFrame"/> have
|
||||
/// their session state saved and restored automatically as a part of the global
|
||||
/// <see cref="SessionState"/>. Frames that are not registered have transient state
|
||||
/// that can still be useful when restoring pages that have been discarded from the
|
||||
/// navigation cache.
|
||||
/// </summary>
|
||||
/// <remarks>Apps may choose to rely on <see cref="NavigationHelper"/> to manage
|
||||
/// page-specific state instead of working with frame session state directly.</remarks>
|
||||
/// <param name="frame">The instance for which session state is desired.</param>
|
||||
/// <returns>A collection of state subject to the same serialization mechanism as
|
||||
/// <see cref="SessionState"/>.</returns>
|
||||
public static Dictionary<String, Object> SessionStateForFrame(Frame frame)
|
||||
{
|
||||
var frameState = (Dictionary<String, Object>)frame.GetValue(FrameSessionStateProperty);
|
||||
|
||||
if (frameState == null)
|
||||
{
|
||||
var frameSessionKey = (String)frame.GetValue(FrameSessionStateKeyProperty);
|
||||
if (frameSessionKey != null)
|
||||
{
|
||||
// Registered frames reflect the corresponding session state
|
||||
if (!_sessionState.ContainsKey(frameSessionKey))
|
||||
{
|
||||
_sessionState[frameSessionKey] = new Dictionary<String, Object>();
|
||||
}
|
||||
frameState = (Dictionary<String, Object>)_sessionState[frameSessionKey];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Frames that aren't registered have transient state
|
||||
frameState = new Dictionary<String, Object>();
|
||||
}
|
||||
frame.SetValue(FrameSessionStateProperty, frameState);
|
||||
}
|
||||
return frameState;
|
||||
}
|
||||
|
||||
private static void RestoreFrameNavigationState(Frame frame)
|
||||
{
|
||||
var frameState = SessionStateForFrame(frame);
|
||||
if (frameState.ContainsKey("Navigation"))
|
||||
{
|
||||
frame.SetNavigationState((String)frameState["Navigation"]);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SaveFrameNavigationState(Frame frame)
|
||||
{
|
||||
var frameState = SessionStateForFrame(frame);
|
||||
frameState["Navigation"] = frame.GetNavigationState();
|
||||
}
|
||||
}
|
||||
public class SuspensionManagerException : Exception
|
||||
{
|
||||
public SuspensionManagerException()
|
||||
{
|
||||
}
|
||||
|
||||
public SuspensionManagerException(Exception e)
|
||||
: base("SuspensionManager failed", e)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user