Skip to content

Commit 07a6817

Browse files
committed
- BREAKING CHANGE: CheckPermission returns bool instead of Permission (see yasirkula/UnityAndroidRuntimePermissions#14)
- BREAKING CHANGE: Replaced RequestPermission with RequestPermissionAsync (see yasirkula/UnityNativeGallery#343) - BREAKING CHANGE: Removed CanOpenSettings since it's now always true - Updated Unity version to 2021.3.41f1 (simplified codebase accordingly) - iOS frameworks are now added properly instead of changing OTHER_LDFLAGS - Fixed Xcode compiler warnings
1 parent 49d16ac commit 07a6817

11 files changed

Lines changed: 106 additions & 270 deletions

File tree

.github/AAR Source (Android)/java/com/yasirkula/unity/NativeCamera.java

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,20 +101,14 @@ public static int CheckPermission( Context context, final boolean isPicturePermi
101101
}
102102

103103
// Credit: https://github.com/Over17/UnityAndroidPermissions/blob/0dca33e40628f1f279decb67d901fd444b409cd7/src/UnityAndroidPermissions/src/main/java/com/unity3d/plugin/UnityAndroidPermissions.java
104-
public static void RequestPermission( Context context, final NativeCameraPermissionReceiver permissionReceiver, final boolean isPicturePermission, final int lastCheckResult )
104+
public static void RequestPermission( Context context, final NativeCameraPermissionReceiver permissionReceiver, final boolean isPicturePermission )
105105
{
106106
if( CheckPermission( context, isPicturePermission ) == 1 )
107107
{
108108
permissionReceiver.OnPermissionResult( 1 );
109109
return;
110110
}
111111

112-
if( lastCheckResult == 0 ) // If user clicked "Don't ask again" before, don't bother asking them again
113-
{
114-
permissionReceiver.OnPermissionResult( 0 );
115-
return;
116-
}
117-
118112
Bundle bundle = new Bundle();
119113
bundle.putBoolean( NativeCameraPermissionFragment.PICTURE_PERMISSION_ID, isPicturePermission );
120114

.github/README.md

Lines changed: 12 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ If you are sure that your plugin is up-to-date, then enable **Custom Proguard Fi
4949

5050
- **NativeCamera functions return Permission.Denied even though I've granted the permission"**
5151

52-
Declare `WRITE_EXTERNAL_STORAGE` permission manually in your [**Plugins/Android/AndroidManifest.xml** file](https://answers.unity.com/questions/982710/where-is-the-manifest-file-in-unity.html) with the `tools:node="replace"` attribute as follows: `<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="replace"/>` (you'll need to add the `xmlns:tools="http://schemas.android.com/tools"` attribute to the `<manifest ...>` element).
52+
Declare `WRITE_EXTERNAL_STORAGE` permission manually in your **Plugins/Android/AndroidManifest.xml** with the `tools:node="replace"` attribute as follows: `<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="replace"/>`.
5353

5454
## HOW TO
5555

@@ -70,31 +70,27 @@ Declare `WRITE_EXTERNAL_STORAGE` permission manually in your [**Plugins/Android/
7070

7171
`NativeCamera.IsCameraBusy()`: returns true if the camera is currently open. In that case, another TakePicture or RecordVideo request will simply be ignored.
7272

73-
Note that TakePicture and RecordVideo functions return a *NativeCamera.Permission* value. More details available below.
73+
Note that TakePicture and RecordVideo functions automatically call *NativeCamera.RequestPermissionAsync*. More details available below.
7474

7575
### B. Runtime Permissions
7676

7777
Beginning with *6.0 Marshmallow*, Android apps must request runtime permissions before accessing certain services, similar to iOS. There are two functions to handle permissions with this plugin:
7878

79-
`NativeCamera.Permission NativeCamera.CheckPermission( bool isPicturePermission )`: checks whether the app has access to camera or not.
79+
`bool NativeCamera.CheckPermission( bool isPicturePermission )`: checks whether the app has access to camera or not.
8080
- **isPicturePermission** determines whether we're checking permission to take a picture or record a video. Has no effect on iOS
8181

82+
`void NativeCamera.RequestPermissionAsync( PermissionCallback callback, bool isPicturePermission )`: requests permission to access the camera from the user and returns the result asynchronously. It is recommended to show a brief explanation before asking the permission so that user understands why the permission is needed and doesn't click Deny or worse, "Don't ask again". Note that TakePicture and RecordVideo functions call RequestPermissionAsync internally and execute only if the permission is granted.
83+
- **PermissionCallback** takes `NativeCamera.Permission permission` parameter
84+
8285
**NativeCamera.Permission** is an enum that can take 3 values:
8386
- **Granted**: we have the permission to access the camera
84-
- **ShouldAsk**: we don't have permission yet, but we can ask the user for permission via *RequestPermission* function (see below). On Android, as long as the user doesn't select "Don't ask again" while denying the permission, ShouldAsk is returned
87+
- **ShouldAsk**: permission is denied but we can ask the user for permission once again. On Android, as long as the user doesn't select "Don't ask again" while denying the permission, ShouldAsk is returned
8588
- **Denied**: we don't have permission and we can't ask the user for permission. In this case, user has to give the permission from Settings. This happens when user denies the permission on iOS (can't request permission again on iOS), when user selects "Don't ask again" while denying the permission on Android or when user is not allowed to give that permission (parental controls etc.)
8689

87-
`NativeCamera.Permission NativeCamera.RequestPermission( bool isPicturePermission )`: requests permission to access the camera from the user and returns the result. It is recommended to show a brief explanation before asking the permission so that user understands why the permission is needed and doesn't click Deny or worse, "Don't ask again". Note that TakePicture and RecordVideo functions call RequestPermission internally and execute only if the permission is granted (the result of RequestPermission is then returned).
88-
89-
`void NativeCamera.RequestPermissionAsync( PermissionCallback callback, bool isPicturePermission )`: Asynchronous variant of *RequestPermission*. Unlike RequestPermission, this function doesn't freeze the app unnecessarily before the permission dialog is displayed. So it's recommended to call this function instead.
90-
- **PermissionCallback** takes `NativeCamera.Permission permission` parameter
91-
92-
`Task<NativeCamera.Permission> NativeCamera.RequestPermissionAsync( bool isPicturePermission )`: Another asynchronous variant of *RequestPermission* (requires Unity 2018.4 or later).
90+
`Task<NativeCamera.Permission> NativeCamera.RequestPermissionAsync( bool isPicturePermission )`: Task-based overload of *RequestPermissionAsync*.
9391

9492
`NativeCamera.OpenSettings()`: opens the settings for this app, from where the user can manually grant permission in case current permission state is *Permission.Denied* (Android requires *Storage* and, if declared in AndroidManifest, *Camera* permissions; iOS requires *Camera* permission).
9593

96-
`bool NativeCamera.CanOpenSettings()`: on iOS versions prior to 8.0, opening settings from within the app is not possible and in this case, this function returns *false*. Otherwise, it returns *true*.
97-
9894
### C. Utility Functions
9995

10096
`NativeCamera.ImageProperties NativeCamera.GetImageProperties( string imagePath )`: returns an *ImageProperties* instance that holds the width, height, mime type and EXIF orientation information of an image file without creating a *Texture2D* object. Mime type will be *null*, if it can't be determined.
@@ -107,14 +103,14 @@ Beginning with *6.0 Marshmallow*, Android apps must request runtime permissions
107103
- **generateMipmaps** determines whether texture should have mipmaps or not
108104
- **linearColorSpace** determines whether texture should be in linear color space or sRGB color space
109105

110-
`async Task<Texture2D> NativeCamera.LoadImageAtPathAsync( string imagePath, int maxSize = -1, bool markTextureNonReadable = true )`: asynchronous variant of *LoadImageAtPath* (requires Unity 2018.4 or later). Whether or not the returned Texture2D has mipmaps enabled depends on *UnityWebRequestTexture*'s implementation on the target Unity version. Note that it isn't possible to load multiple images simultaneously using this function.
106+
`async Task<Texture2D> NativeCamera.LoadImageAtPathAsync( string imagePath, int maxSize = -1, bool markTextureNonReadable = true )`: asynchronous variant of *LoadImageAtPath*. Whether or not the returned Texture2D has mipmaps enabled depends on *UnityWebRequestTexture*'s implementation on the target Unity version. Note that it isn't possible to load multiple images simultaneously using this function.
111107

112108
`Texture2D NativeCamera.GetVideoThumbnail( string videoPath, int maxSize = -1, double captureTimeInSeconds = -1.0, bool markTextureNonReadable = true, bool generateMipmaps = true, bool linearColorSpace = false )`: creates a Texture2D thumbnail from a video file and returns it. Returns *null*, if something goes wrong.
113109
- **maxSize** determines the maximum size of the returned Texture2D in pixels. Larger thumbnails will be down-scaled. If untouched, its value will be set to *SystemInfo.maxTextureSize*. It is recommended to set a proper maxSize for better performance
114110
- **captureTimeInSeconds** determines the frame of the video that the thumbnail is captured from. If untouched, OS will decide this value
115111
- **markTextureNonReadable** (see *LoadImageAtPath*)
116112

117-
`async Task<Texture2D> NativeCamera.GetVideoThumbnailAsync( string videoPath, int maxSize = -1, double captureTimeInSeconds = -1.0, bool markTextureNonReadable = true )`: asynchronous variant of *GetVideoThumbnail* (requires Unity 2018.4 or later). Whether or not the returned Texture2D has mipmaps enabled depends on *UnityWebRequestTexture*'s implementation on the target Unity version. Note that it isn't possible to generate multiple video thumbnails simultaneously using this function.
113+
`async Task<Texture2D> NativeCamera.GetVideoThumbnailAsync( string videoPath, int maxSize = -1, double captureTimeInSeconds = -1.0, bool markTextureNonReadable = true )`: asynchronous variant of *GetVideoThumbnail*. Whether or not the returned Texture2D has mipmaps enabled depends on *UnityWebRequestTexture*'s implementation on the target Unity version. Note that it isn't possible to generate multiple video thumbnails simultaneously using this function.
118114

119115
## EXAMPLE CODE
120116

@@ -146,17 +142,9 @@ void Update()
146142
}
147143
}
148144

149-
// Example code doesn't use this function but it is here for reference. It's recommended to ask for permissions manually using the
150-
// RequestPermissionAsync methods prior to calling NativeCamera functions
151-
private async void RequestPermissionAsynchronously( bool isPicturePermission )
152-
{
153-
NativeCamera.Permission permission = await NativeCamera.RequestPermissionAsync( isPicturePermission );
154-
Debug.Log( "Permission result: " + permission );
155-
}
156-
157145
private void TakePicture( int maxSize )
158146
{
159-
NativeCamera.Permission permission = NativeCamera.TakePicture( ( path ) =>
147+
NativeCamera.TakePicture( ( path ) =>
160148
{
161149
Debug.Log( "Image path: " + path );
162150
if( path != null )
@@ -188,13 +176,11 @@ private void TakePicture( int maxSize )
188176
Destroy( texture, 5f );
189177
}
190178
}, maxSize );
191-
192-
Debug.Log( "Permission result: " + permission );
193179
}
194180

195181
private void RecordVideo()
196182
{
197-
NativeCamera.Permission permission = NativeCamera.RecordVideo( ( path ) =>
183+
NativeCamera.RecordVideo( ( path ) =>
198184
{
199185
Debug.Log( "Video path: " + path );
200186
if( path != null )
@@ -203,7 +189,5 @@ private void RecordVideo()
203189
Handheld.PlayFullScreenMovie( "file://" + path );
204190
}
205191
} );
206-
207-
Debug.Log( "Permission result: " + permission );
208192
}
209193
```
Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,28 @@
11
#if UNITY_EDITOR || UNITY_ANDROID
2+
using System;
23
using UnityEngine;
34

45
namespace NativeCameraNamespace
56
{
67
public class NCCallbackHelper : MonoBehaviour
78
{
8-
private System.Action mainThreadAction = null;
9+
private bool autoDestroyWithCallback;
10+
private Action mainThreadAction = null;
911

10-
private void Awake()
12+
public static NCCallbackHelper Create( bool autoDestroyWithCallback )
1113
{
12-
DontDestroyOnLoad( gameObject );
14+
NCCallbackHelper result = new GameObject( "NCCallbackHelper" ).AddComponent<NCCallbackHelper>();
15+
result.autoDestroyWithCallback = autoDestroyWithCallback;
16+
DontDestroyOnLoad( result.gameObject );
17+
return result;
18+
}
19+
20+
public void CallOnMainThread( Action function )
21+
{
22+
lock( this )
23+
{
24+
mainThreadAction += function;
25+
}
1326
}
1427

1528
private void Update()
@@ -18,21 +31,22 @@ private void Update()
1831
{
1932
try
2033
{
21-
System.Action temp = mainThreadAction;
22-
mainThreadAction = null;
34+
Action temp;
35+
lock( this )
36+
{
37+
temp = mainThreadAction;
38+
mainThreadAction = null;
39+
}
40+
2341
temp();
2442
}
2543
finally
2644
{
27-
Destroy( gameObject );
45+
if( autoDestroyWithCallback )
46+
Destroy( gameObject );
2847
}
2948
}
3049
}
31-
32-
public void CallOnMainThread( System.Action function )
33-
{
34-
mainThreadAction = function;
35-
}
3650
}
3751
}
3852
#endif

Plugins/NativeCamera/Android/NCCameraCallbackAndroid.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public class NCCameraCallbackAndroid : AndroidJavaProxy
1111
public NCCameraCallbackAndroid( NativeCamera.CameraCallback callback ) : base( "com.yasirkula.unity.NativeCameraMediaReceiver" )
1212
{
1313
this.callback = callback;
14-
callbackHelper = new GameObject( "NCCallbackHelper" ).AddComponent<NCCallbackHelper>();
14+
callbackHelper = NCCallbackHelper.Create( true );
1515
}
1616

1717
[UnityEngine.Scripting.Preserve]

Plugins/NativeCamera/Android/NCPermissionCallbackAndroid.cs

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,17 @@
11
#if UNITY_EDITOR || UNITY_ANDROID
2-
using System.Threading;
32
using UnityEngine;
43

54
namespace NativeCameraNamespace
65
{
76
public class NCPermissionCallbackAndroid : AndroidJavaProxy
8-
{
9-
private object threadLock;
10-
public int Result { get; private set; }
11-
12-
public NCPermissionCallbackAndroid( object threadLock ) : base( "com.yasirkula.unity.NativeCameraPermissionReceiver" )
13-
{
14-
Result = -1;
15-
this.threadLock = threadLock;
16-
}
17-
18-
[UnityEngine.Scripting.Preserve]
19-
public void OnPermissionResult( int result )
20-
{
21-
Result = result;
22-
23-
lock( threadLock )
24-
{
25-
Monitor.Pulse( threadLock );
26-
}
27-
}
28-
}
29-
30-
public class NCPermissionCallbackAsyncAndroid : AndroidJavaProxy
317
{
328
private readonly NativeCamera.PermissionCallback callback;
339
private readonly NCCallbackHelper callbackHelper;
3410

35-
public NCPermissionCallbackAsyncAndroid( NativeCamera.PermissionCallback callback ) : base( "com.yasirkula.unity.NativeCameraPermissionReceiver" )
11+
public NCPermissionCallbackAndroid( NativeCamera.PermissionCallback callback ) : base( "com.yasirkula.unity.NativeCameraPermissionReceiver" )
3612
{
3713
this.callback = callback;
38-
callbackHelper = new GameObject( "NCCallbackHelper" ).AddComponent<NCCallbackHelper>();
14+
callbackHelper = NCCallbackHelper.Create( true );
3915
}
4016

4117
[UnityEngine.Scripting.Preserve]
-60 Bytes
Binary file not shown.

Plugins/NativeCamera/Editor/NCPostProcessBuild.cs

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ public void Save()
4747
File.WriteAllText( SAVE_PATH, JsonUtility.ToJson( this, true ) );
4848
}
4949

50-
#if UNITY_2018_3_OR_NEWER
5150
[SettingsProvider]
5251
public static SettingsProvider CreatePreferencesGUI()
5352
{
@@ -57,11 +56,7 @@ public static SettingsProvider CreatePreferencesGUI()
5756
keywords = new System.Collections.Generic.HashSet<string>() { "Native", "Camera", "Android", "iOS" }
5857
};
5958
}
60-
#endif
6159

62-
#if !UNITY_2018_3_OR_NEWER
63-
[PreferenceItem( "Native Camera" )]
64-
#endif
6560
public static void PreferencesGUI()
6661
{
6762
EditorGUI.BeginChangeCheck();
@@ -95,14 +90,9 @@ public static void OnPostprocessBuild( BuildTarget target, string buildPath )
9590
PBXProject pbxProject = new PBXProject();
9691
pbxProject.ReadFromFile( pbxProjectPath );
9792

98-
#if UNITY_2019_3_OR_NEWER
9993
string targetGUID = pbxProject.GetUnityFrameworkTargetGuid();
100-
#else
101-
string targetGUID = pbxProject.TargetGuidByName( PBXProject.GetUnityTargetName() );
102-
#endif
103-
104-
pbxProject.AddBuildProperty( targetGUID, "OTHER_LDFLAGS", "-framework MobileCoreServices" );
105-
pbxProject.AddBuildProperty( targetGUID, "OTHER_LDFLAGS", "-framework ImageIO" );
94+
pbxProject.AddFrameworkToProject( targetGUID, "MobileCoreServices.framework", false );
95+
pbxProject.AddFrameworkToProject( targetGUID, "ImageIO.framework", false );
10696

10797
File.WriteAllText( pbxProjectPath, pbxProject.WriteToString() );
10898

0 commit comments

Comments
 (0)