hippogamesunity / PixelStudioHub

Pixel Studio public hub (wiki, issues)
108 stars 8 forks source link

Demo app for NativeFilePicker (iOS/Android) #90

Open hippogamesunity opened 2 years ago

hippogamesunity commented 2 years ago

Make a demo Unity app for iOS/Android with NativeFilePicker plugin imported. The app should have the following features:

yasirkula commented 2 years ago

For the first point, can you explain in what sort of UI you're planning to display the file sizes? If you are planning to display them in NativeFilePicker's file picker UI, I'd like to warn you that NativeFilePicker uses UIDocumentPickerViewController and AFAIK its native UI can't be customized.

Can you test UISupportsDocumentBrowser case as follows:

#import <Foundation/Foundation.h>

extern "C" int _PixelStudio_StartAccessFile( const char* filePath ) 
{
    NSString *filePathStr = [NSString stringWithUTF8String:filePath];
    if( ![[NSURL fileURLWithPath:filePathStr] startAccessingSecurityScopedResource] )
    {
        NSLog( @"Couldn't start accessing file: %@", filePathStr );
        return 0;
    }

    return 1;
}

extern "C" void _PixelStudio_StopAccessFile( const char* filePath ) 
{
    [[NSURL fileURLWithPath:[NSString stringWithUTF8String:filePath]] stopAccessingSecurityScopedResource];
}
#if !UNITY_EDITOR && UNITY_IOS
[System.Runtime.InteropServices.DllImport( "__Internal" )]
private static extern int _PixelStudio_StartAccessFile( string filePath );
[System.Runtime.InteropServices.DllImport( "__Internal" )]
private static extern void _PixelStudio_StopAccessFile( string filePath );
#endif
#if !UNITY_EDITOR && UNITY_IOS
if( _PixelStudio_StartAccessFile( filePath ) == 0 )
{
    // Couldn't grant access to the file, reading it will probably fail
}
#endif
#if !UNITY_EDITOR && UNITY_IOS
_PixelStudio_StopAccessFile( filePath );
#endif
hippogamesunity commented 2 years ago

Thanks for replying! It doesn't matter, you can just print this to Console. It's just to confirm that you can access and read files by all possible input URLs.

It would be perfect to get a working demo project with NativeColorPicker that works as expected. There are some blank spaces in the asset.

For example, NativeColorPicker have a feature to declare custom types, but they don't work without some manual fixes in info.plist (need to add CFBundleDocumentTypes and UTExportedTypeDeclarations as orangeagain suggested here https://github.com/yasirkula/UnityNativeFilePicker/issues/7).

yasirkula commented 2 years ago

I can't create a demo project, I don't have iOS hardware to test it on. I can only give instructions.

hippogamesunity commented 2 years ago

Ah, I understand. I'll try your suggestions and will reply soon. Thanks.

hippogamesunity commented 2 years ago

I'm trying to open a PSP file from Files app using "Open with" feature. The file is located outside of Pixel Studio persistentDataPath.

Application.absoluteURL=file:///private/var/mobile/Containers/Shared/AppGroup/867471D1-2921-42A5-B986-A6247986BEBA/File%20Provider%20Storage/256_2.psp

Assuming path="file:///private/var/mobile/Containers/Shared/AppGroup/867471D1-2921-42A5-B986-A6247986BEBA/File%20Provider%20Storage/256_2.psp" and passing this to _PixelStudio_StartAccessFile gives 0 and File.Exists returns False

Assuming path="/private/var/mobile/Containers/Shared/AppGroup/867471D1-2921-42A5-B986-A6247986BEBA/File Provider Storage/256_2.psp" and passing this to _PixelStudio_StartAccessFile gives 0 and File.Exists returns False

Assuming path="/var/mobile/Containers/Shared/AppGroup/867471D1-2921-42A5-B986-A6247986BEBA/File Provider Storage/256_2.psp" and passing this to _PixelStudio_StartAccessFile gives 0 and File.Exists returns False

At the same time I can open files located inside persistentDataPath, I just need to remove "file:///private/" from Application.absoluteURL plus Replace("%20", " ") to get a valid path.

yasirkula commented 2 years ago

Can you try the last 2 paths without removing %20s? 🤞

P.S. In addition, can you change the native function as follows and see what it prints for each path variant:

extern "C" int _PixelStudio_StartAccessFile( const char* filePath ) 
{
    NSString *filePathStr = [NSString stringWithUTF8String:filePath];

    if( [[NSFileManager defaultManager] isReadableFileAtPath:filePathStr] )
        NSLog( @"File was accessible before" );
    else
        NSLog( @"File wasn't accessible before" );

    if( ![[NSURL fileURLWithPath:filePathStr] startAccessingSecurityScopedResource] )
    {
        NSLog( @"Couldn't start accessing file: %@", filePathStr );

        if( [[NSFileManager defaultManager] isReadableFileAtPath:filePathStr] )
            NSLog( @"File is now accessible before" );
        else
            NSLog( @"File still isn't accessible before" );

        return 0;
    }

    if( [[NSFileManager defaultManager] isReadableFileAtPath:filePathStr] )
        NSLog( @"File is accessible" );
    else
        NSLog( @"File isn't accessible" );

    return 1;
}
hippogamesunity commented 2 years ago

Hello! I've tried this with no luck. Application.absoluteURL=file:///private/var/mobile/Containers/Shared/AppGroup/867471D1-2921-42A5-B986-A6247986BEBA/File%20Provider%20Storage/256_2.psp

_PixelStudio_StartAccessFile = 0

path=/private/var/mobile/Containers/Shared/AppGroup/867471D1-2921-42A5-B986-A6247986BEBA/File%20Provider%20Storage/256_2.psp

File.Exists=False

_PixelStudio_StartAccessFile = 0

path=var/mobile/Containers/Shared/AppGroup/867471D1-2921-42A5-B986-A6247986BEBA/File%20Provider%20Storage/256_2.psp

File.Exists=False

Here is my code:

Снимок экрана 2021-10-30 в 12 56 29
yasirkula commented 2 years ago

Which NSLogs are printed to Xcode console?

hippogamesunity commented 2 years ago

I've noticed that the second path starts with "var" instead of "/var". Checking it again...

hippogamesunity commented 2 years ago

2021-10-30 13:24:05.122074+0300 PixelStudio[9337:1007047] [connection] nw_read_request_report [C1] Receive failed with error "Software caused connection abort" 2021-10-30 13:24:05.126054+0300 PixelStudio[9337:1007047] [connection] nw_read_request_report [C3] Receive failed with error "Software caused connection abort" 2021-10-30 13:24:05.127445+0300 PixelStudio[9337:1007047] [connection] nw_read_request_report [C4] Receive failed with error "Software caused connection abort" -> applicationWillEnterForeground() -> applicationDidBecomeActive() OpenByPath: Application.absoluteURL=file:///private/var/mobile/Containers/Shared/AppGroup/867471D1-2921-42A5-B986-A6247986BEBA/File%20Provider%20Storage/256_2.psp Assets.Scripts.Engine:OpenByPath() Assets.Scripts.Engine:OnApplicationPause(Boolean)

2021-10-30 13:24:05.603006+0300 PixelStudio[9337:1006852] File wasn't accessible before 2021-10-30 13:24:05.603081+0300 PixelStudio[9337:1006852] Couldn't start accessing file: /private/var/mobile/Containers/Shared/AppGroup/867471D1-2921-42A5-B986-A6247986BEBA/File%20Provider%20Storage/256_2.psp 2021-10-30 13:24:05.603127+0300 PixelStudio[9337:1006852] File still isn't accessible before _PixelStudio_StartAccessFile = 0 Assets.Scripts.Engine:OpenByPath() Assets.Scripts.Engine:OnApplicationPause(Boolean)

path=/private/var/mobile/Containers/Shared/AppGroup/867471D1-2921-42A5-B986-A6247986BEBA/File%20Provider%20Storage/256_2.psp Assets.Scripts.Engine:OpenByPath() Assets.Scripts.Engine:OnApplicationPause(Boolean)

File.Exists=False Assets.Scripts.Engine:OpenByPath() Assets.Scripts.Engine:OnApplicationPause(Boolean)

2021-10-30 13:24:05.605561+0300 PixelStudio[9337:1006852] File wasn't accessible before 2021-10-30 13:24:05.605616+0300 PixelStudio[9337:1006852] Couldn't start accessing file: /var/mobile/Containers/Shared/AppGroup/867471D1-2921-42A5-B986-A6247986BEBA/File%20Provider%20Storage/256_2.psp 2021-10-30 13:24:05.605660+0300 PixelStudio[9337:1006852] File still isn't accessible before _PixelStudio_StartAccessFile = 0 Assets.Scripts.Engine:OpenByPath() Assets.Scripts.Engine:OnApplicationPause(Boolean)

path=/var/mobile/Containers/Shared/AppGroup/867471D1-2921-42A5-B986-A6247986BEBA/File%20Provider%20Storage/256_2.psp Assets.Scripts.Engine:OpenByPath() Assets.Scripts.Engine:OnApplicationPause(Boolean)

File.Exists=False Assets.Scripts.Engine:OpenByPath() Assets.Scripts.Engine:OnApplicationPause(Boolean)

2021-10-30 13:24:06.089745+0300 PixelStudio[9337:1008496] [connection] nw_endpoint_handler_set_adaptive_read_handler [C8.1 87.250.250.207:443 ready channel-flow (satisfied (Path is satisfied), viable, interface: en0, ipv4, dns)] unregister notification for read_timeout failed 2021-10-30 13:24:06.089865+0300 PixelStudio[9337:1008496] [connection] nw_endpoint_handler_set_adaptive_write_handler [C8.1 87.250.250.207:443 ready channel-flow (satisfied (Path is satisfied), viable, interface: en0, ipv4, dns)] unregister notification for write_timeout failed 2021-10-30 13:24:06.109387+0300 PixelStudio[9337:1007047] [connection] nw_endpoint_handler_set_adaptive_read_handler [C9.1 35.241.52.229:443 ready channel-flow (satisfied (Path is satisfied), viable, interface: en0, ipv4, dns)] unregister notification for read_timeout failed 2021-10-30 13:24:06.109432+0300 PixelStudio[9337:1007047] [connection] nw_endpoint_handler_set_adaptive_write_handler [C9.1 35.241.52.229:443 ready channel-flow (satisfied (Path is satisfied), viable, interface: en0, ipv4, dns)] unregister notification for write_timeout failed 2021-10-30 13:24:06.154683+0300 PixelStudio[9337:1007036] [YandexMobileMetrica] Client event is sent: AppStart. (apiKey: c076437d-xxxx-xxxx-xxxx-xxxxxxxx1efc). 2021-10-30 13:24:06.154715+0300 PixelStudio[9337:1007036] [YandexMobileMetrica] Profile event is sent. (apiKey: c076437d-xxxx-xxxx-xxxx-xxxxxxxx1efc). 2021-10-30 13:24:06.154729+0300 PixelStudio[9337:1007036] [YandexMobileMetrica] Client event is sent: OpenWindow. (apiKey: c076437d-xxxx-xxxx-xxxx-xxxxxxxx1efc). 2021-10-30 13:24:06.154742+0300 PixelStudio[9337:1007036] [YandexMobileMetrica] Client event is sent: OpenWindow. (apiKey: c076437d-xxxx-xxxx-xxxx-xxxxxxxx1efc). 2021-10-30 13:24:06.154755+0300 PixelStudio[9337:1007036] [YandexMobileMetrica] Client event is sent: OpenWindow. (apiKey: c076437d-xxxx-xxxx-xxxx-xxxxxxxx1efc). 2021-10-30 13:24:06.155747+0300 PixelStudio[9337:1007036] [YandexMobileMetrica] Client event is removed from db: AppStart. (apiKey: c076437d-xxxx-xxxx-xxxx-xxxxxxxx1efc). 2021-10-30 13:24:06.155766+0300 PixelStudio[9337:1007036] [YandexMobileMetrica] Profile event is removed from db. (apiKey: c076437d-xxxx-xxxx-xxxx-xxxxxxxx1efc). 2021-10-30 13:24:06.155782+0300 PixelStudio[9337:1007036] [YandexMobileMetrica] Client event is removed from db: OpenWindow. (apiKey: c076437d-xxxx-xxxx-xxxx-xxxxxxxx1efc). 2021-10-30 13:24:06.155794+0300 PixelStudio[9337:1007036] [YandexMobileMetrica] Client event is removed from db: OpenWindow. (apiKey: c076437d-xxxx-xxxx-xxxx-xxxxxxxx1efc). 2021-10-30 13:24:06.155806+0300 PixelStudio[9337:1007036] [YandexMobileMetrica] Client event is removed from db: OpenWindow. (apiKey: c076437d-xxxx-xxxx-xxxx-xxxxxxxx1efc).

hippogamesunity commented 2 years ago

Please note that application.persistentpath is like /var/mobile/Containers/Data/Application/GUID, but the path I can't access has "/Shared/AppGroup/" insted of "/Data/Application/" (which is accessible).

yasirkula commented 2 years ago

None of the solutions I've initially suggested here seem to work for these paths.

When I google "Containers/Shared/AppGroup", I usually see answers mentioning "enabling App Groups capability". I have no idea what it is or if it's related to this issue, though 😄

yasirkula commented 2 years ago

I think I've found the reason behind this issue: https://developer.apple.com/documentation/foundation/nsurl

Security-Scoped URLs and String Paths

In a macOS app, when you copy a security-scoped URL, the copy has the security scope of the original. You gain access to the file-system resource (that the URL points to) just as you would with the original URL: by calling the startAccessingSecurityScopedResource method (or its Core Foundation equivalent).

If you need a security-scoped URL’s path as a string value (as provided by the path method), such as to provide to an API that requires a string value, obtain the path from the URL as needed. Note, however, that a string-based path obtained from a security-scoped URL does not have security scope and you cannot use that string to obtain access to a security-scoped resource.

Unity returns us the file path as a string and the security scope is lost in the process. You need the original security-scoped NSURL which I'm skeptical that Unity provides. Retrieving that NSURL will probably require modifying Unity's own native classes like AppDelegate and it's beyond my expertise. Your best bet would be to contact Unity team for support in this case.

Possibly related reading: https://developer.apple.com/documentation/uikit/view_controllers/adding_a_document_browser_to_your_app/setting_up_a_document_browser_app

One trick you can try beforehand is, setting LSSupportsOpeningDocumentsInPlace to false in Info.plist. I don't know if it will affect your files' visibility in Files app.

Unrelated but, you've previously mentioned iCloud support which might be achievable by setting UIFileSharingEnabled to true in Info.plist.

hippogamesunity commented 2 years ago

Many thanks for your suggestions. I'll try them.

hippogamesunity commented 2 years ago

Found some related info: https://stackoverflow.com/a/57632194/5513493 "startAccessingSecurityScopedResource" is also mentioned there.

yasirkula commented 2 years ago

I'm assuming disabling "Supports opening documents in place" didn't work for you.

hippogamesunity commented 2 years ago

Yes, LSSupportsOpeningDocumentsInPlace=NO doesn't work if UISupportsDocumentBrowser is enabled. Files are not copied to Inbox folders (that are always accessible for reading).

yasirkula commented 2 years ago

I see. As I said, startAccessingSecurityScopedResource is impossible without the original NSURL which Unity doesn't provide (AFAIK).

hippogamesunity commented 2 years ago

Are you sure Application.absoluteURL=file:///private/var/mobile/Containers/Shared/AppGroup/867471D1-2921-42A5-B986-A6247986BEBA/File%20Provider%20Storage/256_2.psp is not NSURL?

hippogamesunity commented 2 years ago

Ah, you may mean this image https://forum.unity.com/threads/5-4b11-ios-url-schemes.393478/#post-2583564

yasirkula commented 2 years ago

I'm not sure about this forum thread. They never mention NSURL.

Application.absoluteURL is definitely a string, Unity doesn't have an NSURL type. Think of NSURL as a data type that happens to have a string field as well as some fields that store security credentials (which are required for startAccessingSecurityScopedResource). Application.absoluteURL is only the string field of that data type. Since you only have the string and not the NSURL, you simply don't have access to the security credentials. You absolutely have to retrieve the original NSURL and it'll require modifying Unity's native source code in Xcode. There's no way around it to my knowledge but if there is, I'd like to know that, as well.

hippogamesunity commented 2 years ago

Ok, I'll create a thread on Unity forums and will let you know about further progress. Thanks for your time!

yasirkula commented 2 years ago

Thanks for letting me know about the progress! Please post the forum thread link here, as well.