Skip to content

Commit 8bb2dbd

Browse files
committed
On Android 33+, it's now possible to preserve access to the picked images after the app is restarted but it isn't recommended (see the relevant FAQ entry)(#328)
1 parent 66157bd commit 8bb2dbd

File tree

6 files changed

+42
-7
lines changed

6 files changed

+42
-7
lines changed

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import android.app.Activity;
44
import android.app.Fragment;
55
import android.content.ActivityNotFoundException;
6+
import android.content.Context;
67
import android.content.Intent;
78
import android.content.pm.PackageManager;
89
import android.os.Build;
@@ -29,6 +30,7 @@ public class NativeGalleryMediaPickerFragment extends Fragment
2930
public static boolean tryPreserveFilenames = false; // When enabled, app's cache will fill more quickly since most of the images will have a unique filename (less chance of overwriting old files)
3031
public static boolean showProgressbar = true; // When enabled, a progressbar will be displayed while selected file(s) are copied (if necessary) to the destination directory
3132
public static boolean useDefaultGalleryApp = false; // false: Intent.createChooser is used to pick the Gallery app
33+
public static boolean GrantPersistableUriPermission = false; // When enabled, on newest Android versions, picked file can still be accessed after the app is restarted. Note that there's a 512-file hard limit: https://issuetracker.google.com/issues/149315521#comment7
3234

3335
private final NativeGalleryMediaReceiver mediaReceiver;
3436
private boolean selectMultiple;
@@ -49,7 +51,10 @@ public void onCreate( Bundle savedInstanceState )
4951
{
5052
super.onCreate( savedInstanceState );
5153
if( mediaReceiver == null )
54+
{
55+
Log.e( "Unity", "NativeGalleryMediaPickerFragment.mediaReceiver became null in onCreate!" );
5256
onActivityResult( MEDIA_REQUEST_CODE, Activity.RESULT_CANCELED, null );
57+
}
5358
else
5459
{
5560
int mediaType = getArguments().getInt( MEDIA_TYPE_ID );
@@ -114,6 +119,9 @@ else if( mediaType == NativeGallery.MEDIA_TYPE_VIDEO )
114119
intent.setType( mime );
115120
}
116121

122+
if( ShouldGrantPersistableUriPermission( getActivity() ) )
123+
intent.addFlags( Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION );
124+
117125
if( title != null && title.length() > 0 )
118126
intent.putExtra( Intent.EXTRA_TITLE, title );
119127

@@ -133,6 +141,11 @@ else if( mediaType == NativeGallery.MEDIA_TYPE_VIDEO )
133141
}
134142
}
135143

144+
public static boolean ShouldGrantPersistableUriPermission( Context context )
145+
{
146+
return GrantPersistableUriPermission && Build.VERSION.SDK_INT >= 33 && context.getApplicationInfo().targetSdkVersion >= 33;
147+
}
148+
136149
@Override
137150
public void onActivityResult( int requestCode, int resultCode, Intent data )
138151
{
@@ -142,7 +155,7 @@ public void onActivityResult( int requestCode, int resultCode, Intent data )
142155
NativeGalleryMediaPickerResultFragment resultFragment = null;
143156

144157
if( mediaReceiver == null )
145-
Log.d( "Unity", "NativeGalleryMediaPickerFragment.mediaReceiver became null!" );
158+
Log.d( "Unity", "NativeGalleryMediaPickerFragment.mediaReceiver became null in onActivityResult!" );
146159
else if( resultCode != Activity.RESULT_OK || data == null )
147160
{
148161
if( !selectMultiple )

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

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import android.content.Intent;
66
import android.database.Cursor;
77
import android.net.Uri;
8+
import android.os.Build;
89
import android.provider.OpenableColumns;
910
import android.util.Log;
1011
import android.webkit.MimeTypeMap;
@@ -50,9 +51,11 @@ public void execute()
5051
{
5152
if( !selectMultiple || data.getClipData() == null )
5253
{
53-
unityResult = getPathFromURI( data.getData() );
54-
if( unityResult == null || ( unityResult.length() > 0 && !( new File( unityResult ).exists() ) ) )
55-
unityResult = "";
54+
String _unityResult = getPathFromURI( data.getData() );
55+
if( _unityResult != null && _unityResult.length() > 0 && new File( _unityResult ).exists() )
56+
unityResult = _unityResult;
57+
58+
Log.d( "Unity", "NativeGalleryMediaPickerResultOperation: " + _unityResult );
5659
}
5760
else
5861
{
@@ -73,6 +76,8 @@ public void execute()
7376
else
7477
unityResult += ">" + _unityResult;
7578
}
79+
80+
Log.d( "Unity", "NativeGalleryMediaPickerResultOperation: " + _unityResult );
7681
}
7782
}
7883
}
@@ -106,7 +111,7 @@ public void sendResultToUnity()
106111
sentResult = true;
107112

108113
if( mediaReceiver == null )
109-
Log.d( "Unity", "NativeGalleryMediaPickerResultOperation.mediaReceiver became null!" );
114+
Log.d( "Unity", "NativeGalleryMediaPickerResultOperation.mediaReceiver became null in sendResultToUnity!" );
110115
else
111116
{
112117
if( selectMultiple )
@@ -133,10 +138,14 @@ private String getPathFromURI( Uri uri )
133138
inputStream = new FileInputStream( new File( path ) );
134139
inputStream.read();
135140

141+
if( NativeGalleryMediaPickerFragment.ShouldGrantPersistableUriPermission( context ) )
142+
context.getContentResolver().takePersistableUriPermission( uri, Intent.FLAG_GRANT_READ_URI_PERMISSION );
143+
136144
return path;
137145
}
138146
catch( Exception e )
139147
{
148+
Log.e( "Unity", "Media uri isn't accessible via File API: " + uri, e );
140149
}
141150
finally
142151
{
@@ -214,7 +223,10 @@ else if( filename.endsWith( extension ) )
214223
{
215224
InputStream input = resolver.openInputStream( uri );
216225
if( input == null )
226+
{
227+
Log.w( "Unity", "Couldn't open input stream: " + uri );
217228
return null;
229+
}
218230

219231
if( fileSize < 0 )
220232
{
@@ -289,6 +301,7 @@ else if( fileSize > 0 )
289301
savedFiles.add( fullName );
290302
}
291303

304+
Log.d( "Unity", "Copied media from " + uri + " to: " + tempFile.getAbsolutePath() );
292305
return tempFile.getAbsolutePath();
293306
}
294307
finally

.github/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,15 @@ Only Android & iOS platforms are supported. Editor functionality is for preview
5555

5656
If you are sure that your plugin is up-to-date, then enable **Custom Proguard File** option from *Player Settings* and add the following line to that file: `-keep class com.yasirkula.unity.* { *; }`
5757

58+
- **I save the picked image's path for later use but I can no longer access it after restarting the app**
59+
60+
This happens on Android 33+ devices when *Target API Level* is set to 33 or later. The recommended solution is to copy the image to [persistentDataPath](https://docs.unity3d.com/ScriptReference/Application-persistentDataPath.html) because otherwise, the source image can be deleted by the user from Gallery, deleted by the operating system to free up space or overwritten by NativeGallery in the next *PickImage* call (if NativeGallery can't determine the image's source file path, then it copies the picked image to a fixed location in temporaryCachePath and thus, the image can easily be overwritten). If you still would like to persist access to the image, you can call the following code in your *Awake* function but be aware that this fix has a [hard limit of 512 files](https://issuetracker.google.com/issues/149315521#comment7):
61+
62+
#if !UNITY_EDITOR && UNITY_ANDROID
63+
using( AndroidJavaClass ajc = new AndroidJavaClass( "com.yasirkula.unity.NativeGalleryMediaPickerFragment" ) )
64+
ajc.SetStatic<bool>( "GrantPersistableUriPermission", true );
65+
#endif
66+
5867
- **Android build fails, it says "error: attribute android:requestLegacyExternalStorage not found" in Console**
5968

6069
`android:requestLegacyExternalStorage` attribute in _AndroidManifest.xml_ fixes a rare UnauthorizedAccessException on Android 10 but requires you to update your Android SDK to at least **SDK 29**. If this isn't possible for you, you should open *NativeGallery.aar* with WinRAR or 7-Zip and then remove the `<application ... />` tag from _AndroidManifest.xml_.
545 Bytes
Binary file not shown.

Plugins/NativeGallery/README.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
= Native Gallery for Android & iOS (v1.8.0) =
1+
= Native Gallery for Android & iOS (v1.8.1) =
22

33
Documentation: https://github.com/yasirkula/UnityNativeGallery
44
FAQ: https://github.com/yasirkula/UnityNativeGallery#faq

package.json

Lines changed: 1 addition & 1 deletion
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.8.0",
4+
"version": "1.8.1",
55
"documentationUrl": "https://github.com/yasirkula/UnityNativeGallery",
66
"changelogUrl": "https://github.com/yasirkula/UnityNativeGallery/releases",
77
"licensesUrl": "https://github.com/yasirkula/UnityNativeGallery/blob/master/LICENSE.txt",

0 commit comments

Comments
 (0)