121 lines
4.3 KiB
C#
121 lines
4.3 KiB
C#
using System.Diagnostics;
|
||
using System.IO;
|
||
using System.Text.Json;
|
||
using System.Windows.Threading;
|
||
|
||
namespace DeskCore;
|
||
|
||
public class UsageEntry
|
||
{
|
||
public int Count { get; set; }
|
||
public DateTime Last { get; set; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// Merkt sich, was wie oft und wann zuletzt über DeskCore gestartet wurde (für „Meistgenutzt“, „Zuletzt benutzt“
|
||
/// und die Schnellstart-Suche). Liegt getrennt von den Einstellungen in usage.json, weil es sich ständig ändert.
|
||
/// </summary>
|
||
public static class Usage
|
||
{
|
||
/// <summary>Schlüssel-Präfix für Start-Gruppen (die keinen Dateipfad haben).</summary>
|
||
public const string GroupPrefix = "gruppe:";
|
||
|
||
private static readonly string FilePath = Path.Combine(AppSettings.Folder, "usage.json");
|
||
private static readonly Dictionary<string, UsageEntry> _data = Load();
|
||
private static DispatcherTimer? _saveTimer;
|
||
|
||
/// <summary>Etwas wurde gestartet (z. B. damit „Zuletzt benutzt“-Boxen sich aktualisieren).</summary>
|
||
public static event Action? Changed;
|
||
|
||
private static Dictionary<string, UsageEntry> Load()
|
||
{
|
||
try
|
||
{
|
||
if (File.Exists(FilePath) &&
|
||
JsonSerializer.Deserialize<Dictionary<string, UsageEntry>>(File.ReadAllText(FilePath)) is { } d)
|
||
return new(d, StringComparer.OrdinalIgnoreCase);
|
||
}
|
||
catch { }
|
||
return new(StringComparer.OrdinalIgnoreCase);
|
||
}
|
||
|
||
/// <summary>Programm/Datei starten und den Start mitzählen. Wirft bei Fehlern wie Process.Start.</summary>
|
||
public static void Start(string path)
|
||
{
|
||
Process.Start(new ProcessStartInfo(path) { UseShellExecute = true });
|
||
Record(path);
|
||
}
|
||
|
||
public static void Record(string key)
|
||
{
|
||
if (!_data.TryGetValue(key, out var e)) _data[key] = e = new UsageEntry();
|
||
e.Count++;
|
||
e.Last = DateTime.Now;
|
||
ScheduleSave();
|
||
Changed?.Invoke();
|
||
}
|
||
|
||
public static int Count(string key) => _data.TryGetValue(key, out var e) ? e.Count : 0;
|
||
|
||
/// <summary>
|
||
/// Wie „beliebt“ etwas ist: Anzahl der Starts, mit der Zeit abnehmend gewichtet –
|
||
/// was du früher oft, aber seit Wochen nicht mehr benutzt hast, rutscht langsam nach hinten.
|
||
/// </summary>
|
||
public static double Score(string key)
|
||
{
|
||
if (!_data.TryGetValue(key, out var e)) return 0;
|
||
double days = Math.Max(0, (DateTime.Now - e.Last).TotalDays);
|
||
return e.Count / (1 + days / 30);
|
||
}
|
||
|
||
/// <summary>Zuletzt gestartete Dateien, die es noch gibt (neueste zuerst).</summary>
|
||
public static List<string> Recent(int max) => _data
|
||
.Where(kv => !kv.Key.StartsWith(GroupPrefix) && (File.Exists(kv.Key) || Directory.Exists(kv.Key)))
|
||
.OrderByDescending(kv => kv.Value.Last)
|
||
.Take(max)
|
||
.Select(kv => kv.Key)
|
||
.ToList();
|
||
|
||
/// <summary>Datei wurde umbenannt/verschoben → Zählung mitnehmen.</summary>
|
||
public static void Rename(string oldKey, string newKey)
|
||
{
|
||
if (!_data.Remove(oldKey, out var e)) return;
|
||
if (_data.TryGetValue(newKey, out var existing))
|
||
{
|
||
existing.Count += e.Count;
|
||
if (e.Last > existing.Last) existing.Last = e.Last;
|
||
}
|
||
else _data[newKey] = e;
|
||
ScheduleSave();
|
||
}
|
||
|
||
public static void Forget(string key)
|
||
{
|
||
if (_data.Remove(key)) { ScheduleSave(); Changed?.Invoke(); }
|
||
}
|
||
|
||
private static void ScheduleSave()
|
||
{
|
||
_saveTimer ??= new DispatcherTimer(TimeSpan.FromSeconds(2), DispatcherPriority.Background, (_, _) => Flush(),
|
||
Dispatcher.CurrentDispatcher);
|
||
_saveTimer.Stop();
|
||
_saveTimer.Start();
|
||
}
|
||
|
||
/// <summary>Sofort speichern (z. B. beim Beenden).</summary>
|
||
public static void Flush()
|
||
{
|
||
_saveTimer?.Stop();
|
||
try
|
||
{
|
||
// Nicht mehr vorhandene Einträge, die lange nicht benutzt wurden, aufräumen
|
||
foreach (var key in _data.Where(kv => kv.Value.Last < DateTime.Now.AddDays(-180) && !kv.Key.StartsWith(GroupPrefix)
|
||
&& !File.Exists(kv.Key) && !Directory.Exists(kv.Key)).Select(kv => kv.Key).ToList())
|
||
_data.Remove(key);
|
||
Directory.CreateDirectory(AppSettings.Folder);
|
||
File.WriteAllText(FilePath, JsonSerializer.Serialize(_data));
|
||
}
|
||
catch { }
|
||
}
|
||
}
|