JUCE: AirDropでファイルを受けられるようにする

## 問題 ##
JUCEにはContentSharerというクラスがあり、テキストやファイルを他のデバイスにシェアできる機能があるのだが、受けることができない。これはアホなのでなんとかしたい。目標はAirDropでファイルを送って受けられるようにすること。送る側はContentSharerを使うだけなので割愛。

## 方法 ##
JUCEのソースをいじらないと流石にできなかった。iOS/Macのコードに手を入れる。このメソッドを追加。ring2 changeで括ってあるところが変更点。自信がないのでまどろっこしく書いているがもっと短く書いて良いと思う。

2022/3/6追記
openURLで[url startAccessingSecurityScopedResource]を呼ばないとJUCEに渡した後のURL::createInputStream()でfileHandleがNULLになるようになっていた。以前は無くても動いていたと思うんだけど…。尚、それを呼んだ後は[url stopAccessingSecurityScopedResource]を呼ぶ。

あと、iOS10からopenURL:がdeprecatedになって、openURL:options:completionHandler:を使うように？となったらしいのでそれも追記してみたけど、少なくとも標準のファイルアプリからアプリ固有のファイルを選んでアプリを起動するとopenURLが呼ばれる…。なので、一応入れてあるだけ。これがちゃんと動くとはちょっと思えないが…。

```modules/juce_gui_basics/native/juce_Windowing_ios.mm

JUCE_END_IGNORE_WARNINGS_GCC_LIKE

- (void) userNotificationCenter: (UNUserNotificationCenter*) center
        willPresentNotification: (UNNotification*) notification
          withCompletionHandler: (void (^)(UNNotificationPresentationOptions options)) completionHandler;
- (void) userNotificationCenter: (UNUserNotificationCenter*) center
 didReceiveNotificationResponse: (UNNotificationResponse*) response
          withCompletionHandler: (void(^)())completionHandler;

//  ring2 change-->
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options;
- (void)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *, id> *)options completionHandler:(void (^ __nullable)(BOOL success))completion;
//  <-- ring2 change

#endif

@end

@implementation JuceAppStartupDelegate

    NSObject* _pushNotificationsDelegate;

//  ring2 change-->
- (BOOL)application:(UIApplication *)app
            openURL:(NSURL *)url
            options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options
{
    [url startAccessingSecurityScopedResource];
    auto* juceApp = JUCEApplicationBase::getInstance();
    NSString* absoluteString = [url absoluteString];
    const char* cstr = [absoluteString UTF8String];
    URL juceUrl(cstr);
    BOOL result = juceApp->openUrl(juceUrl);
    [url stopAccessingSecurityScopedResource];
    return result;
}

- (void)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *, id> *)options completionHandler:(void (^ __nullable)(BOOL success))completion
{
    [url startAccessingSecurityScopedResource];
    auto* juceApp = JUCEApplicationBase::getInstance();
    NSString* absoluteString = [url absoluteString];
    const char* cstr = [absoluteString UTF8String];
    URL juceUrl(cstr);
    juceApp->openUrl(juceUrl);
    [url stopAccessingSecurityScopedResource];
    return;
}
//  <-- ring2 change

- (id) init
{
    self = [super init];
```

次に、ApplicationBaseにメソッド１つ追加。

```modules/juce_events/messages/juce_ApplicationBase.h

    /** This method is called when the application is being woken from background mode
        by the operating system.
    */
    virtual void resumed() = 0;

    //  ring2 change-->
    virtual bool openUrl(URL& url) { return false; }
    //  <--ring2 change

    /** If any unhandled exceptions make it through to the message dispatch loop, this
        callback will be triggered, in case you want to log them or do some other
        type of error-handling.

        If the type of exception is derived from the std::exception class, the pointer
        passed-in will be valid. If the exception is of unknown type, this pointer
        will be null.
    */
    virtual void unhandledException (const std::exception*,
                                     const String& sourceFilename,
                                     int lineNumber) = 0;



```

Projucerを使っている場合は、プロジェクトの設定でPreprocessor DefinitionsにJUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP=1を書いて、modules/juce_audio_plugin_client/Standalone/juce_StandaloneFilterApp.cppをコピーしたMyStandAloneFilterApp.cppのようなものをプロジェクトに追加し、openUrl()をオーバーライドすれば良い。プラグインのスタンドアロン版の場合はこんなふうにすればOK。自分のプラグインのクラスのヘッダをincludeして。

```Source/MyStandAloneApp.cpp
    bool openUrl(URL& url) override
    {
        StandalonePluginHolder* holder = StandalonePluginHolder::getInstance();
        jassert(holder != nullptr);
        IfwAudioProcessor* processor = (IfwAudioProcessor*)holder->processor.get();
        jassert(processor != nullptr);
        processor->openUrl(url);
        return false;
    }

```

Xcodeでファイルタイプの設定が必要なので、ProjucerのiOSのExporterでCustom Plistに下記のように書いておけば、Info.plistに書き込まれる。UTExportedTypeDeclarationsが適切に記述されていないとopenUrlが呼ばれない模様。記述が足りなかったりすると、iOS 12では呼ばれるがiOS 13以降では呼ばれないとかいうことがあった。

```CustomPlist
<plist>
    <key>UTExportedTypeDeclarations</key>
    <array>
        <dict>
            <key>UTTypeConformsTo</key>
            <array>
                <string>public.data</string> <-- public.xmlとかpublic.textとかデータに合わせて -->
            </array>
            <key>UTTypeDescription</key>
            <string>My Data</string>
            <key>UTTypeIconFile</key>
            <string>Icon.icns</string>
            <key>UTTypeIconFiles</key>
            <array/>
            <key>UTTypeIdentifier</key>
            <string>com.myurl.myapp.mydata</string>
            <key>UTTypeTagSpecification</key>
            <dict>
                <key>public.filename-extension</key>
                <array>
                    <string>mydata</string>
                </array>
            </dict>
        </dict>
    </array>

    <key>CFBundleDocumentTypes</key>
    <array>
      <dict>
        <key>CFBundleTypeExtensions</key>
        <array>
          <string>mydata</string>
        </array>
        <key>CFBundleTypeName</key>
        <string>My App</string>
        <key>CFBundleTypeRole</key>
        <string>Editor</string>
        <key>LSItemContentTypes</key>
        <array>
          <string>com.myurl.myapp.mydata</string>
        </array>
        <key>LSTypeIsPackage</key>
        <false/>
        <key>NSPersistentStoreTypeKey</key>
        <string>Binary</string>
      </dict>
    </array>

    <key>LSSupportsOpeningDocumentsInPlace</key>
    <true/>

  </dict>
</plist>


```