Развитие системы растений
This commit is contained in:
1 parent
1b578067f3
commit
ba49ef5015
218 files changed
+70415
-1519
No files matched your search
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5fc918beb0f7cfe49a7f596d5b6b8768
|
||||
folderAsset: yes
|
||||
timeCreated: 1525098661
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,52 @@
|
||||
#if UNITY_EDITOR || UNITY_ANDROID
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NativeCameraNamespace
|
||||
{
|
||||
public class NCCallbackHelper : MonoBehaviour
|
||||
{
|
||||
private bool autoDestroyWithCallback;
|
||||
private Action mainThreadAction = null;
|
||||
|
||||
public static NCCallbackHelper Create( bool autoDestroyWithCallback )
|
||||
{
|
||||
NCCallbackHelper result = new GameObject( "NCCallbackHelper" ).AddComponent<NCCallbackHelper>();
|
||||
result.autoDestroyWithCallback = autoDestroyWithCallback;
|
||||
DontDestroyOnLoad( result.gameObject );
|
||||
return result;
|
||||
}
|
||||
|
||||
public void CallOnMainThread( Action function )
|
||||
{
|
||||
lock( this )
|
||||
{
|
||||
mainThreadAction += function;
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if( mainThreadAction != null )
|
||||
{
|
||||
try
|
||||
{
|
||||
Action temp;
|
||||
lock( this )
|
||||
{
|
||||
temp = mainThreadAction;
|
||||
mainThreadAction = null;
|
||||
}
|
||||
|
||||
temp();
|
||||
}
|
||||
finally
|
||||
{
|
||||
if( autoDestroyWithCallback )
|
||||
Destroy( gameObject );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b2bbe0051e738ea4585119c46d863f19
|
||||
timeCreated: 1545147258
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 117802
|
||||
packageName: Native Camera for Android & iOS
|
||||
packageVersion: 1.5.0
|
||||
assetPath: Assets/Plugins/NativeCamera/Android/NCCallbackHelper.cs
|
||||
uploadId: 746912
|
||||
@@ -0,0 +1,24 @@
|
||||
#if UNITY_EDITOR || UNITY_ANDROID
|
||||
using UnityEngine;
|
||||
|
||||
namespace NativeCameraNamespace
|
||||
{
|
||||
public class NCCameraCallbackAndroid : AndroidJavaProxy
|
||||
{
|
||||
private readonly NativeCamera.CameraCallback callback;
|
||||
private readonly NCCallbackHelper callbackHelper;
|
||||
|
||||
public NCCameraCallbackAndroid( NativeCamera.CameraCallback callback ) : base( "com.yasirkula.unity.NativeCameraMediaReceiver" )
|
||||
{
|
||||
this.callback = callback;
|
||||
callbackHelper = NCCallbackHelper.Create( true );
|
||||
}
|
||||
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
public void OnMediaReceived( string path )
|
||||
{
|
||||
callbackHelper.CallOnMainThread( () => callback( !string.IsNullOrEmpty( path ) ? path : null ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3cc8df584d2a4344b929a4f13a53723a
|
||||
timeCreated: 1519060539
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 117802
|
||||
packageName: Native Camera for Android & iOS
|
||||
packageVersion: 1.5.0
|
||||
assetPath: Assets/Plugins/NativeCamera/Android/NCCameraCallbackAndroid.cs
|
||||
uploadId: 746912
|
||||
@@ -0,0 +1,24 @@
|
||||
#if UNITY_EDITOR || UNITY_ANDROID
|
||||
using UnityEngine;
|
||||
|
||||
namespace NativeCameraNamespace
|
||||
{
|
||||
public class NCPermissionCallbackAndroid : AndroidJavaProxy
|
||||
{
|
||||
private readonly NativeCamera.PermissionCallback callback;
|
||||
private readonly NCCallbackHelper callbackHelper;
|
||||
|
||||
public NCPermissionCallbackAndroid( NativeCamera.PermissionCallback callback ) : base( "com.yasirkula.unity.NativeCameraPermissionReceiver" )
|
||||
{
|
||||
this.callback = callback;
|
||||
callbackHelper = NCCallbackHelper.Create( true );
|
||||
}
|
||||
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
public void OnPermissionResult( int result )
|
||||
{
|
||||
callbackHelper.CallOnMainThread( () => callback( (NativeCamera.Permission) result ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bafa24bbc8c455f44a2b98dcbe6451bd
|
||||
timeCreated: 1519060539
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 117802
|
||||
packageName: Native Camera for Android & iOS
|
||||
packageVersion: 1.5.0
|
||||
assetPath: Assets/Plugins/NativeCamera/Android/NCPermissionCallbackAndroid.cs
|
||||
uploadId: 746912
|
||||
Binary file not shown.
@@ -0,0 +1,40 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 284037eba2526f54d9cf51b5d9bffcfa
|
||||
timeCreated: 1569764737
|
||||
licenseType: Store
|
||||
PluginImporter:
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
platformData:
|
||||
data:
|
||||
first:
|
||||
Android: Android
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
data:
|
||||
first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
data:
|
||||
first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 117802
|
||||
packageName: Native Camera for Android & iOS
|
||||
packageVersion: 1.5.0
|
||||
assetPath: Assets/Plugins/NativeCamera/Android/NativeCamera.aar
|
||||
uploadId: 746912
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 16fe39fd709533a4eba946790a8e3123
|
||||
folderAsset: yes
|
||||
timeCreated: 1521452097
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,113 @@
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
#if UNITY_IOS
|
||||
using UnityEditor.Callbacks;
|
||||
using UnityEditor.iOS.Xcode;
|
||||
#endif
|
||||
|
||||
namespace NativeCameraNamespace
|
||||
{
|
||||
[System.Serializable]
|
||||
public class Settings
|
||||
{
|
||||
private const string SAVE_PATH = "ProjectSettings/NativeCamera.json";
|
||||
|
||||
public bool AutomatedSetup = true;
|
||||
public string CameraUsageDescription = "The app requires access to the camera to take pictures or record videos with it.";
|
||||
public string MicrophoneUsageDescription = "The app will capture microphone input in the recorded video.";
|
||||
|
||||
private static Settings m_instance = null;
|
||||
public static Settings Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if( m_instance == null )
|
||||
{
|
||||
try
|
||||
{
|
||||
if( File.Exists( SAVE_PATH ) )
|
||||
m_instance = JsonUtility.FromJson<Settings>( File.ReadAllText( SAVE_PATH ) );
|
||||
else
|
||||
m_instance = new Settings();
|
||||
}
|
||||
catch( System.Exception e )
|
||||
{
|
||||
Debug.LogException( e );
|
||||
m_instance = new Settings();
|
||||
}
|
||||
}
|
||||
|
||||
return m_instance;
|
||||
}
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
File.WriteAllText( SAVE_PATH, JsonUtility.ToJson( this, true ) );
|
||||
}
|
||||
|
||||
[SettingsProvider]
|
||||
public static SettingsProvider CreatePreferencesGUI()
|
||||
{
|
||||
return new SettingsProvider( "Project/yasirkula/Native Camera", SettingsScope.Project )
|
||||
{
|
||||
guiHandler = ( searchContext ) => PreferencesGUI(),
|
||||
keywords = new System.Collections.Generic.HashSet<string>() { "Native", "Camera", "Android", "iOS" }
|
||||
};
|
||||
}
|
||||
|
||||
public static void PreferencesGUI()
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
Instance.AutomatedSetup = EditorGUILayout.Toggle( "Automated Setup", Instance.AutomatedSetup );
|
||||
|
||||
EditorGUI.BeginDisabledGroup( !Instance.AutomatedSetup );
|
||||
Instance.CameraUsageDescription = EditorGUILayout.DelayedTextField( "Camera Usage Description", Instance.CameraUsageDescription );
|
||||
Instance.MicrophoneUsageDescription = EditorGUILayout.DelayedTextField( "Microphone Usage Description", Instance.MicrophoneUsageDescription );
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
if( EditorGUI.EndChangeCheck() )
|
||||
Instance.Save();
|
||||
}
|
||||
}
|
||||
|
||||
public class NCPostProcessBuild
|
||||
{
|
||||
#if UNITY_IOS
|
||||
[PostProcessBuild]
|
||||
public static void OnPostprocessBuild( BuildTarget target, string buildPath )
|
||||
{
|
||||
if( !Settings.Instance.AutomatedSetup )
|
||||
return;
|
||||
|
||||
if( target == BuildTarget.iOS )
|
||||
{
|
||||
string pbxProjectPath = PBXProject.GetPBXProjectPath( buildPath );
|
||||
string plistPath = Path.Combine( buildPath, "Info.plist" );
|
||||
|
||||
PBXProject pbxProject = new PBXProject();
|
||||
pbxProject.ReadFromFile( pbxProjectPath );
|
||||
|
||||
string targetGUID = pbxProject.GetUnityFrameworkTargetGuid();
|
||||
pbxProject.AddFrameworkToProject( targetGUID, "MobileCoreServices.framework", false );
|
||||
pbxProject.AddFrameworkToProject( targetGUID, "ImageIO.framework", false );
|
||||
|
||||
File.WriteAllText( pbxProjectPath, pbxProject.WriteToString() );
|
||||
|
||||
PlistDocument plist = new PlistDocument();
|
||||
plist.ReadFromString( File.ReadAllText( plistPath ) );
|
||||
|
||||
PlistElementDict rootDict = plist.root;
|
||||
if( !string.IsNullOrEmpty( Settings.Instance.CameraUsageDescription ) )
|
||||
rootDict.SetString( "NSCameraUsageDescription", Settings.Instance.CameraUsageDescription );
|
||||
if( !string.IsNullOrEmpty( Settings.Instance.MicrophoneUsageDescription ) )
|
||||
rootDict.SetString( "NSMicrophoneUsageDescription", Settings.Instance.MicrophoneUsageDescription );
|
||||
|
||||
File.WriteAllText( plistPath, plist.WriteToString() );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fa3b57e342928704cb910789ae4dde20
|
||||
timeCreated: 1521452119
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 117802
|
||||
packageName: Native Camera for Android & iOS
|
||||
packageVersion: 1.5.0
|
||||
assetPath: Assets/Plugins/NativeCamera/Editor/NCPostProcessBuild.cs
|
||||
uploadId: 746912
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "NativeCamera.Editor",
|
||||
"references": [],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 31117d0234af0084b91a7e53b3d9e0a3
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 117802
|
||||
packageName: Native Camera for Android & iOS
|
||||
packageVersion: 1.5.0
|
||||
assetPath: Assets/Plugins/NativeCamera/Editor/NativeCamera.Editor.asmdef
|
||||
uploadId: 746912
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"name": "NativeCamera.Runtime"
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b107fd1956cb3e04985108f5ee29e115
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 117802
|
||||
packageName: Native Camera for Android & iOS
|
||||
packageVersion: 1.5.0
|
||||
assetPath: Assets/Plugins/NativeCamera/NativeCamera.Runtime.asmdef
|
||||
uploadId: 746912
|
||||
@@ -0,0 +1,563 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine.Networking;
|
||||
#if UNITY_ANDROID || UNITY_IOS
|
||||
using NativeCameraNamespace;
|
||||
#endif
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
public static class NativeCamera
|
||||
{
|
||||
public struct ImageProperties
|
||||
{
|
||||
public readonly int width;
|
||||
public readonly int height;
|
||||
public readonly string mimeType;
|
||||
public readonly ImageOrientation orientation;
|
||||
|
||||
public ImageProperties( int width, int height, string mimeType, ImageOrientation orientation )
|
||||
{
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.mimeType = mimeType;
|
||||
this.orientation = orientation;
|
||||
}
|
||||
}
|
||||
|
||||
public struct VideoProperties
|
||||
{
|
||||
public readonly int width;
|
||||
public readonly int height;
|
||||
public readonly long duration;
|
||||
public readonly float rotation;
|
||||
|
||||
public VideoProperties( int width, int height, long duration, float rotation )
|
||||
{
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.duration = duration;
|
||||
this.rotation = rotation;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Permission { Denied = 0, Granted = 1, ShouldAsk = 2 };
|
||||
public enum Quality { Default = -1, Low = 0, Medium = 1, High = 2 };
|
||||
public enum PreferredCamera { Default = -1, Rear = 0, Front = 1 }
|
||||
|
||||
// EXIF orientation: http://sylvana.net/jpegcrop/exif_orientation.html (indices are reordered)
|
||||
public enum ImageOrientation { Unknown = -1, Normal = 0, Rotate90 = 1, Rotate180 = 2, Rotate270 = 3, FlipHorizontal = 4, Transpose = 5, FlipVertical = 6, Transverse = 7 };
|
||||
|
||||
public delegate void PermissionCallback( Permission permission );
|
||||
public delegate void CameraCallback( string path );
|
||||
|
||||
#region Platform Specific Elements
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
private static AndroidJavaClass m_ajc = null;
|
||||
private static AndroidJavaClass AJC
|
||||
{
|
||||
get
|
||||
{
|
||||
if( m_ajc == null )
|
||||
m_ajc = new AndroidJavaClass( "com.yasirkula.unity.NativeCamera" );
|
||||
|
||||
return m_ajc;
|
||||
}
|
||||
}
|
||||
|
||||
private static AndroidJavaObject m_context = null;
|
||||
private static AndroidJavaObject Context
|
||||
{
|
||||
get
|
||||
{
|
||||
if( m_context == null )
|
||||
{
|
||||
using( AndroidJavaObject unityClass = new AndroidJavaClass( "com.unity3d.player.UnityPlayer" ) )
|
||||
{
|
||||
m_context = unityClass.GetStatic<AndroidJavaObject>( "currentActivity" );
|
||||
}
|
||||
}
|
||||
|
||||
return m_context;
|
||||
}
|
||||
}
|
||||
#elif !UNITY_EDITOR && UNITY_IOS
|
||||
[System.Runtime.InteropServices.DllImport( "__Internal" )]
|
||||
private static extern int _NativeCamera_CheckPermission();
|
||||
|
||||
[System.Runtime.InteropServices.DllImport( "__Internal" )]
|
||||
private static extern void _NativeCamera_RequestPermission();
|
||||
|
||||
[System.Runtime.InteropServices.DllImport( "__Internal" )]
|
||||
private static extern void _NativeCamera_OpenSettings();
|
||||
|
||||
[System.Runtime.InteropServices.DllImport( "__Internal" )]
|
||||
private static extern int _NativeCamera_HasCamera();
|
||||
|
||||
[System.Runtime.InteropServices.DllImport( "__Internal" )]
|
||||
private static extern void _NativeCamera_TakePicture( string imageSavePath, int maxSize, int preferredCamera );
|
||||
|
||||
[System.Runtime.InteropServices.DllImport( "__Internal" )]
|
||||
private static extern void _NativeCamera_RecordVideo( int quality, int maxDuration, int preferredCamera );
|
||||
|
||||
[System.Runtime.InteropServices.DllImport( "__Internal" )]
|
||||
private static extern string _NativeCamera_GetImageProperties( string path );
|
||||
|
||||
[System.Runtime.InteropServices.DllImport( "__Internal" )]
|
||||
private static extern string _NativeCamera_GetVideoProperties( string path );
|
||||
|
||||
[System.Runtime.InteropServices.DllImport( "__Internal" )]
|
||||
private static extern string _NativeCamera_GetVideoThumbnail( string path, string thumbnailSavePath, int maxSize, double captureTimeInSeconds );
|
||||
|
||||
[System.Runtime.InteropServices.DllImport( "__Internal" )]
|
||||
private static extern string _NativeCamera_LoadImageAtPath( string path, string temporaryFilePath, int maxSize );
|
||||
#endif
|
||||
|
||||
#if !UNITY_EDITOR && ( UNITY_ANDROID || UNITY_IOS )
|
||||
private static string m_temporaryImagePath = null;
|
||||
private static string TemporaryImagePath
|
||||
{
|
||||
get
|
||||
{
|
||||
if( m_temporaryImagePath == null )
|
||||
{
|
||||
m_temporaryImagePath = Path.Combine( Application.temporaryCachePath, "tmpImg" );
|
||||
Directory.CreateDirectory( Application.temporaryCachePath );
|
||||
}
|
||||
|
||||
return m_temporaryImagePath;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !UNITY_EDITOR && UNITY_IOS
|
||||
private static string m_iOSSelectedImagePath = null;
|
||||
private static string IOSSelectedImagePath
|
||||
{
|
||||
get
|
||||
{
|
||||
if( m_iOSSelectedImagePath == null )
|
||||
{
|
||||
m_iOSSelectedImagePath = Path.Combine( Application.temporaryCachePath, "CameraImg" );
|
||||
Directory.CreateDirectory( Application.temporaryCachePath );
|
||||
}
|
||||
|
||||
return m_iOSSelectedImagePath;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region Runtime Permissions
|
||||
public static bool CheckPermission( bool isPicturePermission )
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
return AJC.CallStatic<int>( "CheckPermission", Context, isPicturePermission ) == 1;
|
||||
#elif !UNITY_EDITOR && UNITY_IOS
|
||||
return _NativeCamera_CheckPermission() == 1;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void RequestPermissionAsync( PermissionCallback callback, bool isPicturePermission )
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
NCPermissionCallbackAndroid nativeCallback = new( callback );
|
||||
AJC.CallStatic( "RequestPermission", Context, nativeCallback, isPicturePermission );
|
||||
#elif !UNITY_EDITOR && UNITY_IOS
|
||||
NCPermissionCallbackiOS.Initialize( callback );
|
||||
_NativeCamera_RequestPermission();
|
||||
#else
|
||||
callback( Permission.Granted );
|
||||
#endif
|
||||
}
|
||||
|
||||
public static Task<Permission> RequestPermissionAsync( bool isPicturePermission )
|
||||
{
|
||||
TaskCompletionSource<Permission> tcs = new TaskCompletionSource<Permission>();
|
||||
RequestPermissionAsync( ( permission ) => tcs.SetResult( permission ), isPicturePermission );
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
public static void OpenSettings()
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
AJC.CallStatic( "OpenSettings", Context );
|
||||
#elif !UNITY_EDITOR && UNITY_IOS
|
||||
_NativeCamera_OpenSettings();
|
||||
#endif
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Camera Functions
|
||||
public static void TakePicture( CameraCallback callback, int maxSize = -1, bool saveAsJPEG = true, PreferredCamera preferredCamera = PreferredCamera.Default )
|
||||
{
|
||||
RequestPermissionAsync( ( permission ) =>
|
||||
{
|
||||
if( permission != Permission.Granted || IsCameraBusy() )
|
||||
{
|
||||
callback?.Invoke( null );
|
||||
return;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
string pickedFile = UnityEditor.EditorUtility.OpenFilePanelWithFilters( "Select image", "", new string[] { "Image files", "png,jpg,jpeg", "All files", "*" } );
|
||||
|
||||
if( callback != null )
|
||||
callback( pickedFile != "" ? pickedFile : null );
|
||||
#elif UNITY_ANDROID
|
||||
AJC.CallStatic( "TakePicture", Context, new NCCameraCallbackAndroid( callback ), (int) preferredCamera );
|
||||
#elif UNITY_IOS
|
||||
if( maxSize <= 0 )
|
||||
maxSize = SystemInfo.maxTextureSize;
|
||||
|
||||
NCCameraCallbackiOS.Initialize( callback );
|
||||
_NativeCamera_TakePicture( IOSSelectedImagePath + ( saveAsJPEG ? ".jpeg" : ".png" ), maxSize, (int) preferredCamera );
|
||||
#else
|
||||
if( callback != null )
|
||||
callback( null );
|
||||
#endif
|
||||
}, true );
|
||||
}
|
||||
|
||||
public static void RecordVideo( CameraCallback callback, Quality quality = Quality.Default, int maxDuration = 0, long maxSizeBytes = 0L, PreferredCamera preferredCamera = PreferredCamera.Default )
|
||||
{
|
||||
RequestPermissionAsync( ( permission ) =>
|
||||
{
|
||||
if( permission != Permission.Granted || IsCameraBusy() )
|
||||
{
|
||||
callback?.Invoke( null );
|
||||
return;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
string pickedFile = UnityEditor.EditorUtility.OpenFilePanelWithFilters( "Select video", "", new string[] { "Video files", "mp4,mov,webm,avi", "All files", "*" } );
|
||||
|
||||
if( callback != null )
|
||||
callback( pickedFile != "" ? pickedFile : null );
|
||||
#elif UNITY_ANDROID
|
||||
AJC.CallStatic( "RecordVideo", Context, new NCCameraCallbackAndroid( callback ), (int) preferredCamera, (int) quality, maxDuration, maxSizeBytes );
|
||||
#elif UNITY_IOS
|
||||
NCCameraCallbackiOS.Initialize( callback );
|
||||
_NativeCamera_RecordVideo( (int) quality, maxDuration, (int) preferredCamera );
|
||||
#else
|
||||
if( callback != null )
|
||||
callback( null );
|
||||
#endif
|
||||
}, false );
|
||||
}
|
||||
|
||||
public static bool DeviceHasCamera()
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
return AJC.CallStatic<bool>( "HasCamera", Context );
|
||||
#elif !UNITY_EDITOR && UNITY_IOS
|
||||
return _NativeCamera_HasCamera() == 1;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static bool IsCameraBusy()
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_IOS
|
||||
return NCCameraCallbackiOS.IsBusy;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Utility Functions
|
||||
public static Texture2D LoadImageAtPath( string imagePath, int maxSize = -1, bool markTextureNonReadable = true, bool generateMipmaps = true, bool linearColorSpace = false )
|
||||
{
|
||||
if( string.IsNullOrEmpty( imagePath ) )
|
||||
throw new ArgumentException( "Parameter 'imagePath' is null or empty!" );
|
||||
|
||||
if( !File.Exists( imagePath ) )
|
||||
throw new FileNotFoundException( "File not found at " + imagePath );
|
||||
|
||||
if( maxSize <= 0 )
|
||||
maxSize = SystemInfo.maxTextureSize;
|
||||
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
string loadPath = AJC.CallStatic<string>( "LoadImageAtPath", Context, imagePath, TemporaryImagePath, maxSize );
|
||||
#elif !UNITY_EDITOR && UNITY_IOS
|
||||
string loadPath = _NativeCamera_LoadImageAtPath( imagePath, TemporaryImagePath, maxSize );
|
||||
#else
|
||||
string loadPath = imagePath;
|
||||
#endif
|
||||
|
||||
string extension = Path.GetExtension( imagePath ).ToLowerInvariant();
|
||||
TextureFormat format = ( extension == ".jpg" || extension == ".jpeg" ) ? TextureFormat.RGB24 : TextureFormat.RGBA32;
|
||||
|
||||
Texture2D result = new Texture2D( 2, 2, format, generateMipmaps, linearColorSpace );
|
||||
|
||||
try
|
||||
{
|
||||
if( !result.LoadImage( File.ReadAllBytes( loadPath ), markTextureNonReadable ) )
|
||||
{
|
||||
Debug.LogWarning( "Couldn't load image at path: " + loadPath );
|
||||
|
||||
Object.DestroyImmediate( result );
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
Debug.LogException( e );
|
||||
|
||||
Object.DestroyImmediate( result );
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if( loadPath != imagePath )
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete( loadPath );
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static async Task<Texture2D> LoadImageAtPathAsync( string imagePath, int maxSize = -1, bool markTextureNonReadable = true )
|
||||
{
|
||||
if( string.IsNullOrEmpty( imagePath ) )
|
||||
throw new ArgumentException( "Parameter 'imagePath' is null or empty!" );
|
||||
|
||||
if( !File.Exists( imagePath ) )
|
||||
throw new FileNotFoundException( "File not found at " + imagePath );
|
||||
|
||||
if( maxSize <= 0 )
|
||||
maxSize = SystemInfo.maxTextureSize;
|
||||
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
string temporaryImagePath = TemporaryImagePath; // Must be accessed from main thread
|
||||
string loadPath = await TryCallNativeAndroidFunctionOnSeparateThread( () => AJC.CallStatic<string>( "LoadImageAtPath", Context, imagePath, temporaryImagePath, maxSize ) );
|
||||
#elif !UNITY_EDITOR && UNITY_IOS
|
||||
string temporaryImagePath = TemporaryImagePath; // Must be accessed from main thread
|
||||
string loadPath = await Task.Run( () => _NativeCamera_LoadImageAtPath( imagePath, temporaryImagePath, maxSize ) );
|
||||
#else
|
||||
string loadPath = imagePath;
|
||||
#endif
|
||||
|
||||
Texture2D result = null;
|
||||
|
||||
using( UnityWebRequest www = UnityWebRequestTexture.GetTexture( "file://" + loadPath, markTextureNonReadable ) )
|
||||
{
|
||||
UnityWebRequestAsyncOperation asyncOperation = www.SendWebRequest();
|
||||
while( !asyncOperation.isDone )
|
||||
await Task.Yield();
|
||||
|
||||
if( www.result != UnityWebRequest.Result.Success )
|
||||
Debug.LogWarning( "Couldn't use UnityWebRequest to load image, falling back to LoadImage: " + www.error );
|
||||
else
|
||||
result = DownloadHandlerTexture.GetContent( www );
|
||||
}
|
||||
|
||||
if( !result ) // Fallback to Texture2D.LoadImage if something goes wrong
|
||||
{
|
||||
string extension = Path.GetExtension( imagePath ).ToLowerInvariant();
|
||||
TextureFormat format = ( extension == ".jpg" || extension == ".jpeg" ) ? TextureFormat.RGB24 : TextureFormat.RGBA32;
|
||||
|
||||
result = new Texture2D( 2, 2, format, true, false );
|
||||
|
||||
try
|
||||
{
|
||||
if( !result.LoadImage( File.ReadAllBytes( loadPath ), markTextureNonReadable ) )
|
||||
{
|
||||
Debug.LogWarning( "Couldn't load image at path: " + loadPath );
|
||||
|
||||
Object.DestroyImmediate( result );
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
Debug.LogException( e );
|
||||
|
||||
Object.DestroyImmediate( result );
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if( loadPath != imagePath )
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete( loadPath );
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Texture2D GetVideoThumbnail( string videoPath, int maxSize = -1, double captureTimeInSeconds = -1.0, bool markTextureNonReadable = true, bool generateMipmaps = true, bool linearColorSpace = false )
|
||||
{
|
||||
if( maxSize <= 0 )
|
||||
maxSize = SystemInfo.maxTextureSize;
|
||||
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
string thumbnailPath = AJC.CallStatic<string>( "GetVideoThumbnail", Context, videoPath, TemporaryImagePath + ".png", false, maxSize, captureTimeInSeconds );
|
||||
#elif !UNITY_EDITOR && UNITY_IOS
|
||||
string thumbnailPath = _NativeCamera_GetVideoThumbnail( videoPath, TemporaryImagePath + ".png", maxSize, captureTimeInSeconds );
|
||||
#else
|
||||
string thumbnailPath = null;
|
||||
#endif
|
||||
|
||||
if( !string.IsNullOrEmpty( thumbnailPath ) )
|
||||
return LoadImageAtPath( thumbnailPath, maxSize, markTextureNonReadable, generateMipmaps, linearColorSpace );
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public static async Task<Texture2D> GetVideoThumbnailAsync( string videoPath, int maxSize = -1, double captureTimeInSeconds = -1.0, bool markTextureNonReadable = true )
|
||||
{
|
||||
if( maxSize <= 0 )
|
||||
maxSize = SystemInfo.maxTextureSize;
|
||||
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
string temporaryImagePath = TemporaryImagePath; // Must be accessed from main thread
|
||||
string thumbnailPath = await TryCallNativeAndroidFunctionOnSeparateThread( () => AJC.CallStatic<string>( "GetVideoThumbnail", Context, videoPath, temporaryImagePath + ".png", false, maxSize, captureTimeInSeconds ) );
|
||||
#elif !UNITY_EDITOR && UNITY_IOS
|
||||
string temporaryImagePath = TemporaryImagePath; // Must be accessed from main thread
|
||||
string thumbnailPath = await Task.Run( () => _NativeCamera_GetVideoThumbnail( videoPath, temporaryImagePath + ".png", maxSize, captureTimeInSeconds ) );
|
||||
#else
|
||||
string thumbnailPath = null;
|
||||
#endif
|
||||
|
||||
if( !string.IsNullOrEmpty( thumbnailPath ) )
|
||||
return await LoadImageAtPathAsync( thumbnailPath, maxSize, markTextureNonReadable );
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
#if UNITY_ANDROID
|
||||
private static async Task<T> TryCallNativeAndroidFunctionOnSeparateThread<T>( Func<T> function )
|
||||
{
|
||||
T result = default( T );
|
||||
bool hasResult = false;
|
||||
|
||||
await Task.Run( () =>
|
||||
{
|
||||
if( AndroidJNI.AttachCurrentThread() != 0 )
|
||||
Debug.LogWarning( "Couldn't attach JNI thread, calling native function on the main thread" );
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
result = function();
|
||||
hasResult = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
AndroidJNI.DetachCurrentThread();
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
return hasResult ? result : function();
|
||||
}
|
||||
#endif
|
||||
|
||||
public static ImageProperties GetImageProperties( string imagePath )
|
||||
{
|
||||
if( !File.Exists( imagePath ) )
|
||||
throw new FileNotFoundException( "File not found at " + imagePath );
|
||||
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
string value = AJC.CallStatic<string>( "GetImageProperties", Context, imagePath );
|
||||
#elif !UNITY_EDITOR && UNITY_IOS
|
||||
string value = _NativeCamera_GetImageProperties( imagePath );
|
||||
#else
|
||||
string value = null;
|
||||
#endif
|
||||
|
||||
int width = 0, height = 0;
|
||||
string mimeType = null;
|
||||
ImageOrientation orientation = ImageOrientation.Unknown;
|
||||
if( !string.IsNullOrEmpty( value ) )
|
||||
{
|
||||
string[] properties = value.Split( '>' );
|
||||
if( properties != null && properties.Length >= 4 )
|
||||
{
|
||||
if( !int.TryParse( properties[0].Trim(), out width ) )
|
||||
width = 0;
|
||||
if( !int.TryParse( properties[1].Trim(), out height ) )
|
||||
height = 0;
|
||||
|
||||
mimeType = properties[2].Trim();
|
||||
if( mimeType.Length == 0 )
|
||||
{
|
||||
string extension = Path.GetExtension( imagePath ).ToLowerInvariant();
|
||||
if( extension == ".png" )
|
||||
mimeType = "image/png";
|
||||
else if( extension == ".jpg" || extension == ".jpeg" )
|
||||
mimeType = "image/jpeg";
|
||||
else if( extension == ".gif" )
|
||||
mimeType = "image/gif";
|
||||
else if( extension == ".bmp" )
|
||||
mimeType = "image/bmp";
|
||||
else
|
||||
mimeType = null;
|
||||
}
|
||||
|
||||
int orientationInt;
|
||||
if( int.TryParse( properties[3].Trim(), out orientationInt ) )
|
||||
orientation = (ImageOrientation) orientationInt;
|
||||
}
|
||||
}
|
||||
|
||||
return new ImageProperties( width, height, mimeType, orientation );
|
||||
}
|
||||
|
||||
public static VideoProperties GetVideoProperties( string videoPath )
|
||||
{
|
||||
if( !File.Exists( videoPath ) )
|
||||
throw new FileNotFoundException( "File not found at " + videoPath );
|
||||
|
||||
#if !UNITY_EDITOR && UNITY_ANDROID
|
||||
string value = AJC.CallStatic<string>( "GetVideoProperties", Context, videoPath );
|
||||
#elif !UNITY_EDITOR && UNITY_IOS
|
||||
string value = _NativeCamera_GetVideoProperties( videoPath );
|
||||
#else
|
||||
string value = null;
|
||||
#endif
|
||||
|
||||
int width = 0, height = 0;
|
||||
long duration = 0L;
|
||||
float rotation = 0f;
|
||||
if( !string.IsNullOrEmpty( value ) )
|
||||
{
|
||||
string[] properties = value.Split( '>' );
|
||||
if( properties != null && properties.Length >= 4 )
|
||||
{
|
||||
if( !int.TryParse( properties[0].Trim(), out width ) )
|
||||
width = 0;
|
||||
if( !int.TryParse( properties[1].Trim(), out height ) )
|
||||
height = 0;
|
||||
if( !long.TryParse( properties[2].Trim(), out duration ) )
|
||||
duration = 0L;
|
||||
if( !float.TryParse( properties[3].Trim().Replace( ',', '.' ), NumberStyles.Float, CultureInfo.InvariantCulture, out rotation ) )
|
||||
rotation = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
if( rotation == -90f )
|
||||
rotation = 270f;
|
||||
|
||||
return new VideoProperties( width, height, duration, rotation );
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff758a73b21d4a04aa6f95679b3da605
|
||||
timeCreated: 1498722610
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 117802
|
||||
packageName: Native Camera for Android & iOS
|
||||
packageVersion: 1.5.0
|
||||
assetPath: Assets/Plugins/NativeCamera/NativeCamera.cs
|
||||
uploadId: 746912
|
||||
@@ -0,0 +1,6 @@
|
||||
= Native Camera for Android & iOS (v1.5.0) =
|
||||
|
||||
Documentation: https://github.com/yasirkula/UnityNativeCamera
|
||||
FAQ: https://github.com/yasirkula/UnityNativeCamera#faq
|
||||
Example code: https://github.com/yasirkula/UnityNativeCamera#example-code
|
||||
E-mail: yasirkula@gmail.com
|
||||
@@ -0,0 +1,15 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5a88d1b1b9d7b904b862304c20ed4db4
|
||||
timeCreated: 1563308465
|
||||
licenseType: Store
|
||||
TextScriptImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 117802
|
||||
packageName: Native Camera for Android & iOS
|
||||
packageVersion: 1.5.0
|
||||
assetPath: Assets/Plugins/NativeCamera/README.txt
|
||||
uploadId: 746912
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5576acd2a06eb72409e6ec4b7f204a4e
|
||||
folderAsset: yes
|
||||
timeCreated: 1498722622
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,73 @@
|
||||
#if UNITY_EDITOR || UNITY_IOS
|
||||
using UnityEngine;
|
||||
|
||||
namespace NativeCameraNamespace
|
||||
{
|
||||
public class NCCameraCallbackiOS : MonoBehaviour
|
||||
{
|
||||
private static NCCameraCallbackiOS instance;
|
||||
private NativeCamera.CameraCallback callback;
|
||||
|
||||
private float nextBusyCheckTime;
|
||||
|
||||
public static bool IsBusy { get; private set; }
|
||||
|
||||
[System.Runtime.InteropServices.DllImport( "__Internal" )]
|
||||
private static extern int _NativeCamera_IsCameraBusy();
|
||||
|
||||
public static void Initialize( NativeCamera.CameraCallback callback )
|
||||
{
|
||||
if( IsBusy )
|
||||
return;
|
||||
|
||||
if( instance == null )
|
||||
{
|
||||
instance = new GameObject( "NCCameraCallbackiOS" ).AddComponent<NCCameraCallbackiOS>();
|
||||
DontDestroyOnLoad( instance.gameObject );
|
||||
}
|
||||
|
||||
instance.callback = callback;
|
||||
|
||||
instance.nextBusyCheckTime = Time.realtimeSinceStartup + 1f;
|
||||
IsBusy = true;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if( IsBusy )
|
||||
{
|
||||
if( Time.realtimeSinceStartup >= nextBusyCheckTime )
|
||||
{
|
||||
nextBusyCheckTime = Time.realtimeSinceStartup + 1f;
|
||||
|
||||
if( _NativeCamera_IsCameraBusy() == 0 )
|
||||
{
|
||||
IsBusy = false;
|
||||
|
||||
NativeCamera.CameraCallback _callback = callback;
|
||||
callback = null;
|
||||
|
||||
if( _callback != null )
|
||||
_callback( null );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
public void OnMediaReceived( string path )
|
||||
{
|
||||
IsBusy = false;
|
||||
|
||||
if( string.IsNullOrEmpty( path ) )
|
||||
path = null;
|
||||
|
||||
NativeCamera.CameraCallback _callback = callback;
|
||||
callback = null;
|
||||
|
||||
if( _callback != null )
|
||||
_callback( path );
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d8f19d5713752dc41bd377562677d8ee
|
||||
timeCreated: 1519060539
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 117802
|
||||
packageName: Native Camera for Android & iOS
|
||||
packageVersion: 1.5.0
|
||||
assetPath: Assets/Plugins/NativeCamera/iOS/NCCameraCallbackiOS.cs
|
||||
uploadId: 746912
|
||||
@@ -0,0 +1,35 @@
|
||||
#if UNITY_EDITOR || UNITY_IOS
|
||||
using UnityEngine;
|
||||
|
||||
namespace NativeCameraNamespace
|
||||
{
|
||||
public class NCPermissionCallbackiOS : MonoBehaviour
|
||||
{
|
||||
private static NCPermissionCallbackiOS instance;
|
||||
private NativeCamera.PermissionCallback callback;
|
||||
|
||||
public static void Initialize( NativeCamera.PermissionCallback callback )
|
||||
{
|
||||
if( instance == null )
|
||||
{
|
||||
instance = new GameObject( "NCPermissionCallbackiOS" ).AddComponent<NCPermissionCallbackiOS>();
|
||||
DontDestroyOnLoad( instance.gameObject );
|
||||
}
|
||||
else if( instance.callback != null )
|
||||
instance.callback( NativeCamera.Permission.ShouldAsk );
|
||||
|
||||
instance.callback = callback;
|
||||
}
|
||||
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
public void OnPermissionRequested( string message )
|
||||
{
|
||||
NativeCamera.PermissionCallback _callback = callback;
|
||||
callback = null;
|
||||
|
||||
if( _callback != null )
|
||||
_callback( (NativeCamera.Permission) int.Parse( message ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1efd0cf9fb7457142b76fb1bc8672e8d
|
||||
timeCreated: 1519060539
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 117802
|
||||
packageName: Native Camera for Android & iOS
|
||||
packageVersion: 1.5.0
|
||||
assetPath: Assets/Plugins/NativeCamera/iOS/NCPermissionCallbackiOS.cs
|
||||
uploadId: 746912
|
||||
@@ -0,0 +1,561 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <MobileCoreServices/UTCoreTypes.h>
|
||||
#import <ImageIO/ImageIO.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
extern UIViewController* UnityGetGLViewController();
|
||||
|
||||
@interface UNativeCamera:NSObject
|
||||
+ (int)checkPermission;
|
||||
+ (void)requestPermission;
|
||||
+ (void)openSettings;
|
||||
+ (int)hasCamera;
|
||||
+ (void)openCamera:(BOOL)imageMode defaultCamera:(int)defaultCamera savePath:(NSString *)imageSavePath maxImageSize:(int)maxImageSize videoQuality:(int)videoQuality maxVideoDuration:(int)maxVideoDuration;
|
||||
+ (int)isCameraBusy;
|
||||
+ (char *)getImageProperties:(NSString *)path;
|
||||
+ (char *)getVideoProperties:(NSString *)path;
|
||||
+ (char *)getVideoThumbnail:(NSString *)path savePath:(NSString *)savePath maximumSize:(int)maximumSize captureTime:(double)captureTime;
|
||||
+ (char *)loadImageAtPath:(NSString *)path tempFilePath:(NSString *)tempFilePath maximumSize:(int)maximumSize;
|
||||
@end
|
||||
|
||||
@implementation UNativeCamera
|
||||
|
||||
static NSString *pickedMediaSavePath;
|
||||
static UIImagePickerController *imagePicker;
|
||||
static int cameraMaxImageSize = -1;
|
||||
static int imagePickerState = 0; // 0 -> none, 1 -> showing, 2 -> finished
|
||||
static BOOL recordingVideo = NO;
|
||||
static AVAudioSessionCategory unityAudioSessionCategory = AVAudioSessionCategoryAmbient;
|
||||
static NSUInteger unityAudioSessionCategoryOptions = 1;
|
||||
static AVAudioSessionMode unityAudioSessionMode = AVAudioSessionModeDefault;
|
||||
|
||||
// Credit: https://stackoverflow.com/a/20464727/2373034
|
||||
+ (int)checkPermission
|
||||
{
|
||||
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
|
||||
if( status == AVAuthorizationStatusAuthorized )
|
||||
return 1;
|
||||
else if( status == AVAuthorizationStatusNotDetermined )
|
||||
return 2;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Credit: https://stackoverflow.com/a/20464727/2373034
|
||||
+ (void)requestPermission
|
||||
{
|
||||
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
|
||||
if( status == AVAuthorizationStatusAuthorized )
|
||||
UnitySendMessage( "NCPermissionCallbackiOS", "OnPermissionRequested", "1" );
|
||||
else if( status == AVAuthorizationStatusNotDetermined )
|
||||
{
|
||||
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^( BOOL granted )
|
||||
{
|
||||
UnitySendMessage( "NCPermissionCallbackiOS", "OnPermissionRequested", granted ? "1" : "0" );
|
||||
}];
|
||||
}
|
||||
else
|
||||
UnitySendMessage( "NCPermissionCallbackiOS", "OnPermissionRequested", "0" );
|
||||
}
|
||||
|
||||
// Credit: https://stackoverflow.com/a/25453667/2373034
|
||||
+ (void)openSettings
|
||||
{
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:nil];
|
||||
}
|
||||
|
||||
+ (int)hasCamera
|
||||
{
|
||||
return [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera] ? 1 : 0;
|
||||
}
|
||||
|
||||
// Credit: https://stackoverflow.com/a/10531752/2373034
|
||||
+ (void)openCamera:(BOOL)imageMode defaultCamera:(int)defaultCamera savePath:(NSString *)imageSavePath maxImageSize:(int)maxImageSize videoQuality:(int)videoQuality maxVideoDuration:(int)maxVideoDuration
|
||||
{
|
||||
if( ![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera] )
|
||||
{
|
||||
NSLog( @"Device has no registered cameras!" );
|
||||
|
||||
UnitySendMessage( "NCCameraCallbackiOS", "OnMediaReceived", "" );
|
||||
return;
|
||||
}
|
||||
|
||||
if( ( imageMode && ![[UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera] containsObject:(NSString*)kUTTypeImage] ) ||
|
||||
( !imageMode && ![[UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera] containsObject:(NSString*)kUTTypeMovie] ) )
|
||||
{
|
||||
NSLog( @"Camera does not support this operation!" );
|
||||
|
||||
UnitySendMessage( "NCCameraCallbackiOS", "OnMediaReceived", "" );
|
||||
return;
|
||||
}
|
||||
|
||||
imagePicker = [[UIImagePickerController alloc] init];
|
||||
imagePicker.delegate = (id) self;
|
||||
imagePicker.allowsEditing = NO;
|
||||
imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
|
||||
|
||||
if( imageMode )
|
||||
imagePicker.mediaTypes = [NSArray arrayWithObject:(NSString *)kUTTypeImage];
|
||||
else
|
||||
{
|
||||
imagePicker.mediaTypes = [NSArray arrayWithObject:(NSString *)kUTTypeMovie];
|
||||
|
||||
if( maxVideoDuration > 0 )
|
||||
imagePicker.videoMaximumDuration = maxVideoDuration;
|
||||
|
||||
if( videoQuality == 0 )
|
||||
imagePicker.videoQuality = UIImagePickerControllerQualityTypeLow;
|
||||
else if( videoQuality == 1 )
|
||||
imagePicker.videoQuality = UIImagePickerControllerQualityTypeMedium;
|
||||
else if( videoQuality == 2 )
|
||||
imagePicker.videoQuality = UIImagePickerControllerQualityTypeHigh;
|
||||
}
|
||||
|
||||
if( defaultCamera == 0 && [UIImagePickerController isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceRear] )
|
||||
imagePicker.cameraDevice = UIImagePickerControllerCameraDeviceRear;
|
||||
else if( defaultCamera == 1 && [UIImagePickerController isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront] )
|
||||
imagePicker.cameraDevice = UIImagePickerControllerCameraDeviceFront;
|
||||
|
||||
// Bugfix for https://github.com/yasirkula/UnityNativeCamera/issues/45
|
||||
if( !imageMode )
|
||||
{
|
||||
unityAudioSessionCategory = [[AVAudioSession sharedInstance] category];
|
||||
unityAudioSessionCategoryOptions = [[AVAudioSession sharedInstance] categoryOptions];
|
||||
unityAudioSessionMode = [[AVAudioSession sharedInstance] mode];
|
||||
}
|
||||
|
||||
recordingVideo = !imageMode;
|
||||
pickedMediaSavePath = imageSavePath;
|
||||
cameraMaxImageSize = maxImageSize;
|
||||
|
||||
imagePickerState = 1;
|
||||
[UnityGetGLViewController() presentViewController:imagePicker animated:YES completion:^{ imagePickerState = 0; }];
|
||||
}
|
||||
|
||||
+ (int)isCameraBusy
|
||||
{
|
||||
if( imagePickerState == 2 )
|
||||
return 1;
|
||||
|
||||
if( imagePicker != nil )
|
||||
{
|
||||
if( imagePickerState == 1 || [imagePicker presentingViewController] == UnityGetGLViewController() )
|
||||
return 1;
|
||||
|
||||
imagePicker = nil;
|
||||
[self restoreAudioSession];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+ (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
|
||||
{
|
||||
NSString *path = nil;
|
||||
if( [info[UIImagePickerControllerMediaType] isEqualToString:(NSString *)kUTTypeImage] )
|
||||
{
|
||||
NSLog( @"UIImagePickerController finished taking picture" );
|
||||
|
||||
UIImage *image = info[UIImagePickerControllerEditedImage] ?: info[UIImagePickerControllerOriginalImage];
|
||||
if( image == nil )
|
||||
path = nil;
|
||||
else
|
||||
{
|
||||
NSString *extension = [pickedMediaSavePath pathExtension];
|
||||
BOOL saveAsJPEG = [extension caseInsensitiveCompare:@"jpg"] == NSOrderedSame || [extension caseInsensitiveCompare:@"jpeg"] == NSOrderedSame;
|
||||
|
||||
// Try to save the image with metadata
|
||||
// CANCELED: a number of users reported that this method results in 90-degree rotated images, uncomment at your own risk
|
||||
// Credit: https://stackoverflow.com/a/15858955
|
||||
/*NSDictionary *metadata = [info objectForKey:UIImagePickerControllerMediaMetadata];
|
||||
NSMutableDictionary *mutableMetadata = nil;
|
||||
CFDictionaryRef metadataRef;
|
||||
CFStringRef imageType;
|
||||
|
||||
if( saveAsJPEG )
|
||||
{
|
||||
mutableMetadata = [metadata mutableCopy];
|
||||
[mutableMetadata setObject:@(1.0) forKey:(__bridge NSString *)kCGImageDestinationLossyCompressionQuality];
|
||||
|
||||
metadataRef = (__bridge CFDictionaryRef) mutableMetadata;
|
||||
imageType = kUTTypeJPEG;
|
||||
}
|
||||
else
|
||||
{
|
||||
metadataRef = (__bridge CFDictionaryRef) metadata;
|
||||
imageType = kUTTypePNG;
|
||||
}
|
||||
|
||||
CGImageDestinationRef imageDestination = CGImageDestinationCreateWithURL( (__bridge CFURLRef) [NSURL fileURLWithPath:pickedMediaSavePath], imageType , 1, NULL );
|
||||
if( imageDestination == NULL )
|
||||
NSLog( @"Failed to create image destination" );
|
||||
else
|
||||
{
|
||||
CGImageDestinationAddImage( imageDestination, image.CGImage, metadataRef );
|
||||
if( CGImageDestinationFinalize( imageDestination ) )
|
||||
path = pickedMediaSavePath;
|
||||
else
|
||||
NSLog( @"Failed to finalize the image" );
|
||||
|
||||
CFRelease( imageDestination );
|
||||
}*/
|
||||
|
||||
if( path == nil )
|
||||
{
|
||||
//NSLog( @"Attempting to save the image without metadata as fallback" );
|
||||
|
||||
if( ( saveAsJPEG && [UIImageJPEGRepresentation( [self scaleImage:image maxSize:cameraMaxImageSize], 1.0 ) writeToFile:pickedMediaSavePath atomically:YES] ) ||
|
||||
( !saveAsJPEG && [UIImagePNGRepresentation( [self scaleImage:image maxSize:cameraMaxImageSize] ) writeToFile:pickedMediaSavePath atomically:YES] ) )
|
||||
path = pickedMediaSavePath;
|
||||
else
|
||||
{
|
||||
NSLog( @"Error saving image without metadata" );
|
||||
path = nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog( @"UIImagePickerController finished recording video" );
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
NSURL *mediaUrl = info[UIImagePickerControllerMediaURL] ?: info[UIImagePickerControllerReferenceURL];
|
||||
#pragma clang diagnostic pop
|
||||
if( mediaUrl == nil )
|
||||
path = nil;
|
||||
else
|
||||
path = [mediaUrl path];
|
||||
}
|
||||
|
||||
imagePicker = nil;
|
||||
imagePickerState = 2;
|
||||
UnitySendMessage( "NCCameraCallbackiOS", "OnMediaReceived", [self getCString:path] );
|
||||
|
||||
[picker dismissViewControllerAnimated:NO completion:nil];
|
||||
[self restoreAudioSession];
|
||||
}
|
||||
|
||||
+ (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
|
||||
{
|
||||
NSLog( @"UIImagePickerController cancelled" );
|
||||
|
||||
imagePicker = nil;
|
||||
UnitySendMessage( "NCCameraCallbackiOS", "OnMediaReceived", "" );
|
||||
|
||||
[picker dismissViewControllerAnimated:NO completion:nil];
|
||||
[self restoreAudioSession];
|
||||
}
|
||||
|
||||
// Bugfix for https://github.com/yasirkula/UnityNativeCamera/issues/45
|
||||
+ (void)restoreAudioSession
|
||||
{
|
||||
if( recordingVideo )
|
||||
{
|
||||
NSError *error = nil;
|
||||
if( ![[AVAudioSession sharedInstance] setCategory:unityAudioSessionCategory mode:unityAudioSessionMode options:unityAudioSessionCategoryOptions error:&error] )
|
||||
{
|
||||
if( error != nil )
|
||||
NSLog( @"Error setting audio session category back to %@ with mode %@ and options %lu: %@", unityAudioSessionCategory, unityAudioSessionMode, (unsigned long) unityAudioSessionCategoryOptions, error );
|
||||
else
|
||||
NSLog( @"Error setting audio session category back to %@ with mode %@ and options %lu", unityAudioSessionCategory, unityAudioSessionMode, (unsigned long) unityAudioSessionCategoryOptions );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Credit: https://stackoverflow.com/a/4170099/2373034
|
||||
+ (NSArray *)getImageMetadata:(NSString *)path
|
||||
{
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int orientation = -1;
|
||||
|
||||
CGImageSourceRef imageSource = CGImageSourceCreateWithURL( (__bridge CFURLRef) [NSURL fileURLWithPath:path], nil );
|
||||
if( imageSource != nil )
|
||||
{
|
||||
NSDictionary *options = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO] forKey:(__bridge NSString *)kCGImageSourceShouldCache];
|
||||
CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex( imageSource, 0, (__bridge CFDictionaryRef) options );
|
||||
CFRelease( imageSource );
|
||||
|
||||
CGFloat widthF = 0.0f, heightF = 0.0f;
|
||||
if( imageProperties != nil )
|
||||
{
|
||||
if( CFDictionaryContainsKey( imageProperties, kCGImagePropertyPixelWidth ) )
|
||||
CFNumberGetValue( (CFNumberRef) CFDictionaryGetValue( imageProperties, kCGImagePropertyPixelWidth ), kCFNumberCGFloatType, &widthF );
|
||||
|
||||
if( CFDictionaryContainsKey( imageProperties, kCGImagePropertyPixelHeight ) )
|
||||
CFNumberGetValue( (CFNumberRef) CFDictionaryGetValue( imageProperties, kCGImagePropertyPixelHeight ), kCFNumberCGFloatType, &heightF );
|
||||
|
||||
if( CFDictionaryContainsKey( imageProperties, kCGImagePropertyOrientation ) )
|
||||
{
|
||||
CFNumberGetValue( (CFNumberRef) CFDictionaryGetValue( imageProperties, kCGImagePropertyOrientation ), kCFNumberIntType, &orientation );
|
||||
|
||||
if( orientation > 4 )
|
||||
{
|
||||
// Landscape image
|
||||
CGFloat temp = widthF;
|
||||
widthF = heightF;
|
||||
heightF = temp;
|
||||
}
|
||||
}
|
||||
|
||||
CFRelease( imageProperties );
|
||||
}
|
||||
|
||||
width = (int) roundf( widthF );
|
||||
height = (int) roundf( heightF );
|
||||
}
|
||||
|
||||
return [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:width], [NSNumber numberWithInt:height], [NSNumber numberWithInt:orientation], nil];
|
||||
}
|
||||
|
||||
+ (char *)getImageProperties:(NSString *)path
|
||||
{
|
||||
NSArray *metadata = [self getImageMetadata:path];
|
||||
|
||||
int orientationUnity;
|
||||
int orientation = [metadata[2] intValue];
|
||||
|
||||
// To understand the magic numbers, see ImageOrientation enum in NativeCamera.cs
|
||||
// and http://sylvana.net/jpegcrop/exif_orientation.html
|
||||
if( orientation == 1 )
|
||||
orientationUnity = 0;
|
||||
else if( orientation == 2 )
|
||||
orientationUnity = 4;
|
||||
else if( orientation == 3 )
|
||||
orientationUnity = 2;
|
||||
else if( orientation == 4 )
|
||||
orientationUnity = 6;
|
||||
else if( orientation == 5 )
|
||||
orientationUnity = 5;
|
||||
else if( orientation == 6 )
|
||||
orientationUnity = 1;
|
||||
else if( orientation == 7 )
|
||||
orientationUnity = 7;
|
||||
else if( orientation == 8 )
|
||||
orientationUnity = 3;
|
||||
else
|
||||
orientationUnity = -1;
|
||||
|
||||
return [self getCString:[NSString stringWithFormat:@"%d>%d> >%d", [metadata[0] intValue], [metadata[1] intValue], orientationUnity]];
|
||||
}
|
||||
|
||||
+ (char *)getVideoProperties:(NSString *)path
|
||||
{
|
||||
CGSize size = CGSizeZero;
|
||||
float rotation = 0;
|
||||
long long duration = 0;
|
||||
|
||||
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:path] options:nil];
|
||||
if( asset != nil )
|
||||
{
|
||||
duration = (long long) round( CMTimeGetSeconds( [asset duration] ) * 1000 );
|
||||
CGAffineTransform transform = [asset preferredTransform];
|
||||
NSArray<AVAssetTrack *>* videoTracks = [asset tracksWithMediaType:AVMediaTypeVideo];
|
||||
if( videoTracks != nil && [videoTracks count] > 0 )
|
||||
{
|
||||
size = [[videoTracks objectAtIndex:0] naturalSize];
|
||||
transform = [[videoTracks objectAtIndex:0] preferredTransform];
|
||||
}
|
||||
|
||||
rotation = atan2( transform.b, transform.a ) * ( 180.0 / M_PI );
|
||||
}
|
||||
|
||||
return [self getCString:[NSString stringWithFormat:@"%d>%d>%lld>%f", (int) roundf( size.width ), (int) roundf( size.height ), duration, rotation]];
|
||||
}
|
||||
|
||||
+ (char *)getVideoThumbnail:(NSString *)path savePath:(NSString *)savePath maximumSize:(int)maximumSize captureTime:(double)captureTime
|
||||
{
|
||||
AVAssetImageGenerator *thumbnailGenerator = [[AVAssetImageGenerator alloc] initWithAsset:[[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:path] options:nil]];
|
||||
thumbnailGenerator.appliesPreferredTrackTransform = YES;
|
||||
thumbnailGenerator.maximumSize = CGSizeMake( (CGFloat) maximumSize, (CGFloat) maximumSize );
|
||||
thumbnailGenerator.requestedTimeToleranceBefore = kCMTimeZero;
|
||||
thumbnailGenerator.requestedTimeToleranceAfter = kCMTimeZero;
|
||||
|
||||
if( captureTime < 0.0 )
|
||||
captureTime = 0.0;
|
||||
else
|
||||
{
|
||||
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:path] options:nil];
|
||||
if( asset != nil )
|
||||
{
|
||||
double videoDuration = CMTimeGetSeconds( [asset duration] );
|
||||
if( videoDuration > 0.0 && captureTime >= videoDuration - 0.1 )
|
||||
{
|
||||
if( captureTime > videoDuration )
|
||||
captureTime = videoDuration;
|
||||
|
||||
thumbnailGenerator.requestedTimeToleranceBefore = CMTimeMakeWithSeconds( 1.0, 600 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NSError *error = nil;
|
||||
CGImageRef image = [thumbnailGenerator copyCGImageAtTime:CMTimeMakeWithSeconds( captureTime, 600 ) actualTime:nil error:&error];
|
||||
if( image == nil )
|
||||
{
|
||||
if( error != nil )
|
||||
NSLog( @"Error generating video thumbnail: %@", error );
|
||||
else
|
||||
NSLog( @"Error generating video thumbnail..." );
|
||||
|
||||
return [self getCString:@""];
|
||||
}
|
||||
|
||||
UIImage *thumbnail = [[UIImage alloc] initWithCGImage:image];
|
||||
CGImageRelease( image );
|
||||
|
||||
if( ![UIImagePNGRepresentation( thumbnail ) writeToFile:savePath atomically:YES] )
|
||||
{
|
||||
NSLog( @"Error saving thumbnail image" );
|
||||
return [self getCString:@""];
|
||||
}
|
||||
|
||||
return [self getCString:savePath];
|
||||
}
|
||||
|
||||
+ (UIImage *)scaleImage:(UIImage *)image maxSize:(int)maxSize
|
||||
{
|
||||
CGFloat width = image.size.width;
|
||||
CGFloat height = image.size.height;
|
||||
|
||||
UIImageOrientation orientation = image.imageOrientation;
|
||||
if( width <= maxSize && height <= maxSize && orientation != UIImageOrientationDown &&
|
||||
orientation != UIImageOrientationLeft && orientation != UIImageOrientationRight &&
|
||||
orientation != UIImageOrientationLeftMirrored && orientation != UIImageOrientationRightMirrored &&
|
||||
orientation != UIImageOrientationUpMirrored && orientation != UIImageOrientationDownMirrored )
|
||||
return image;
|
||||
|
||||
CGFloat scaleX = 1.0f;
|
||||
CGFloat scaleY = 1.0f;
|
||||
if( width > maxSize )
|
||||
scaleX = maxSize / width;
|
||||
if( height > maxSize )
|
||||
scaleY = maxSize / height;
|
||||
|
||||
// Credit: https://github.com/mbcharbonneau/UIImage-Categories/blob/master/UIImage%2BAlpha.m
|
||||
CGImageAlphaInfo alpha = CGImageGetAlphaInfo( image.CGImage );
|
||||
BOOL hasAlpha = alpha == kCGImageAlphaFirst || alpha == kCGImageAlphaLast || alpha == kCGImageAlphaPremultipliedFirst || alpha == kCGImageAlphaPremultipliedLast;
|
||||
|
||||
CGFloat scaleRatio = scaleX < scaleY ? scaleX : scaleY;
|
||||
CGRect imageRect = CGRectMake( 0, 0, width * scaleRatio, height * scaleRatio );
|
||||
UIGraphicsImageRendererFormat *format = [image imageRendererFormat];
|
||||
format.opaque = !hasAlpha;
|
||||
format.scale = image.scale;
|
||||
UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:imageRect.size format:format];
|
||||
image = [renderer imageWithActions:^( UIGraphicsImageRendererContext* _Nonnull myContext )
|
||||
{
|
||||
[image drawInRect:imageRect];
|
||||
}];
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
+ (char *)loadImageAtPath:(NSString *)path tempFilePath:(NSString *)tempFilePath maximumSize:(int)maximumSize
|
||||
{
|
||||
// Check if the image can be loaded by Unity without requiring a conversion to PNG
|
||||
// Credit: https://stackoverflow.com/a/12048937/2373034
|
||||
NSString *extension = [path pathExtension];
|
||||
BOOL conversionNeeded = [extension caseInsensitiveCompare:@"jpg"] != NSOrderedSame && [extension caseInsensitiveCompare:@"jpeg"] != NSOrderedSame && [extension caseInsensitiveCompare:@"png"] != NSOrderedSame;
|
||||
|
||||
if( !conversionNeeded )
|
||||
{
|
||||
// Check if the image needs to be processed at all
|
||||
NSArray *metadata = [self getImageMetadata:path];
|
||||
int orientationInt = [metadata[2] intValue]; // 1: correct orientation, [1,8]: valid orientation range
|
||||
if( orientationInt == 1 && [metadata[0] intValue] <= maximumSize && [metadata[1] intValue] <= maximumSize )
|
||||
return [self getCString:path];
|
||||
}
|
||||
|
||||
UIImage *image = [UIImage imageWithContentsOfFile:path];
|
||||
if( image == nil )
|
||||
return [self getCString:path];
|
||||
|
||||
UIImage *scaledImage = [self scaleImage:image maxSize:maximumSize];
|
||||
if( conversionNeeded || scaledImage != image )
|
||||
{
|
||||
if( ![UIImagePNGRepresentation( scaledImage ) writeToFile:tempFilePath atomically:YES] )
|
||||
{
|
||||
NSLog( @"Error creating scaled image" );
|
||||
return [self getCString:path];
|
||||
}
|
||||
|
||||
return [self getCString:tempFilePath];
|
||||
}
|
||||
else
|
||||
return [self getCString:path];
|
||||
}
|
||||
|
||||
// Credit: https://stackoverflow.com/a/37052118/2373034
|
||||
+ (char *)getCString:(NSString *)source
|
||||
{
|
||||
if( source == nil )
|
||||
source = @"";
|
||||
|
||||
const char *sourceUTF8 = [source UTF8String];
|
||||
char *result = (char*) malloc( strlen( sourceUTF8 ) + 1 );
|
||||
strcpy( result, sourceUTF8 );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
extern "C" int _NativeCamera_CheckPermission()
|
||||
{
|
||||
return [UNativeCamera checkPermission];
|
||||
}
|
||||
|
||||
extern "C" void _NativeCamera_RequestPermission()
|
||||
{
|
||||
[UNativeCamera requestPermission];
|
||||
}
|
||||
|
||||
extern "C" void _NativeCamera_OpenSettings()
|
||||
{
|
||||
[UNativeCamera openSettings];
|
||||
}
|
||||
|
||||
extern "C" int _NativeCamera_HasCamera()
|
||||
{
|
||||
return [UNativeCamera hasCamera];
|
||||
}
|
||||
|
||||
extern "C" void _NativeCamera_TakePicture( const char* imageSavePath, int maxSize, int preferredCamera )
|
||||
{
|
||||
[UNativeCamera openCamera:YES defaultCamera:preferredCamera savePath:[NSString stringWithUTF8String:imageSavePath] maxImageSize:maxSize videoQuality:-1 maxVideoDuration:-1];
|
||||
}
|
||||
|
||||
extern "C" void _NativeCamera_RecordVideo( int quality, int maxDuration, int preferredCamera )
|
||||
{
|
||||
[UNativeCamera openCamera:NO defaultCamera:preferredCamera savePath:nil maxImageSize:4096 videoQuality:quality maxVideoDuration:maxDuration];
|
||||
}
|
||||
|
||||
extern "C" int _NativeCamera_IsCameraBusy()
|
||||
{
|
||||
return [UNativeCamera isCameraBusy];
|
||||
}
|
||||
|
||||
extern "C" char* _NativeCamera_GetImageProperties( const char* path )
|
||||
{
|
||||
return [UNativeCamera getImageProperties:[NSString stringWithUTF8String:path]];
|
||||
}
|
||||
|
||||
extern "C" char* _NativeCamera_GetVideoProperties( const char* path )
|
||||
{
|
||||
return [UNativeCamera getVideoProperties:[NSString stringWithUTF8String:path]];
|
||||
}
|
||||
|
||||
extern "C" char* _NativeCamera_GetVideoThumbnail( const char* path, const char* thumbnailSavePath, int maxSize, double captureTimeInSeconds )
|
||||
{
|
||||
return [UNativeCamera getVideoThumbnail:[NSString stringWithUTF8String:path] savePath:[NSString stringWithUTF8String:thumbnailSavePath] maximumSize:maxSize captureTime:captureTimeInSeconds];
|
||||
}
|
||||
|
||||
extern "C" char* _NativeCamera_LoadImageAtPath( const char* path, const char* temporaryFilePath, int maxSize )
|
||||
{
|
||||
return [UNativeCamera loadImageAtPath:[NSString stringWithUTF8String:path] tempFilePath:[NSString stringWithUTF8String:temporaryFilePath] maximumSize:maxSize];
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f71ce2f3d3a5dbd46af575e628ed9d6e
|
||||
timeCreated: 1498722774
|
||||
licenseType: Store
|
||||
PluginImporter:
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
platformData:
|
||||
data:
|
||||
first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
data:
|
||||
first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
data:
|
||||
first:
|
||||
iPhone: iOS
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 117802
|
||||
packageName: Native Camera for Android & iOS
|
||||
packageVersion: 1.5.0
|
||||
assetPath: Assets/Plugins/NativeCamera/iOS/NativeCamera.mm
|
||||
uploadId: 746912
|
||||
Reference in new issue
Block a user