Files
MobileCameraShare/Assets/Plugins/iOS/NativeShareBridge.mm
HIRO_MAC 1911637020 Add mobile camera capture, polaroid preview, gallery save & native share
- カメラ撮影 → プレビュー(ポラロイド風白縁)→ 保存 / ネイティブ共有
- iOS / Android のネイティブ実装(共有・ギャラリー保存・FileProvider)
- iOS は共有シートのプリウォームで初回表示を軽減
- iOS Info.plist 用途説明をビルド後処理で自動付与
- Android パッケージ名ガードを有効化(ビルド前チェック)
- README を整備、.gitignore に slnx / .vscode / .utmp を追加

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 12:56:15 +09:00

236 lines
6.2 KiB
Plaintext

#import <UIKit/UIKit.h>
static NSString* NativeShareMakeString(const char* cString)
{
if (cString == NULL)
{
return @"";
}
return [NSString stringWithUTF8String:cString];
}
static UIViewController* NativeShareGetTopViewController()
{
UIWindow* keyWindow = nil;
if (@available(iOS 13.0, *))
{
NSSet<UIScene*>* scenes = [UIApplication sharedApplication].connectedScenes;
for (UIScene* scene in scenes)
{
if (scene.activationState == UISceneActivationStateForegroundActive &&
[scene isKindOfClass:[UIWindowScene class]])
{
UIWindowScene* windowScene = (UIWindowScene*)scene;
for (UIWindow* window in windowScene.windows)
{
if (window.isKeyWindow)
{
keyWindow = window;
break;
}
}
}
if (keyWindow != nil)
{
break;
}
}
}
if (keyWindow == nil)
{
keyWindow = [UIApplication sharedApplication].keyWindow;
}
if (keyWindow == nil)
{
return nil;
}
UIViewController* controller = keyWindow.rootViewController;
while (controller.presentedViewController != nil)
{
controller = controller.presentedViewController;
}
return controller;
}
// プレビューを開いた時点で先に作っておく共有シート(初回の遅延対策)
static UIActivityViewController* gPrewarmedActivityVC = nil;
static NSString* gPrewarmedPath = nil;
static UIActivityViewController* NativeShareBuildActivityVC(
NSString* pathString,
NSString* textString,
NSString* subjectString
)
{
NSMutableArray* items = [NSMutableArray array];
UIImage* image = nil;
if (pathString.length > 0 &&
[[NSFileManager defaultManager] fileExistsAtPath:pathString])
{
image = [UIImage imageWithContentsOfFile:pathString];
}
if (image != nil)
{
// iOSで「画像を保存」を出すため、テキストを混ぜず UIImage だけを渡す。
[items addObject:image];
}
else
{
// 画像化できなかった場合だけテキストを共有する
if (textString.length > 0)
{
[items addObject:textString];
}
if (pathString.length > 0 &&
[[NSFileManager defaultManager] fileExistsAtPath:pathString])
{
[items addObject:[NSURL fileURLWithPath:pathString]];
}
}
if (items.count == 0)
{
return nil;
}
UIActivityViewController* vc =
[[UIActivityViewController alloc]
initWithActivityItems:items
applicationActivities:nil];
if (subjectString.length > 0)
{
[vc setValue:subjectString forKey:@"subject"];
}
return vc;
}
static void NativeSharePresent(UIActivityViewController* vc)
{
if (vc == nil)
{
return;
}
UIViewController* controller = NativeShareGetTopViewController();
if (controller == nil)
{
return;
}
UIPopoverPresentationController* popover = vc.popoverPresentationController;
if (popover != nil)
{
popover.sourceView = controller.view;
popover.sourceRect = CGRectMake(
controller.view.bounds.size.width * 0.5,
controller.view.bounds.size.height * 0.5,
1,
1
);
popover.permittedArrowDirections = 0;
}
[controller presentViewController:vc animated:YES completion:nil];
}
extern "C"
{
// 共有シートを先に生成しておく(プレビュー表示時に呼ぶ想定)。
void _NativeShareBridge_PrewarmShare(
const char* filePath,
const char* text,
const char* subject
)
{
NSString* pathString = NativeShareMakeString(filePath);
NSString* textString = NativeShareMakeString(text);
NSString* subjectString = NativeShareMakeString(subject);
dispatch_async(dispatch_get_main_queue(), ^
{
gPrewarmedActivityVC =
NativeShareBuildActivityVC(pathString, textString, subjectString);
gPrewarmedPath = pathString;
});
}
void _NativeShareBridge_ShareImage(
const char* filePath,
const char* text,
const char* subject
)
{
NSString* pathString = NativeShareMakeString(filePath);
NSString* textString = NativeShareMakeString(text);
NSString* subjectString = NativeShareMakeString(subject);
dispatch_async(dispatch_get_main_queue(), ^
{
UIActivityViewController* vc = nil;
// プレビューで先に作っておいたものがあれば再利用する
if (gPrewarmedActivityVC != nil &&
gPrewarmedPath != nil &&
[gPrewarmedPath isEqualToString:pathString])
{
vc = gPrewarmedActivityVC;
}
gPrewarmedActivityVC = nil;
gPrewarmedPath = nil;
if (vc == nil)
{
vc = NativeShareBuildActivityVC(pathString, textString, subjectString);
}
NativeSharePresent(vc);
});
}
void _NativeShareBridge_SaveImageToGallery(const char* filePath)
{
NSString* pathString = NativeShareMakeString(filePath);
dispatch_async(dispatch_get_main_queue(), ^
{
if (pathString.length == 0 ||
![[NSFileManager defaultManager] fileExistsAtPath:pathString])
{
NSLog(@"NativeShareBridge: 保存対象のファイルがありません: %@", pathString);
return;
}
UIImage* image = [UIImage imageWithContentsOfFile:pathString];
if (image == nil)
{
NSLog(@"NativeShareBridge: 画像を読み込めませんでした: %@", pathString);
return;
}
// カメラロールへ保存する。
// Info.plist に NSPhotoLibraryAddUsageDescription が必要。
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
});
}
}