411 lines
11 KiB
C#
411 lines
11 KiB
C#
using System;
|
|
using System.IO;
|
|
using UnityEngine;
|
|
using System.Threading.Tasks;
|
|
#if UNITY_ANDROID || UNITY_IOS
|
|
using NativeFilePickerNamespace;
|
|
#endif
|
|
|
|
public static class NativeFilePicker
|
|
{
|
|
public delegate void PermissionCallback( Permission permission );
|
|
public delegate void FilePickedCallback( string path );
|
|
public delegate void MultipleFilesPickedCallback( string[] paths );
|
|
public delegate void FilesExportedCallback( bool success );
|
|
|
|
public enum Permission { Denied = 0, Granted = 1, ShouldAsk = 2 };
|
|
|
|
#region Platform Specific Elements
|
|
#if !UNITY_EDITOR && UNITY_ANDROID
|
|
private static AndroidJavaClass m_ajc = null;
|
|
private static AndroidJavaClass AJC
|
|
{
|
|
get
|
|
{
|
|
if( m_ajc == null )
|
|
m_ajc = new AndroidJavaClass( "com.yasirkula.unity.NativeFilePicker" );
|
|
|
|
return m_ajc;
|
|
}
|
|
}
|
|
|
|
private static AndroidJavaObject m_context = null;
|
|
private static AndroidJavaObject Context
|
|
{
|
|
get
|
|
{
|
|
if( m_context == null )
|
|
{
|
|
using( AndroidJavaObject unityClass = new AndroidJavaClass( "com.unity3d.player.UnityPlayer" ) )
|
|
{
|
|
m_context = unityClass.GetStatic<AndroidJavaObject>( "currentActivity" );
|
|
}
|
|
}
|
|
|
|
return m_context;
|
|
}
|
|
}
|
|
#elif !UNITY_EDITOR && UNITY_IOS
|
|
[System.Runtime.InteropServices.DllImport( "__Internal" )]
|
|
private static extern int _NativeFilePicker_CanPickMultipleFiles();
|
|
|
|
[System.Runtime.InteropServices.DllImport( "__Internal" )]
|
|
private static extern string _NativeFilePicker_ConvertExtensionToUTI( string extension );
|
|
|
|
[System.Runtime.InteropServices.DllImport( "__Internal" )]
|
|
private static extern void _NativeFilePicker_PickFile( string[] UTIs, int UTIsCount );
|
|
|
|
[System.Runtime.InteropServices.DllImport( "__Internal" )]
|
|
private static extern void _NativeFilePicker_PickMultipleFiles( string[] UTIs, int UTIsCount );
|
|
|
|
[System.Runtime.InteropServices.DllImport( "__Internal" )]
|
|
private static extern void _NativeFilePicker_ExportFiles( string[] files, int filesCount );
|
|
#endif
|
|
#endregion
|
|
|
|
#if !UNITY_EDITOR && UNITY_ANDROID
|
|
private static string m_selectedFilePath = null;
|
|
private static string SelectedFilePath
|
|
{
|
|
get
|
|
{
|
|
if( m_selectedFilePath == null )
|
|
{
|
|
m_selectedFilePath = Path.Combine( Application.temporaryCachePath, "pickedFile" );
|
|
Directory.CreateDirectory( Application.temporaryCachePath );
|
|
}
|
|
|
|
return m_selectedFilePath;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#region Runtime Permissions
|
|
public static bool CheckPermission( bool readPermissionOnly = false )
|
|
{
|
|
#if !UNITY_EDITOR && UNITY_ANDROID
|
|
return AJC.CallStatic<int>( "CheckPermission", Context, readPermissionOnly ) == 1;
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
public static void RequestPermissionAsync( PermissionCallback callback, bool readPermissionOnly = false )
|
|
{
|
|
#if !UNITY_EDITOR && UNITY_ANDROID
|
|
FPPermissionCallbackAndroid nativeCallback = new( callback );
|
|
AJC.CallStatic( "RequestPermission", Context, nativeCallback, readPermissionOnly );
|
|
#else
|
|
callback( Permission.Granted );
|
|
#endif
|
|
}
|
|
|
|
public static Task<Permission> RequestPermissionAsync( bool readPermissionOnly = false )
|
|
{
|
|
TaskCompletionSource<Permission> tcs = new TaskCompletionSource<Permission>();
|
|
RequestPermissionAsync( ( permission ) => tcs.SetResult( permission ), readPermissionOnly );
|
|
return tcs.Task;
|
|
}
|
|
|
|
public static void OpenSettings()
|
|
{
|
|
#if !UNITY_EDITOR && UNITY_ANDROID
|
|
AJC.CallStatic( "OpenSettings", Context );
|
|
#endif
|
|
}
|
|
#endregion
|
|
|
|
#region Helper Functions
|
|
public static bool CanPickMultipleFiles()
|
|
{
|
|
#if !UNITY_EDITOR && UNITY_ANDROID
|
|
return AJC.CallStatic<bool>( "CanPickMultipleFiles" );
|
|
#elif !UNITY_EDITOR && UNITY_IOS
|
|
return _NativeFilePicker_CanPickMultipleFiles() == 1;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
public static bool CanExportFiles()
|
|
{
|
|
#if UNITY_EDITOR
|
|
return true;
|
|
#elif UNITY_ANDROID
|
|
return AJC.CallStatic<bool>( "CanExportFiles" );
|
|
#elif UNITY_IOS
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
public static bool CanExportMultipleFiles()
|
|
{
|
|
#if UNITY_EDITOR
|
|
return true;
|
|
#elif UNITY_ANDROID
|
|
return AJC.CallStatic<bool>( "CanExportMultipleFiles" );
|
|
#elif UNITY_IOS
|
|
return _NativeFilePicker_CanPickMultipleFiles() == 1;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
public static bool IsFilePickerBusy()
|
|
{
|
|
#if !UNITY_EDITOR && UNITY_IOS
|
|
return FPResultCallbackiOS.IsBusy;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
public static string ConvertExtensionToFileType( string extension )
|
|
{
|
|
if( string.IsNullOrEmpty( extension ) )
|
|
return null;
|
|
|
|
if( extension.IndexOf( '*' ) >= 0 )
|
|
{
|
|
// So many users try to do this that it's now necessary to throw an exception for this particular scenario
|
|
throw new ArgumentException( "See: https://github.com/yasirkula/UnityNativeFilePicker#faq" );
|
|
}
|
|
|
|
#if !UNITY_EDITOR && UNITY_ANDROID
|
|
return AJC.CallStatic<string>( "GetMimeTypeFromExtension", extension.ToLowerInvariant() );
|
|
#elif !UNITY_EDITOR && UNITY_IOS
|
|
return _NativeFilePicker_ConvertExtensionToUTI( extension.ToLowerInvariant() );
|
|
#else
|
|
return extension;
|
|
#endif
|
|
}
|
|
#endregion
|
|
|
|
#region Import Functions
|
|
public static void PickFile( FilePickedCallback callback, params string[] allowedFileTypes )
|
|
{
|
|
// If no file type is specified, allow all file types
|
|
if( allowedFileTypes == null || allowedFileTypes.Length == 0 )
|
|
{
|
|
#if UNITY_ANDROID
|
|
allowedFileTypes = new string[] { "*/*" };
|
|
#else
|
|
allowedFileTypes = new string[] { "public.item", "public.content" };
|
|
#endif
|
|
}
|
|
|
|
RequestPermissionAsync( ( permission ) =>
|
|
{
|
|
if( permission != Permission.Granted || IsFilePickerBusy() )
|
|
{
|
|
callback?.Invoke( null );
|
|
return;
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
// Accept Android and iOS UTIs when possible, for user's convenience
|
|
string[] editorFilters = new string[allowedFileTypes.Length * 2];
|
|
for( int i = 0; i < allowedFileTypes.Length; i++ )
|
|
{
|
|
if( allowedFileTypes[i].IndexOf( '*' ) >= 0 )
|
|
{
|
|
if( allowedFileTypes[i] == "image/*" )
|
|
{
|
|
editorFilters[i * 2] = "Image files";
|
|
editorFilters[i * 2 + 1] = "png,jpg,jpeg";
|
|
}
|
|
else if( allowedFileTypes[i] == "video/*" )
|
|
{
|
|
editorFilters[i * 2] = "Video files";
|
|
editorFilters[i * 2 + 1] = "mp4,mov,webm,avi";
|
|
}
|
|
else if( allowedFileTypes[i] == "audio/*" )
|
|
{
|
|
editorFilters[i * 2] = "Audio files";
|
|
editorFilters[i * 2 + 1] = "mp3,wav,aac,flac";
|
|
}
|
|
else
|
|
{
|
|
editorFilters[i * 2] = "All files";
|
|
editorFilters[i * 2 + 1] = "*";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
editorFilters[i * 2] = allowedFileTypes[i];
|
|
|
|
if( allowedFileTypes[i].IndexOf( '/' ) >= 0 ) // Android UTIs like 'image/png'
|
|
editorFilters[i * 2 + 1] = allowedFileTypes[i].Substring( allowedFileTypes[i].IndexOf( '/' ) + 1 );
|
|
else if( allowedFileTypes[i].StartsWith( "public." ) ) // iOS UTIs like 'public.png'
|
|
editorFilters[i * 2 + 1] = allowedFileTypes[i].Substring( 7 );
|
|
else if( allowedFileTypes[i].IndexOf( '.' ) == 0 ) // Extensions starting with period like '.png'
|
|
editorFilters[i * 2 + 1] = allowedFileTypes[i].Substring( 1 );
|
|
else
|
|
editorFilters[i * 2 + 1] = allowedFileTypes[i];
|
|
}
|
|
}
|
|
|
|
string pickedFile = UnityEditor.EditorUtility.OpenFilePanelWithFilters( "Select file", "", editorFilters );
|
|
|
|
if( callback != null )
|
|
callback( pickedFile != "" ? pickedFile : null );
|
|
#elif UNITY_ANDROID
|
|
AJC.CallStatic( "PickFiles", Context, new FPResultCallbackAndroid( callback, null, null ), false, SelectedFilePath, allowedFileTypes, "" );
|
|
#elif UNITY_IOS
|
|
FPResultCallbackiOS.Initialize( callback, null, null );
|
|
_NativeFilePicker_PickFile( allowedFileTypes, allowedFileTypes.Length );
|
|
#else
|
|
if( callback != null )
|
|
callback( null );
|
|
#endif
|
|
}, true );
|
|
}
|
|
|
|
public static void PickMultipleFiles( MultipleFilesPickedCallback callback, params string[] allowedFileTypes )
|
|
{
|
|
// If no file type is specified, allow all file types
|
|
if( allowedFileTypes == null || allowedFileTypes.Length == 0 )
|
|
{
|
|
#if UNITY_ANDROID
|
|
allowedFileTypes = new string[] { "*/*" };
|
|
#else
|
|
allowedFileTypes = new string[] { "public.item", "public.content" };
|
|
#endif
|
|
}
|
|
|
|
RequestPermissionAsync( ( permission ) =>
|
|
{
|
|
if( permission != Permission.Granted || IsFilePickerBusy() )
|
|
{
|
|
callback?.Invoke( null );
|
|
return;
|
|
}
|
|
|
|
if( CanPickMultipleFiles() )
|
|
{
|
|
#if !UNITY_EDITOR && UNITY_ANDROID
|
|
AJC.CallStatic( "PickFiles", Context, new FPResultCallbackAndroid( null, callback, null ), true, SelectedFilePath, allowedFileTypes, "" );
|
|
#elif !UNITY_EDITOR && UNITY_IOS
|
|
FPResultCallbackiOS.Initialize( null, callback, null );
|
|
_NativeFilePicker_PickMultipleFiles( allowedFileTypes, allowedFileTypes.Length );
|
|
#endif
|
|
}
|
|
else if( callback != null )
|
|
callback( null );
|
|
}, true );
|
|
}
|
|
#endregion
|
|
|
|
#region Export Functions
|
|
public static void ExportFile( string filePath, FilesExportedCallback callback = null )
|
|
{
|
|
if( string.IsNullOrEmpty( filePath ) )
|
|
throw new ArgumentException( "Parameter 'filePath' is null or empty!" );
|
|
|
|
RequestPermissionAsync( ( permission ) =>
|
|
{
|
|
if( permission != Permission.Granted || IsFilePickerBusy() )
|
|
{
|
|
callback?.Invoke( false );
|
|
return;
|
|
}
|
|
|
|
if( CanExportFiles() )
|
|
{
|
|
#if UNITY_EDITOR
|
|
string extension = Path.GetExtension( filePath );
|
|
if( extension == null )
|
|
extension = "";
|
|
else if( extension.IndexOf( '.' ) == 0 )
|
|
extension = extension.Substring( 1 );
|
|
|
|
string destination = UnityEditor.EditorUtility.SaveFilePanel( "Select destination", Path.GetDirectoryName( filePath ), Path.GetFileName( filePath ), extension );
|
|
if( string.IsNullOrEmpty( destination ) )
|
|
{
|
|
if( callback != null )
|
|
callback( false );
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
File.Copy( filePath, destination, true );
|
|
|
|
if( callback != null )
|
|
callback( true );
|
|
}
|
|
catch( Exception e )
|
|
{
|
|
Debug.LogException( e );
|
|
|
|
if( callback != null )
|
|
callback( false );
|
|
}
|
|
}
|
|
#elif UNITY_ANDROID
|
|
AJC.CallStatic( "ExportFiles", Context, new FPResultCallbackAndroid( null, null, callback ), new string[1] { filePath }, 1 );
|
|
#elif UNITY_IOS
|
|
FPResultCallbackiOS.Initialize( null, null, callback );
|
|
_NativeFilePicker_ExportFiles( new string[1] { filePath }, 1 );
|
|
#endif
|
|
}
|
|
else if( callback != null )
|
|
callback( false );
|
|
}, false );
|
|
}
|
|
|
|
public static void ExportMultipleFiles( string[] filePaths, FilesExportedCallback callback = null )
|
|
{
|
|
if( filePaths == null || filePaths.Length == 0 )
|
|
throw new ArgumentException( "Parameter 'filePaths' is null or empty!" );
|
|
|
|
RequestPermissionAsync( ( permission ) =>
|
|
{
|
|
if( permission != Permission.Granted || IsFilePickerBusy() )
|
|
{
|
|
callback?.Invoke( false );
|
|
return;
|
|
}
|
|
|
|
if( CanExportMultipleFiles() )
|
|
{
|
|
#if UNITY_EDITOR
|
|
string destination = UnityEditor.EditorUtility.OpenFolderPanel( "Select destination", Path.GetDirectoryName( filePaths[0] ), "" );
|
|
if( string.IsNullOrEmpty( destination ) )
|
|
{
|
|
if( callback != null )
|
|
callback( false );
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
for( int i = 0; i < filePaths.Length; i++ )
|
|
File.Copy( filePaths[i], Path.Combine( destination, Path.GetFileName( filePaths[i] ) ), true );
|
|
|
|
if( callback != null )
|
|
callback( true );
|
|
}
|
|
catch( Exception e )
|
|
{
|
|
Debug.LogException( e );
|
|
|
|
if( callback != null )
|
|
callback( false );
|
|
}
|
|
}
|
|
#elif UNITY_ANDROID
|
|
AJC.CallStatic( "ExportFiles", Context, new FPResultCallbackAndroid( null, null, callback ), filePaths, filePaths.Length );
|
|
#elif UNITY_IOS
|
|
FPResultCallbackiOS.Initialize( null, null, callback );
|
|
_NativeFilePicker_ExportFiles( filePaths, filePaths.Length );
|
|
#endif
|
|
}
|
|
else if( callback != null )
|
|
callback( false );
|
|
}, false );
|
|
}
|
|
#endregion
|
|
} |