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:
2026-06-27 12:56:15 +09:00
parent 7d4324481a
commit 1911637020
115 changed files with 25660 additions and 1 deletions

View File

@@ -0,0 +1,199 @@
package jp.yourname.unity.nativeshare;
import android.app.Activity;
import android.content.ClipData;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.MediaStore;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class NativeShareBridge
{
public static void shareImage(
final Activity activity,
final String filePath,
final String text,
final String subject
)
{
if (activity == null)
{
return;
}
activity.runOnUiThread(new Runnable()
{
@Override
public void run()
{
try
{
if (filePath == null || filePath.length() == 0)
{
return;
}
File file = new File(filePath);
if (!file.exists())
{
return;
}
Uri uri = NativeShareFileProvider.getUriForFile(activity, file);
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("image/png");
shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
if (text != null && text.length() > 0)
{
shareIntent.putExtra(Intent.EXTRA_TEXT, text);
}
if (subject != null && subject.length() > 0)
{
shareIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
}
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
shareIntent.setClipData(
ClipData.newUri(
activity.getContentResolver(),
file.getName(),
uri
)
);
String chooserTitle =
subject != null && subject.length() > 0
? subject
: "Share";
Intent chooser = Intent.createChooser(shareIntent, chooserTitle);
activity.startActivity(chooser);
}
catch (Exception e)
{
e.printStackTrace();
}
}
});
}
public static void saveImageToGallery(
final Activity activity,
final String filePath,
final String displayName
)
{
if (activity == null)
{
return;
}
activity.runOnUiThread(new Runnable()
{
@Override
public void run()
{
try
{
if (filePath == null || filePath.length() == 0)
{
return;
}
File file = new File(filePath);
if (!file.exists())
{
return;
}
String name =
displayName != null && displayName.length() > 0
? displayName
: file.getName();
ContentResolver resolver = activity.getContentResolver();
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, name);
values.put(MediaStore.Images.Media.MIME_TYPE, "image/png");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
{
values.put(
MediaStore.Images.Media.RELATIVE_PATH,
Environment.DIRECTORY_PICTURES
);
values.put(MediaStore.Images.Media.IS_PENDING, 1);
}
Uri uri = resolver.insert(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
values
);
if (uri == null)
{
return;
}
OutputStream out = null;
InputStream in = null;
try
{
out = resolver.openOutputStream(uri);
in = new FileInputStream(file);
byte[] buffer = new byte[8192];
int length;
while ((length = in.read(buffer)) > 0)
{
out.write(buffer, 0, length);
}
out.flush();
}
finally
{
if (in != null)
{
in.close();
}
if (out != null)
{
out.close();
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
{
values.clear();
values.put(MediaStore.Images.Media.IS_PENDING, 0);
resolver.update(uri, values, null, null);
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
});
}
}