問題
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が呼ばれる…。なので、一応入れてあるだけ。これがちゃんと動くとはちょっと思えないが…。
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にメソッド1つ追加。
/** 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して。
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以降では呼ばれないとかいうことがあった。
<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>