Singularity/Assets/Plugins/crosstales/FileBrowser/Scripts/Native/FileBrowserWSAImpl.cs
2024-05-06 11:45:45 -07:00

472 lines
14 KiB
C#

#if UNITY_WSA && !UNITY_EDITOR && ENABLE_WINMD_SUPPORT //|| CT_DEVELOP
using System;
using System.Collections.Generic;
using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.Storage.AccessCache;
using Windows.UI.ViewManagement;
using Enumerable = System.Linq.Enumerable;
using UnityEngine;
namespace Crosstales.FB
{
/// <summary>File browser for WSA.</summary>
public class FileBrowserWSAImpl
{
#region Variables
public static PickerLocationId CurrentLocation = PickerLocationId.ComputerFolder;
public static PickerViewMode CurrentViewMode = PickerViewMode.List;
public static StorageFolder LastOpenFolder;
public static StorageFile LastSaveFile;
private static readonly List<StorageFile> lastOpenFiles = new List<StorageFile>();
private static readonly List<StorageFile> lastGetFiles = new List<StorageFile>();
private static readonly List<StorageFolder> lastGetDirectories = new List<StorageFolder>();
private static readonly List<StorageFolder> lastGetDrives = new List<StorageFolder>();
private readonly List<string> selection = new List<string>();
private bool useExtentsion;
#endregion
#region Properties
/// <summary>Selected files or folders</summary>
/// <returns>Selected files or folders</returns>
public List<string> Selection => selection;
/// <summary>Last opened files</summary>
/// <returns>Last opened files</returns>
public static List<StorageFile> LastOpenFiles => lastOpenFiles;
/// <summary>Last opened file</summary>
/// <returns>Last opened file</returns>
public static StorageFile LastOpenFile => (lastOpenFiles.Count > 0) ? lastOpenFiles[0] : null;
/// <summary>Last searched files</summary>
/// <returns>Last searched files</returns>
public static List<StorageFile> LastGetFiles => lastGetFiles;
/// <summary>Last searched folders</summary>
/// <returns>Last searched folders</returns>
public static List<StorageFolder> LastGetDirectories => lastGetDirectories;
/// <summary>Last searched drives</summary>
/// <returns>Last searched drives</returns>
public static List<StorageFolder> LastGetDrives => lastGetDrives;
public static bool canOpenMultipleFiles => true;
public static bool canOpenMultipleFolders => false;
/// <summary>Indicates if the FB is currently busy.</summary>
/// <returns>True if the FB is currently busy</returns>
public bool isBusy { get; set; }
#endregion
#region Public Methods
public async void OpenFiles(List<Extension> extensions, bool multiselect)
{
if (EnsureUnsnapped())
{
//Debug.Log("OpenFiles...");
selection.Clear();
lastOpenFiles.Clear();
isBusy = true;
try
{
FileOpenPicker openPicker = new FileOpenPicker();
openPicker.ViewMode = CurrentViewMode;
openPicker.SuggestedStartLocation = CurrentLocation;
foreach (Extension extension in extensions)
{
foreach (string ext in extension.Extensions)
{
//Debug.Log( $"ext: {ext}");
openPicker.FileTypeFilter.Add(ext.StartsWith("*") ? ext : $".{ext}");
}
}
//openPicker.FileTypeFilter.Add(".jpg");
if (multiselect)
{
IReadOnlyList<StorageFile> files = await openPicker.PickMultipleFilesAsync();
if (files.Count > 0)
{
foreach (StorageFile file in files)
{
selection.Add(file.Path);
lastOpenFiles.Add(file);
}
}
}
else
{
StorageFile file = await openPicker.PickSingleFileAsync();
if (file != null)
{
selection.Add(file.Path);
lastOpenFiles.Add(file);
}
}
}
catch (Exception ex)
{
Debug.LogError(ex);
}
//Debug.Log($"OpenFiles end: {selection.Count}");
}
else
{
Debug.LogError("OpenFiles: could not unsnap!");
}
isBusy = false;
}
public async void OpenSingleFolder()
{
if (EnsureUnsnapped())
{
//Debug.Log("OpenSingleFolder...");
selection.Clear();
isBusy = true;
try
{
FolderPicker folderPicker = new FolderPicker();
folderPicker.ViewMode = CurrentViewMode;
folderPicker.SuggestedStartLocation = CurrentLocation;
folderPicker.FileTypeFilter.Add("*");
StorageFolder folder = await folderPicker.PickSingleFolderAsync();
if (folder != null)
{
selection.Add(folder.Path);
LastOpenFolder = folder;
StorageApplicationPermissions.FutureAccessList.AddOrReplace("PickedFolderToken", folder);
}
}
catch (Exception ex)
{
Debug.LogError(ex);
}
//Debug.Log($"OpenSingleFolder end: {selection.Count}");
}
else
{
Debug.LogError("OpenSingleFolder: could not unsnap!");
}
isBusy = false;
}
public async void SaveFile(string defaultName, List<Extension> extensions)
{
if (EnsureUnsnapped())
{
//Debug.Log( "SaveFile...");
selection.Clear();
isBusy = true;
try
{
FileSavePicker savePicker = new FileSavePicker();
savePicker.SuggestedStartLocation = CurrentLocation;
foreach (Extension extension in extensions)
{
List<string> exts = new List<string>();
foreach (string ext in extension.Extensions)
{
//Debug.Log( $"ext: {ext}");
if (ext.Equals("*"))
{
exts.Add(".");
}
else
{
exts.Add(ext.StartsWith("*") ? ext : $".{ext}");
}
}
savePicker.FileTypeChoices.Add(extension.Name, exts);
}
savePicker.SuggestedFileName = defaultName;
StorageFile file = await savePicker.PickSaveFileAsync();
if (file != null)
{
selection.Add(file.Path);
LastSaveFile = file;
}
}
catch (Exception ex)
{
Debug.LogError(ex);
}
//Debug.Log($"SaveFile end: {selection.Count}");
}
else
{
Debug.LogError("SaveFile: could not unsnap!");
}
isBusy = false;
}
public async void GetDrives()
{
//Debug.Log("GetDrives...");
selection.Clear();
lastGetDrives.Clear();
isBusy = true;
try
{
var removableDevices = KnownFolders.RemovableDevices;
var folders = await removableDevices.GetFoldersAsync();
foreach (var folder in folders)
{
selection.Add(folder.Path);
lastGetDrives.Add(folder);
}
}
catch (UnauthorizedAccessException)
{
Debug.LogError("Access is denied. Did you add the capability 'broadFileSystemAccess' to the 'package.appxmanifest'?");
}
catch (Exception ex)
{
Debug.LogError(ex);
}
//Debug.Log($"GetDrives end: {selection.Count}");
isBusy = false;
}
public async void GetDirectories(string path, bool isRecursive = false)
{
//Debug.Log("GetDirectories...");
selection.Clear();
lastGetDirectories.Clear();
isBusy = true;
try
{
StorageFolder folder = await StorageFolder.GetFolderFromPathAsync(path);
IReadOnlyList<StorageFolder> folders = await folder.GetFoldersAsync();
// Recurse sub-directories.
await getDirectories(folders, isRecursive, false);
}
catch (UnauthorizedAccessException)
{
Debug.LogError($"Access to the path '{path}' is denied. Did you add the capability 'broadFileSystemAccess' to the 'package.appxmanifest'?");
}
catch (Exception ex)
{
Debug.LogError(ex);
}
//Debug.Log($"GetDirectories end: {selection.Count}");
isBusy = false;
}
public async void GetFiles(string path, bool isRecursive = false, params string[] extensions)
{
useExtentsion = true;
await getFiles(path, isRecursive, extensions);
}
public async void GetFilesForName(string path, bool isRecursive = false, params string[] filenames)
{
useExtentsion = false;
await getFiles(path, isRecursive, filenames);
}
/*
void OpenFilesAsync(string title, string directory, ExtensionFilter[] extensions, bool multiselect, System.Action<string[]> cb);
void OpenFoldersAsync(string title, string directory, bool multiselect, System.Action<string[]> cb);
void SaveFileAsync(string title, string directory, string defaultName, ExtensionFilter[] extensions, System.Action<string> cb);
*/
#endregion
#region Private Methods
private async System.Threading.Tasks.Task getFiles(string path, bool isRecursive = false, params string[] extensions)
{
//Debug.Log("getFiles...");
selection.Clear();
lastGetDirectories.Clear();
lastGetFiles.Clear();
isBusy = true;
try
{
StorageFolder folder = await StorageFolder.GetFolderFromPathAsync(path);
IReadOnlyList<StorageFolder> folders = await folder.GetFoldersAsync();
// Recurse sub-directories.
await getDirectories(folders, isRecursive, true, extensions);
// Get the files in this folder.
IReadOnlyList<StorageFile> files = await folder.GetFilesAsync();
foreach (StorageFile file in files)
{
if (isValidFile(file, extensions))
{
//Debug.Log($"File: {file.Path}");
selection.Add(file.Path);
lastGetFiles.Add(file);
}
}
}
catch (UnauthorizedAccessException)
{
Debug.LogError($"Access to the path '{path}' is denied. Did you add the capability 'broadFileSystemAccess' to the 'package.appxmanifest'?");
}
catch (Exception ex)
{
Debug.LogError(ex);
}
//Debug.Log($"getFiles end: {selection.Count}");
isBusy = false;
}
private async System.Threading.Tasks.Task getDirectories(IReadOnlyList<StorageFolder> folders, bool isRecursive, bool addFiles, params string[] extensions)
{
foreach (StorageFolder folder in folders)
{
if (!addFiles)
{
//Debug.Log($"Folder: {folder.Path}");
selection.Add(folder.Path);
lastGetDirectories.Add(folder);
}
// Recurse this folder to get sub-folder info.
IReadOnlyList<StorageFolder> subDir = await folder.GetFoldersAsync();
if (subDir.Count > 0 && isRecursive)
await getDirectories(subDir, isRecursive, addFiles, extensions);
if (addFiles && isRecursive)
{
// Get the files in this folder.
IReadOnlyList<StorageFile> files = await folder.GetFilesAsync();
foreach (StorageFile file in files)
{
if (isValidFile(file, extensions))
{
//Debug.Log($"File: {file.Path}");
selection.Add(file.Path);
lastGetFiles.Add(file);
}
}
}
}
}
private bool isValidFile(StorageFile file, params string[] extensions)
{
if (extensions == null || extensions.Length == 0)
return true;
if (Enumerable.Any(extensions, extension => extension.Equals("*") || extension.Equals("*.*")))
return true;
string filename = file.Path;
return Enumerable.Any(extensions, extension => (useExtentsion && filename.CTEndsWith(extension)) || (!useExtentsion && filename.CTContains(extension)));
}
internal bool EnsureUnsnapped()
{
// FilePicker APIs will not work if the application is in a snapped state.
// If an app wants to show a FilePicker while snapped, it must attempt to unsnap first
// Note: enable for Windows 8.1
//bool unsnapped = ((ApplicationView.Value != ApplicationViewState.Snapped) || ApplicationView.TryUnsnap());
bool unsnapped = true;
return unsnapped;
}
#endregion
}
public struct Extension
{
public string Name;
public string[] Extensions;
public Extension(string filterName, params string[] filterExtensions)
{
Name = filterName;
Extensions = filterExtensions;
}
public override string ToString()
{
System.Text.StringBuilder result = new System.Text.StringBuilder();
result.Append(GetType().Name);
result.Append(" {");
result.Append("Name='");
result.Append(Name);
result.Append("', ");
result.Append("Extensions='");
result.Append(Extensions.Length);
result.Append("'");
result.Append("}");
return result.ToString();
}
}
}
#endif
// © 2018-2023 crosstales LLC (https://www.crosstales.com)