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>
This commit is contained in:
235
Assets/Plugins/iOS/NativeShareBridge.mm
Normal file
235
Assets/Plugins/iOS/NativeShareBridge.mm
Normal file
@@ -0,0 +1,235 @@
|
||||
#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);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user