File tree

9 files changed

+88
-31
lines changed

9 files changed

+88
-31
lines changed
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,9 @@
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.yasirkula.unity">
33
<uses-sdk android:targetSdkVersion="4" />
44
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" tools:node="replace" />
5+
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="replace" />
6+
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" tools:node="replace" />
7+
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" tools:node="replace" />
8+
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" tools:node="replace" />
59
<application android:requestLegacyExternalStorage="true" />
610
</manifest>
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,21 @@ else if( mediaType == MEDIA_TYPE_VIDEO )
166166

167167
Log.d( "Unity", "Saved media to: " + uri.toString() );
168168

169+
try
170+
{
171+
// Refresh the Gallery. This actually shouldn't have been necessary as ACTION_MEDIA_SCANNER_SCAN_FILE
172+
// is deprecated with the message "Callers should migrate to inserting items directly into MediaStore,
173+
// where they will be automatically scanned after each mutation" but apparently, some phones just don't
174+
// want to abide by the rules, ugh... (see: https://.com/yasirkula/UnityNativeGallery/issues/265)
175+
Intent mediaScanIntent = new Intent( Intent.ACTION_MEDIA_SCANNER_SCAN_FILE );
176+
mediaScanIntent.setData( uri );
177+
context.sendBroadcast( mediaScanIntent );
178+
}
179+
catch( Exception e )
180+
{
181+
Log.e( "Unity", "Exception:", e );
182+
}
183+
169184
String path = NativeGalleryUtils.GetPathFromURI( context, uri );
170185
return path != null && path.length() > 0 ? path : uri.toString();
171186
}
@@ -297,7 +312,7 @@ else if( mediaType == MEDIA_TYPE_VIDEO )
297312

298313
public static void PickMedia( Context context, final NativeGalleryMediaReceiver mediaReceiver, int mediaType, boolean selectMultiple, String savePath, String mime, String title )
299314
{
300-
if( CheckPermission( context, true ) != 1 )
315+
if( CheckPermission( context, true, mediaType ) != 1 )
301316
{
302317
if( !selectMultiple )
303318
mediaReceiver.OnMediaReceived( "" );
@@ -321,28 +336,41 @@ public static void PickMedia( Context context, final NativeGalleryMediaReceiver
321336
}
322337

323338
@TargetApi( Build.VERSION_CODES.M )
324-
public static int CheckPermission( Context context, final boolean readPermission )
339+
public static int CheckPermission( Context context, final boolean readPermission, final int mediaType )
325340
{
326341
if( Build.VERSION.SDK_INT < Build.VERSION_CODES.M )
327342
return 1;
328343

329-
// On Android 10 and later, saving to Gallery doesn't require any permissions
330-
if( !readPermission && android.os.Build.VERSION.SDK_INT >= 29 )
331-
return 1;
332-
333-
if( context.checkSelfPermission( Manifest.permission.READ_EXTERNAL_STORAGE ) == PackageManager.PERMISSION_GRANTED )
344+
if( !readPermission )
334345
{
335-
if( readPermission || context.checkSelfPermission( Manifest.permission.WRITE_EXTERNAL_STORAGE ) == PackageManager.PERMISSION_GRANTED )
346+
if( android.os.Build.VERSION.SDK_INT >= 29 ) // On Android 10 and later, saving to Gallery doesn't require any permissions
336347
return 1;
348+
else if( context.checkSelfPermission( Manifest.permission.WRITE_EXTERNAL_STORAGE ) != PackageManager.PERMISSION_GRANTED )
349+
return 0;
350+
}
351+
352+
if( Build.VERSION.SDK_INT < 33 || context.getApplicationInfo().targetSdkVersion < 33 )
353+
{
354+
if( context.checkSelfPermission( Manifest.permission.READ_EXTERNAL_STORAGE ) != PackageManager.PERMISSION_GRANTED )
355+
return 0;
356+
}
357+
else
358+
{
359+
if( ( mediaType & MEDIA_TYPE_IMAGE ) == MEDIA_TYPE_IMAGE && context.checkSelfPermission( "android.permission.READ_MEDIA_IMAGES" ) != PackageManager.PERMISSION_GRANTED )
360+
return 0;
361+
if( ( mediaType & MEDIA_TYPE_VIDEO ) == MEDIA_TYPE_VIDEO && context.checkSelfPermission( "android.permission.READ_MEDIA_VIDEO" ) != PackageManager.PERMISSION_GRANTED )
362+
return 0;
363+
if( ( mediaType & MEDIA_TYPE_AUDIO ) == MEDIA_TYPE_AUDIO && context.checkSelfPermission( "android.permission.READ_MEDIA_AUDIO" ) != PackageManager.PERMISSION_GRANTED )
364+
return 0;
337365
}
338366

339-
return 0;
367+
return 1;
340368
}
341369

342370
// Credit: https://.com/Over17/UnityAndroidPermissions/blob/0dca33e40628f1f279decb67d901fd444b409cd7/src/UnityAndroidPermissions/src/main/java/com/unity3d/plugin/UnityAndroidPermissions.java
343-
public static void RequestPermission( Context context, final NativeGalleryPermissionReceiver permissionReceiver, final boolean readPermission, final int lastCheckResult )
371+
public static void RequestPermission( Context context, final NativeGalleryPermissionReceiver permissionReceiver, final boolean readPermission, final int mediaType, final int lastCheckResult )
344372
{
345-
if( CheckPermission( context, readPermission ) == 1 )
373+
if( CheckPermission( context, readPermission, mediaType ) == 1 )
346374
{
347375
permissionReceiver.OnPermissionResult( 1 );
348376
return;
@@ -356,6 +384,7 @@ public static void RequestPermission( Context context, final NativeGalleryPermis
356384

357385
Bundle bundle = new Bundle();
358386
bundle.putBoolean( NativeGalleryPermissionFragment.READ_PERMISSION_ONLY, readPermission );
387+
bundle.putInt( NativeGalleryPermissionFragment.MEDIA_TYPE_ID, mediaType );
359388

360389
final Fragment request = new NativeGalleryPermissionFragment( permissionReceiver );
361390
request.setArguments( bundle );
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public void onCreate( Bundle savedInstanceState )
4949
{
5050
super.onCreate( savedInstanceState );
5151
if( mediaReceiver == null )
52-
getFragmentManager().beginTransaction().remove( this ).commit();
52+
onActivityResult( MEDIA_REQUEST_CODE, Activity.RESULT_CANCELED, null );
5353
else
5454
{
5555
int mediaType = getArguments().getInt( MEDIA_TYPE_ID );
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,13 @@
3030
import android.os.Bundle;
3131
import android.util.Log;
3232

33+
import java.util.ArrayList;
34+
3335
@TargetApi( Build.VERSION_CODES.M )
3436
public class NativeGalleryPermissionFragment extends Fragment
3537
{
3638
public static final String READ_PERMISSION_ONLY = "NG_ReadOnly";
39+
public static final String MEDIA_TYPE_ID = "NG_MediaType";
3740
private static final int PERMISSIONS_REQUEST_CODE = 123655;
3841

3942
private final NativeGalleryPermissionReceiver permissionReceiver;
@@ -53,13 +56,30 @@ public void onCreate( Bundle savedInstanceState )
5356
{
5457
super.onCreate( savedInstanceState );
5558
if( permissionReceiver == null )
56-
getFragmentManager().beginTransaction().remove( this ).commit();
59+
onRequestPermissionsResult( PERMISSIONS_REQUEST_CODE, new String[0], new int[0] );
5760
else
5861
{
5962
boolean readPermissionOnly = getArguments().getBoolean( READ_PERMISSION_ONLY );
60-
String[] permissions = readPermissionOnly ? new String[] { Manifest.permission.READ_EXTERNAL_STORAGE } :
61-
new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE };
62-
requestPermissions( permissions, PERMISSIONS_REQUEST_CODE );
63+
if( !readPermissionOnly && Build.VERSION.SDK_INT < 30 )
64+
requestPermissions( new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE }, PERMISSIONS_REQUEST_CODE );
65+
else if( Build.VERSION.SDK_INT < 33 || getActivity().getApplicationInfo().targetSdkVersion < 33 )
66+
requestPermissions( new String[] { Manifest.permission.READ_EXTERNAL_STORAGE }, PERMISSIONS_REQUEST_CODE );
67+
else
68+
{
69+
ArrayList<String> permissions = new ArrayList<String>( 3 );
70+
int mediaType = getArguments().getInt( MEDIA_TYPE_ID );
71+
if( ( mediaType & NativeGallery.MEDIA_TYPE_IMAGE ) == NativeGallery.MEDIA_TYPE_IMAGE )
72+
permissions.add( "android.permission.READ_MEDIA_IMAGES" );
73+
if( ( mediaType & NativeGallery.MEDIA_TYPE_VIDEO ) == NativeGallery.MEDIA_TYPE_VIDEO )
74+
permissions.add( "android.permission.READ_MEDIA_VIDEO" );
75+
if( ( mediaType & NativeGallery.MEDIA_TYPE_AUDIO ) == NativeGallery.MEDIA_TYPE_AUDIO )
76+
permissions.add( "android.permission.READ_MEDIA_AUDIO" );
77+
78+
String[] permissionsArray = new String[permissions.size()];
79+
permissions.toArray( permissionsArray );
80+
81+
requestPermissions( permissionsArray, PERMISSIONS_REQUEST_CODE );
82+
}
6383
}
6484
}
6585

Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ There are 5 ways to install this plugin:
2424

2525
### Android Setup
2626

27-
Set **Write Permission** to **External (SDCard)** in **Player Settings**. You can skip this step if your app won't be saving media to the Gallery but instead just reading media from it.
27+
NativeGallery no longer requires any manual setup on Android.
2828

2929
### iOS Setup
3030

@@ -121,14 +121,15 @@ Beginning with *6.0 Marshmallow*, Android apps must request runtime permissions
121121

122122
There are two functions to handle permissions with this plugin:
123123

124-
`NativeGallery.Permission NativeGallery.CheckPermission( PermissionType permissionType )`: checks whether the app has access to Gallery/Photos or not. **PermissionType** can be either **Read** (for *GetImageFromGallery/GetVideoFromGallery* functions) or **Write** (for *SaveImageToGallery/SaveVideoToGallery* functions).
124+
`NativeGallery.Permission NativeGallery.CheckPermission( PermissionType permissionType, MediaType mediaTypes )`: checks whether the app has access to Gallery/Photos or not. **PermissionType** can be either **Read** (for *GetImageFromGallery/GetVideoFromGallery* functions) or **Write** (for *SaveImageToGallery/SaveVideoToGallery* functions).
125+
- **mediaTypes** determines for which media type(s) we're checking the permission for. Has no effect on iOS
125126

126127
**NativeGallery.Permission** is an enum that can take 3 values:
127128
- **Granted**: we have the permission to access Gallery/Photos
128129
- **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
129130
- **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.)
130131

131-
`NativeGallery.Permission NativeGallery.RequestPermission( PermissionType permissionType )`: requests permission to access Gallery/Photos 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 the SaveImageToGallery/SaveVideoToGallery and GetImageFromGallery/GetVideoFromGallery functions call RequestPermission internally and execute only if the permission is granted (the result of RequestPermission is also returned).
132+
`NativeGallery.Permission NativeGallery.RequestPermission( PermissionType permissionType, MediaType mediaTypes )`: requests permission to access Gallery/Photos 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 the SaveImageToGallery/SaveVideoToGallery and GetImageFromGallery/GetVideoFromGallery functions call RequestPermission internally and execute only if the permission is granted (the result of RequestPermission is also returned).
132133

133134
`NativeGallery.OpenSettings()`: opens the settings for this app, from where the user can manually grant permission in case current permission state is *Permission.Denied* (on Android, the necessary permission is named *Storage* and on iOS, the necessary permission is named *Photos*).
134135

Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,10 @@ private static string SelectedMediaPath
177177
// provided custom album
178178
private const bool PermissionFreeMode = true;
179179

180-
public static Permission CheckPermission( PermissionType permissionType )
180+
public static Permission CheckPermission( PermissionType permissionType, MediaType mediaTypes )
181181
{
182182
#if !UNITY_EDITOR && UNITY_ANDROID
183-
Permission result = (Permission) AJC.CallStatic<int>( "CheckPermission", Context, permissionType == PermissionType.Read );
183+
Permission result = (Permission) AJC.CallStatic<int>( "CheckPermission", Context, permissionType == PermissionType.Read, (int) mediaTypes );
184184
if( result == Permission.Denied && (Permission) PlayerPrefs.GetInt( "NativeGalleryPermission", (int) Permission.ShouldAsk ) == Permission.ShouldAsk )
185185
result = Permission.ShouldAsk;
186186

@@ -194,15 +194,15 @@ public static Permission CheckPermission( PermissionType permissionType )
194194
#endif
195195
}
196196

197-
public static Permission RequestPermission( PermissionType permissionType )
197+
public static Permission RequestPermission( PermissionType permissionType, MediaType mediaTypes )
198198
{
199199
#if !UNITY_EDITOR && UNITY_ANDROID
200200
object threadLock = new object();
201201
lock( threadLock )
202202
{
203203
NGPermissionCallbackAndroid nativeCallback = new NGPermissionCallbackAndroid( threadLock );
204204

205-
AJC.CallStatic( "RequestPermission", Context, nativeCallback, permissionType == PermissionType.Read, PlayerPrefs.GetInt( "NativeGalleryPermission", (int) Permission.ShouldAsk ) );
205+
AJC.CallStatic( "RequestPermission", Context, nativeCallback, permissionType == PermissionType.Read, (int) mediaTypes, PlayerPrefs.GetInt( "NativeGalleryPermission", (int) Permission.ShouldAsk ) );
206206

207207
if( nativeCallback.Result == -1 )
208208
System.Threading.Monitor.Wait( threadLock );
@@ -423,7 +423,7 @@ public static MediaType GetMediaTypeOfFile( string path )
423423
#region Internal Functions
424424
private static Permission SaveToGallery( byte[] mediaBytes, string album, string filename, MediaType mediaType, MediaSaveCallback callback )
425425
{
426-
Permission result = RequestPermission( PermissionType.Write );
426+
Permission result = RequestPermission( PermissionType.Write, mediaType );
427427
if( result == Permission.Granted )
428428
{
429429
if( mediaBytes == null || mediaBytes.Length == 0 )
@@ -453,7 +453,7 @@ private static Permission SaveToGallery( byte[] mediaBytes, string album, string
453453

454454
private static Permission SaveToGallery( string existingMediaPath, string album, string filename, MediaType mediaType, MediaSaveCallback callback )
455455
{
456-
Permission result = RequestPermission( PermissionType.Write );
456+
Permission result = RequestPermission( PermissionType.Write, mediaType );
457457
if( result == Permission.Granted )
458458
{
459459
if( !File.Exists( existingMediaPath ) )
@@ -552,7 +552,7 @@ private static string GetTemporarySavePath( string filename )
552552

553553
private static Permission GetMediaFromGallery( MediaPickCallback callback, MediaType mediaType, string mime, string title )
554554
{
555-
Permission result = RequestPermission( PermissionType.Read );
555+
Permission result = RequestPermission( PermissionType.Read, mediaType );
556556
if( result == Permission.Granted && !IsMediaPickerBusy() )
557557
{
558558
#if UNITY_EDITOR
@@ -609,7 +609,7 @@ private static Permission GetMediaFromGallery( MediaPickCallback callback, Media
609609

610610
private static Permission GetMultipleMediaFromGallery( MediaPickMultipleCallback callback, MediaType mediaType, string mime, string title )
611611
{
612-
Permission result = RequestPermission( PermissionType.Read );
612+
Permission result = RequestPermission( PermissionType.Read, mediaType );
613613
if( result == Permission.Granted && !IsMediaPickerBusy() )
614614
{
615615
if( CanSelectMultipleFilesFromGallery() )
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
= Native Gallery for Android & iOS (v1.7.0) =
1+
= Native Gallery for Android & iOS (v1.7.1) =
22

33
Online documentation & example code available at: https://.com/yasirkula/UnityNativeGallery
44
@@ -109,8 +109,9 @@ bool NativeGallery.IsMediaPickerBusy();
109109
//// Runtime Permissions ////
110110

111111
// Interacting with Gallery/Photos is only possible when permission state is Permission.Granted. Most of the functions request permission internally (and return the result) but you can also check/request the permissions manually
112-
NativeGallery.Permission NativeGallery.CheckPermission( PermissionType permissionType );
113-
NativeGallery.Permission NativeGallery.RequestPermission( PermissionType permissionType );
112+
// mediaTypes: for which media type(s) we're checking the permission for. Has no effect on iOS
113+
NativeGallery.Permission NativeGallery.CheckPermission( PermissionType permissionType, MediaType mediaTypes );
114+
NativeGallery.Permission NativeGallery.RequestPermission( PermissionType permissionType, MediaType mediaTypes );
114115

115116
// If permission state is Permission.Denied, user must grant the necessary permission (Storage on Android and Photos on iOS) manually from the Settings. These functions help you open the Settings directly from within the app
116117
void NativeGallery.OpenSettings();
@@ -131,6 +132,8 @@ async Task<Texture2D> NativeGallery.LoadImageAtPathAsync( string imagePath, int
131132
// 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
132133
// captureTimeInSeconds: determines the frame of the video that the thumbnail is captured from. If untouched, OS will decide this value
133134
// markTextureNonReadable: see LoadImageAtPath
135+
// generateMipmaps: see LoadImageAtPath
136+
// linearColorSpace: see LoadImageAtPath
134137
Texture2D NativeGallery.GetVideoThumbnail( string videoPath, int maxSize = -1, double captureTimeInSeconds = -1.0, bool markTextureNonReadable = true, bool generateMipmaps = true, bool linearColorSpace = false );
135138
async Task<Texture2D> NativeGallery.GetVideoThumbnailAsync( string videoPath, int maxSize = -1, double captureTimeInSeconds = -1.0, bool markTextureNonReadable = true, bool generateMipmaps = true, bool linearColorSpace = false );
136139

Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "com.yasirkula.nativegallery",
33
"displayName": "Native Gallery",
4-
"version": "1.7.0",
4+
"version": "1.7.1",
55
"documentationUrl": "https://.com/yasirkula/UnityNativeGallery",
66
"changelogUrl": "https://.com/yasirkula/UnityNativeGallery/releases",
77
"licensesUrl": "https://.com/yasirkula/UnityNativeGallery/blob/master/LICENSE.txt",

0 commit comments

Comments
 (0)