├── .github
├── AAR Source (Android)
│ ├── proguard.txt
│ ├── AndroidManifest.xml
│ └── java
│ │ └── com
│ │ └── yasirkula
│ │ └── unity
│ │ ├── RuntimePermissionsReceiver.java
│ │ ├── RuntimePermissions.java
│ │ └── RuntimePermissionsFragment.java
├── Images
│ └── permission.png
└── README.md
├── Plugins
├── AndroidRuntimePermissions
│ ├── AndroidRuntimePermissions.Runtime.asmdef
│ ├── CREDITS.txt
│ ├── Android
│ │ ├── RuntimePermissions.aar
│ │ ├── PermissionCallback.cs.meta
│ │ ├── PermissionCallbackHelper.cs.meta
│ │ ├── RuntimePermissions.aar.meta
│ │ ├── PermissionCallback.cs
│ │ └── PermissionCallbackHelper.cs
│ ├── CREDITS.txt.meta
│ ├── README.txt.meta
│ ├── AndroidRuntimePermissions.Runtime.asmdef.meta
│ ├── Android.meta
│ ├── README.txt
│ ├── AndroidRuntimePermissions.cs.meta
│ └── AndroidRuntimePermissions.cs
└── AndroidRuntimePermissions.meta
├── LICENSE.txt.meta
├── package.json.meta
├── Plugins.meta
├── package.json
└── LICENSE.txt
/.github/AAR Source (Android)/proguard.txt:
--------------------------------------------------------------------------------
1 | -keep class com.yasirkula.unity.* { *; }
--------------------------------------------------------------------------------
/.github/Images/permission.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yasirkula/UnityAndroidRuntimePermissions/HEAD/.github/Images/permission.png
--------------------------------------------------------------------------------
/Plugins/AndroidRuntimePermissions/AndroidRuntimePermissions.Runtime.asmdef:
--------------------------------------------------------------------------------
1 | {
2 | "name": "AndroidRuntimePermissions.Runtime"
3 | }
4 |
--------------------------------------------------------------------------------
/Plugins/AndroidRuntimePermissions/CREDITS.txt:
--------------------------------------------------------------------------------
1 | Based on UnityAndroidPermissions (MIT License): https://github.com/Over17/UnityAndroidPermissions
--------------------------------------------------------------------------------
/LICENSE.txt.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 3164c62e3fcedf14bbed0d4fe67a7ed6
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Plugins/AndroidRuntimePermissions/Android/RuntimePermissions.aar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yasirkula/UnityAndroidRuntimePermissions/HEAD/Plugins/AndroidRuntimePermissions/Android/RuntimePermissions.aar
--------------------------------------------------------------------------------
/package.json.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 6c93c93f557980c45a8d42ea83dd9b9b
3 | PackageManifestImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Plugins.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 4cee5170601157240b10df1cd0f607dd
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/.github/AAR Source (Android)/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/Plugins/AndroidRuntimePermissions/CREDITS.txt.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 2202d107a7d75364fb19d25ebc3d3ac2
3 | timeCreated: 1524849779
4 | licenseType: Free
5 | TextScriptImporter:
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Plugins/AndroidRuntimePermissions/README.txt.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: d9549e84fd9f666448ee5c58fb1d65fe
3 | timeCreated: 1563308328
4 | licenseType: Free
5 | TextScriptImporter:
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Plugins/AndroidRuntimePermissions/AndroidRuntimePermissions.Runtime.asmdef.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 3caf90d9f0ecf444e865b5fbaa0930af
3 | AssemblyDefinitionImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Plugins/AndroidRuntimePermissions.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: d1bd251f257fab948ba66f0d4cb79288
3 | folderAsset: yes
4 | timeCreated: 1524827386
5 | licenseType: Free
6 | DefaultImporter:
7 | userData:
8 | assetBundleName:
9 | assetBundleVariant:
10 |
--------------------------------------------------------------------------------
/Plugins/AndroidRuntimePermissions/Android.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e3eebd3e44e6565489b8ab06cf1260ba
3 | folderAsset: yes
4 | timeCreated: 1524819254
5 | licenseType: Free
6 | DefaultImporter:
7 | userData:
8 | assetBundleName:
9 | assetBundleVariant:
10 |
--------------------------------------------------------------------------------
/Plugins/AndroidRuntimePermissions/README.txt:
--------------------------------------------------------------------------------
1 | = Android Runtime Permissions (v1.3.0) =
2 |
3 | Documentation: https://github.com/yasirkula/UnityAndroidRuntimePermissions
4 | Example code: https://github.com/yasirkula/UnityAndroidRuntimePermissions#example-code
5 | E-mail: yasirkula@gmail.com
--------------------------------------------------------------------------------
/.github/AAR Source (Android)/java/com/yasirkula/unity/RuntimePermissionsReceiver.java:
--------------------------------------------------------------------------------
1 | package com.yasirkula.unity;
2 |
3 | /**
4 | * Created by yasirkula on 27.04.2018.
5 | */
6 |
7 | public interface RuntimePermissionsReceiver
8 | {
9 | void OnPermissionResult( String result );
10 | }
11 |
--------------------------------------------------------------------------------
/Plugins/AndroidRuntimePermissions/Android/PermissionCallback.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: c4c92d9c498c2be4d94206c1e8bcd6dc
3 | timeCreated: 1524828480
4 | licenseType: Free
5 | MonoImporter:
6 | serializedVersion: 2
7 | defaultReferences: []
8 | executionOrder: 0
9 | icon: {instanceID: 0}
10 | userData:
11 | assetBundleName:
12 | assetBundleVariant:
13 |
--------------------------------------------------------------------------------
/Plugins/AndroidRuntimePermissions/AndroidRuntimePermissions.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 3036d14754ed8b34f8986bc63dc87919
3 | timeCreated: 1524819255
4 | licenseType: Free
5 | MonoImporter:
6 | serializedVersion: 2
7 | defaultReferences: []
8 | executionOrder: 0
9 | icon: {instanceID: 0}
10 | userData:
11 | assetBundleName:
12 | assetBundleVariant:
13 |
--------------------------------------------------------------------------------
/Plugins/AndroidRuntimePermissions/Android/PermissionCallbackHelper.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: ee734852915f4f64a9b8cad60e3c3a53
3 | timeCreated: 1524849960
4 | licenseType: Free
5 | MonoImporter:
6 | serializedVersion: 2
7 | defaultReferences: []
8 | executionOrder: 0
9 | icon: {instanceID: 0}
10 | userData:
11 | assetBundleName:
12 | assetBundleVariant:
13 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "com.yasirkula.androidruntimepermissions",
3 | "displayName": "Android Runtime Permissions",
4 | "version": "1.3.0",
5 | "documentationUrl": "https://github.com/yasirkula/UnityAndroidRuntimePermissions",
6 | "changelogUrl": "https://github.com/yasirkula/UnityAndroidRuntimePermissions/releases",
7 | "licensesUrl": "https://github.com/yasirkula/UnityAndroidRuntimePermissions/blob/master/LICENSE.txt",
8 | "description": "This plugin helps you query/request runtime permissions synchronously on Android M and later. It also works on older Android versions and detects whether a requested permission is declared in AndroidManifest or not."
9 | }
10 |
--------------------------------------------------------------------------------
/Plugins/AndroidRuntimePermissions/Android/RuntimePermissions.aar.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 608c66f59923654438a70686e77edf60
3 | timeCreated: 1618256408
4 | licenseType: Free
5 | PluginImporter:
6 | serializedVersion: 2
7 | iconMap: {}
8 | executionOrder: {}
9 | isPreloaded: 0
10 | isOverridable: 0
11 | platformData:
12 | data:
13 | first:
14 | Android: Android
15 | second:
16 | enabled: 1
17 | settings: {}
18 | data:
19 | first:
20 | Any:
21 | second:
22 | enabled: 0
23 | settings: {}
24 | data:
25 | first:
26 | Editor: Editor
27 | second:
28 | enabled: 0
29 | settings:
30 | DefaultValueInitialized: true
31 | userData:
32 | assetBundleName:
33 | assetBundleVariant:
34 |
--------------------------------------------------------------------------------
/Plugins/AndroidRuntimePermissions/Android/PermissionCallback.cs:
--------------------------------------------------------------------------------
1 | #if UNITY_EDITOR || UNITY_ANDROID
2 | using System;
3 | using UnityEngine;
4 |
5 | namespace AndroidRuntimePermissionsNamespace
6 | {
7 | public class PermissionCallback : AndroidJavaProxy
8 | {
9 | private readonly string[] permissions;
10 | private readonly Action callback;
11 | private readonly PermissionCallbackHelper callbackHelper;
12 |
13 | internal PermissionCallback( string[] permissions, Action callback ) : base( "com.yasirkula.unity.RuntimePermissionsReceiver" )
14 | {
15 | this.permissions = permissions;
16 | this.callback = callback;
17 | callbackHelper = PermissionCallbackHelper.Create( true );
18 | }
19 |
20 | [UnityEngine.Scripting.Preserve]
21 | public void OnPermissionResult( string result )
22 | {
23 | callbackHelper.CallOnMainThread( () => callback( AndroidRuntimePermissions.ProcessPermissionRequestResult( permissions, result ) ) );
24 | }
25 | }
26 | }
27 | #endif
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Süleyman Yasir KULA
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Plugins/AndroidRuntimePermissions/Android/PermissionCallbackHelper.cs:
--------------------------------------------------------------------------------
1 | #if UNITY_EDITOR || UNITY_ANDROID
2 | using System;
3 | using UnityEngine;
4 |
5 | namespace AndroidRuntimePermissionsNamespace
6 | {
7 | public class PermissionCallbackHelper : MonoBehaviour
8 | {
9 | private bool autoDestroyWithCallback;
10 | private Action mainThreadAction = null;
11 |
12 | public static PermissionCallbackHelper Create( bool autoDestroyWithCallback )
13 | {
14 | PermissionCallbackHelper result = new GameObject( "PermissionCallbackHelper" ).AddComponent();
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 | }
26 | }
27 |
28 | private void Update()
29 | {
30 | if( mainThreadAction != null )
31 | {
32 | try
33 | {
34 | Action temp;
35 | lock( this )
36 | {
37 | temp = mainThreadAction;
38 | mainThreadAction = null;
39 | }
40 |
41 | temp();
42 | }
43 | finally
44 | {
45 | if( autoDestroyWithCallback )
46 | Destroy( gameObject );
47 | }
48 | }
49 | }
50 | }
51 | }
52 | #endif
--------------------------------------------------------------------------------
/.github/AAR Source (Android)/java/com/yasirkula/unity/RuntimePermissions.java:
--------------------------------------------------------------------------------
1 | package com.yasirkula.unity;
2 |
3 | import android.annotation.TargetApi;
4 | import android.app.Activity;
5 | import android.app.Fragment;
6 | import android.content.Context;
7 | import android.content.Intent;
8 | import android.content.pm.PackageManager;
9 | import android.net.Uri;
10 | import android.os.Build;
11 | import android.os.Bundle;
12 | import android.provider.Settings;
13 |
14 | import java.util.ArrayList;
15 |
16 | /**
17 | * Created by yasirkula on 27.04.2018.
18 | * Based on UnityAndroidPermissions (MIT License): https://github.com/Over17/UnityAndroidPermissions
19 | */
20 |
21 | public class RuntimePermissions
22 | {
23 | // Credit: https://stackoverflow.com/a/35456817/2373034
24 | public static void OpenSettings( final Context context )
25 | {
26 | Uri uri = Uri.fromParts( "package", context.getPackageName(), null );
27 |
28 | Intent intent = new Intent();
29 | intent.setAction( Settings.ACTION_APPLICATION_DETAILS_SETTINGS );
30 | intent.setData( uri );
31 |
32 | context.startActivity( intent );
33 | }
34 |
35 | @TargetApi( Build.VERSION_CODES.M )
36 | public static String CheckPermission( final String[] permissions, final Context context )
37 | {
38 | String result = "";
39 | if( Build.VERSION.SDK_INT < Build.VERSION_CODES.M )
40 | {
41 | ArrayList permissionsResult = new ArrayList( permissions.length );
42 | for( int i = 0; i < permissions.length; i++ )
43 | permissionsResult.add( 0 );
44 |
45 | try
46 | {
47 | String[] requestedPermissions = context.getPackageManager().getPackageInfo( context.getPackageName(), PackageManager.GET_PERMISSIONS ).requestedPermissions;
48 | if( requestedPermissions != null )
49 | {
50 | int remainingPermissions = permissions.length;
51 | for( String requestedPermission : requestedPermissions )
52 | {
53 | int i = 0;
54 | while( i < permissions.length && !permissions[i].equals( requestedPermission ) )
55 | i++;
56 |
57 | if( i < permissions.length )
58 | {
59 | permissionsResult.set( i, 1 );
60 |
61 | if( --remainingPermissions <= 0 )
62 | break;
63 | }
64 | }
65 | }
66 | }
67 | catch( PackageManager.NameNotFoundException e )
68 | {
69 | }
70 |
71 | for( int i = 0; i < permissionsResult.size(); i++ )
72 | result += permissionsResult.get( i );
73 | }
74 | else
75 | {
76 | for( int i = 0; i < permissions.length; i++ )
77 | result += context.checkSelfPermission( permissions[i] ) == PackageManager.PERMISSION_GRANTED ? '1' : '0';
78 | }
79 |
80 | return result;
81 | }
82 |
83 | public static void RequestPermission( final String[] permissions, final Context context, final RuntimePermissionsReceiver permissionReceiver )
84 | {
85 | String permissionResult = CheckPermission( permissions, context );
86 | if( Build.VERSION.SDK_INT < Build.VERSION_CODES.M )
87 | {
88 | permissionReceiver.OnPermissionResult( permissionResult );
89 | return;
90 | }
91 |
92 | boolean shouldShowPermissionDialog = false;
93 | for( int i = 0; i < permissions.length; i++ )
94 | {
95 | if( permissionResult.charAt( i ) == '0' )
96 | {
97 | shouldShowPermissionDialog = true;
98 | break;
99 | }
100 | }
101 |
102 | if( !shouldShowPermissionDialog )
103 | permissionReceiver.OnPermissionResult( permissionResult );
104 | else
105 | {
106 | Bundle bundle = new Bundle();
107 | bundle.putStringArray( RuntimePermissionsFragment.PERMISSIONS, permissions );
108 |
109 | final Fragment request = new RuntimePermissionsFragment( permissionReceiver );
110 | request.setArguments( bundle );
111 |
112 | ( (Activity) context ).getFragmentManager().beginTransaction().add( 0, request ).commitAllowingStateLoss();
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/.github/AAR Source (Android)/java/com/yasirkula/unity/RuntimePermissionsFragment.java:
--------------------------------------------------------------------------------
1 | package com.yasirkula.unity;
2 |
3 | import android.annotation.TargetApi;
4 | import android.app.Fragment;
5 | import android.content.Intent;
6 | import android.content.pm.PackageManager;
7 | import android.os.Build;
8 | import android.os.Bundle;
9 | import android.util.Log;
10 |
11 | import java.util.ArrayList;
12 |
13 | /**
14 | * Created by yasirkula on 27.04.2018.
15 | * Based on UnityAndroidPermissions (MIT License): https://github.com/Over17/UnityAndroidPermissions
16 | */
17 |
18 | @TargetApi( Build.VERSION_CODES.M )
19 | public class RuntimePermissionsFragment extends Fragment
20 | {
21 | public static final String PERMISSIONS = "RTP_Permissions";
22 | private static final int PERMISSIONS_REQUEST_CODE = 122655;
23 |
24 | private final RuntimePermissionsReceiver permissionReceiver;
25 | private String[] m_permissions;
26 |
27 | public RuntimePermissionsFragment()
28 | {
29 | permissionReceiver = null;
30 | }
31 |
32 | public RuntimePermissionsFragment( final RuntimePermissionsReceiver permissionReceiver )
33 | {
34 | this.permissionReceiver = permissionReceiver;
35 | }
36 |
37 | @Override
38 | public void onCreate( Bundle savedInstanceState )
39 | {
40 | super.onCreate( savedInstanceState );
41 | if( permissionReceiver == null )
42 | onRequestPermissionsResult( PERMISSIONS_REQUEST_CODE, new String[0], new int[0] );
43 | else
44 | {
45 | m_permissions = getArguments().getStringArray( PERMISSIONS );
46 | if( m_permissions != null )
47 | requestPermissions( m_permissions, PERMISSIONS_REQUEST_CODE );
48 | }
49 | }
50 |
51 | @Override
52 | public void onRequestPermissionsResult( int requestCode, String[] permissions, int[] grantResults )
53 | {
54 | if( requestCode != PERMISSIONS_REQUEST_CODE )
55 | return;
56 |
57 | if( permissionReceiver == null || m_permissions == null )
58 | {
59 | Log.e( "Unity", "Fragment data got reset while asking permissions!" );
60 |
61 | getFragmentManager().beginTransaction().remove( this ).commitAllowingStateLoss();
62 | return;
63 | }
64 |
65 | int resolvedPermissionCount = 0;
66 | ArrayList permissionsResult = new ArrayList( m_permissions.length );
67 | for( int i = 0; i < m_permissions.length; i++ )
68 | permissionsResult.add( 2 );
69 |
70 | // 0 -> denied, must go to settings
71 | // 1 -> granted
72 | // 2 -> denied, can ask again
73 | for( int i = 0; i < permissions.length && i < grantResults.length; i++ )
74 | {
75 | String permission = permissions[i];
76 | int permissionIndex = -1;
77 | for( int j = 0; j < m_permissions.length && permissionIndex == -1; j++ )
78 | {
79 | if( permission.equals( m_permissions[j] ) )
80 | permissionIndex = j;
81 | }
82 |
83 | if( permissionIndex == -1 )
84 | {
85 | Log.w( "Unity", "Didn't request permission for: " + permission );
86 | continue;
87 | }
88 | else
89 | resolvedPermissionCount++;
90 |
91 | try
92 | {
93 | if( grantResults[i] == PackageManager.PERMISSION_GRANTED )
94 | permissionsResult.set( permissionIndex, 1 );
95 | else if( !shouldShowRequestPermissionRationale( permission ) ) // This can cause IllegalArgumentException for undocumented permissions
96 | permissionsResult.set( permissionIndex, 0 );
97 | else
98 | permissionsResult.set( permissionIndex, 2 );
99 | }
100 | catch( Exception e )
101 | {
102 | Log.e( "Unity", "Exception:", e );
103 | permissionsResult.set( permissionIndex, 0 );
104 | }
105 | }
106 |
107 | if( resolvedPermissionCount != m_permissions.length )
108 | Log.e( "Unity", "Missed some permissions!" );
109 |
110 | String result = "";
111 | for( int i = 0; i < permissionsResult.size(); i++ )
112 | result += permissionsResult.get( i );
113 |
114 | permissionReceiver.OnPermissionResult( result );
115 | getFragmentManager().beginTransaction().remove( this ).commitAllowingStateLoss();
116 |
117 | // Resolves a bug in Unity 2019 where the calling activity
118 | // doesn't resume automatically after the fragment finishes
119 | // Credit: https://stackoverflow.com/a/12409215/2373034
120 | try
121 | {
122 | Intent resumeUnityActivity = new Intent( getActivity(), getActivity().getClass() );
123 | resumeUnityActivity.setFlags( Intent.FLAG_ACTIVITY_REORDER_TO_FRONT );
124 | getActivity().startActivityIfNeeded( resumeUnityActivity, 0 );
125 | }
126 | catch( Exception e )
127 | {
128 | Log.e( "Unity", "Exception (resume):", e );
129 | }
130 | }
131 | }
--------------------------------------------------------------------------------
/Plugins/AndroidRuntimePermissions/AndroidRuntimePermissions.cs:
--------------------------------------------------------------------------------
1 | #if !UNITY_EDITOR && UNITY_ANDROID
2 | #define IS_ANDROID_PLATFORM
3 | #endif
4 |
5 | using System;
6 | using System.Threading.Tasks;
7 | using UnityEngine;
8 | #if UNITY_ANDROID
9 | using AndroidRuntimePermissionsNamespace;
10 | #endif
11 |
12 | public static class AndroidRuntimePermissions
13 | {
14 | public enum Permission { Denied = 0, Granted = 1, ShouldAsk = 2 };
15 |
16 | #region Native Properties
17 | #if IS_ANDROID_PLATFORM
18 | private static AndroidJavaClass m_ajc = null;
19 | private static AndroidJavaClass AJC
20 | {
21 | get
22 | {
23 | if( m_ajc == null )
24 | m_ajc = new AndroidJavaClass( "com.yasirkula.unity.RuntimePermissions" );
25 |
26 | return m_ajc;
27 | }
28 | }
29 |
30 | private static AndroidJavaObject m_context = null;
31 | private static AndroidJavaObject Context
32 | {
33 | get
34 | {
35 | if( m_context == null )
36 | {
37 | using( AndroidJavaObject unityClass = new AndroidJavaClass( "com.unity3d.player.UnityPlayer" ) )
38 | {
39 | m_context = unityClass.GetStatic( "currentActivity" );
40 | }
41 | }
42 |
43 | return m_context;
44 | }
45 | }
46 | #endif
47 | #endregion
48 |
49 | #region Permission Functions
50 | public static void OpenSettings()
51 | {
52 | #if IS_ANDROID_PLATFORM
53 | AJC.CallStatic( "OpenSettings", Context );
54 | #else
55 | Debug.Log( "Opening settings..." );
56 | #endif
57 | }
58 |
59 | public static bool CheckPermission( string permission )
60 | {
61 | return CheckPermissions( permission )[0];
62 | }
63 |
64 | public static bool[] CheckPermissions( params string[] permissions )
65 | {
66 | ValidateArgument( permissions );
67 |
68 | #if IS_ANDROID_PLATFORM
69 | string resultRaw = AJC.CallStatic( "CheckPermission", permissions, Context );
70 | if( resultRaw.Length != permissions.Length )
71 | throw new Exception( "CheckPermissions: something went wrong" );
72 |
73 | bool[] result = new bool[permissions.Length];
74 | for( int i = 0; i < result.Length; i++ )
75 | result[i] = resultRaw[i].ToPermission() == Permission.Granted;
76 |
77 | return result;
78 | #else
79 | return GetDummyResult( permissions, true );
80 | #endif
81 | }
82 |
83 | public static void RequestPermissionAsync( string permission, Action callback )
84 | {
85 | #if IS_ANDROID_PLATFORM
86 | RequestPermissionsAsync( new string[] { permission }, ( result ) => callback( result[0] ) );
87 | #else
88 | callback( Permission.Granted );
89 | #endif
90 | }
91 |
92 | public static void RequestPermissionsAsync( string[] permissions, Action callback )
93 | {
94 | #if IS_ANDROID_PLATFORM
95 | PermissionCallback nativeCallback = new PermissionCallback( permissions, callback );
96 | AJC.CallStatic( "RequestPermission", permissions, Context, nativeCallback );
97 | #else
98 | callback( GetDummyResult( permissions, Permission.Granted ) );
99 | #endif
100 | }
101 |
102 | public static async Task RequestPermissionAsync( string permission )
103 | {
104 | return ( await RequestPermissionsAsync( permission ) )[0];
105 | }
106 |
107 | public static Task RequestPermissionsAsync( params string[] permissions )
108 | {
109 | TaskCompletionSource tcs = new TaskCompletionSource();
110 | RequestPermissionsAsync( permissions, ( result ) => tcs.SetResult( result ) );
111 | return tcs.Task;
112 | }
113 | #endregion
114 |
115 | #region Helper Functions
116 | internal static Permission[] ProcessPermissionRequestResult( string[] permissions, string resultRaw )
117 | {
118 | if( resultRaw.Length != permissions.Length )
119 | throw new Exception( "RequestPermissions: something went wrong" );
120 |
121 | Permission[] result = new Permission[permissions.Length];
122 | for( int i = 0; i < result.Length; i++ )
123 | result[i] = resultRaw[i].ToPermission();
124 |
125 | return result;
126 | }
127 |
128 | private static void ValidateArgument( string[] permissions )
129 | {
130 | if( permissions == null || permissions.Length == 0 )
131 | throw new ArgumentException( "Parameter 'permissions' is null or empty!" );
132 |
133 | for( int i = 0; i < permissions.Length; i++ )
134 | {
135 | if( string.IsNullOrEmpty( permissions[i] ) )
136 | throw new ArgumentException( "A permission is null or empty!" );
137 | }
138 | }
139 |
140 | private static T[] GetDummyResult( string[] permissions, T value )
141 | {
142 | T[] result = new T[permissions.Length];
143 | for( int i = 0; i < result.Length; i++ )
144 | result[i] = value;
145 |
146 | return result;
147 | }
148 |
149 | private static Permission ToPermission( this char ch )
150 | {
151 | return (Permission) ( ch - '0' );
152 | }
153 | #endregion
154 | }
--------------------------------------------------------------------------------
/.github/README.md:
--------------------------------------------------------------------------------
1 | # Unity Android Runtime Permissions
2 |
3 | 
4 |
5 | **Available on Asset Store:** https://assetstore.unity.com/packages/tools/integration/android-runtime-permissions-117803
6 |
7 | **Forum Thread:** https://forum.unity.com/threads/open-source-androidruntimepermissions-manage-runtime-permissions-synchronously-on-android-m.528833/
8 |
9 | **Discord:** https://discord.gg/UJJt549AaV
10 |
11 | **[GitHub Sponsors ☕](https://github.com/sponsors/yasirkula)**
12 |
13 | *Based on UnityAndroidPermissions (MIT License): https://github.com/Over17/UnityAndroidPermissions*
14 |
15 | This plugin helps you query/request runtime permissions on Android M and later. It also works on older Android versions and detects whether a requested permission is declared in AndroidManifest or not.
16 |
17 | ## INSTALLATION
18 |
19 | There are 5 ways to install this plugin:
20 |
21 | - import [RuntimePermissions.unitypackage](https://github.com/yasirkula/UnityAndroidRuntimePermissions/releases) via *Assets-Import Package*
22 | - clone/[download](https://github.com/yasirkula/UnityAndroidRuntimePermissions/archive/master.zip) this repository and move the *Plugins* folder to your Unity project's *Assets* folder
23 | - import it from [Asset Store](https://assetstore.unity.com/packages/tools/integration/android-runtime-permissions-117803)
24 | - *(via Package Manager)* click the + button and install the package from the following git URL:
25 | - `https://github.com/yasirkula/UnityAndroidRuntimePermissions.git`
26 | - *(via [OpenUPM](https://openupm.com))* after installing [openupm-cli](https://github.com/openupm/openupm-cli), run the following command:
27 | - `openupm add com.yasirkula.androidruntimepermissions`
28 |
29 | ## HOW TO
30 |
31 | Before we start, there is one optional step: by default, Unity shows a permission dialog on startup to prevent plugins from crashing/malfunctioning. This can be disabled, if you want; but you must make sure to handle all the runtime permissions carefully in your app's lifecycle. To disable this dialog, add the following line inside the `...` tag of *Plugins/Android/AndroidManifest.xml*:
32 |
33 | ```xml
34 |
35 | ```
36 |
37 | **NOTE:** if your project doesn't have an AndroidManifest, you can enable *Edit/Project Settings/Player/Publishing Settings/Custom Main Manifest*.
38 |
39 | You can use the following *static* functions of **AndroidRuntimePermissions** to manage runtime permissions:
40 |
41 | `bool CheckPermission( string permission )`: checks whether or not the permission is granted
42 |
43 | `bool[] CheckPermissions( params string[] permissions )`: queries multiple permissions simultaneously. The returned array will contain one entry per queried permission
44 |
45 | `void RequestPermissionAsync( string permission, Action callback )`: requests a permission 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". **Permission** is an enum that can take 3 values:
46 | - **Granted**: permission is granted
47 | - **ShouldAsk**: permission is denied but we can ask the user for permission once again. As long as the user doesn't select "Don't ask again" while denying the permission, ShouldAsk is returned
48 | - **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 app's Settings. This happens when user selects "Don't ask again" while denying the permission or when user is not allowed to give that permission (parental controls etc.)
49 |
50 | `void RequestPermissionsAsync( string[] permissions, Action callback )`: requests multiple permissions simultaneously
51 |
52 | `Task RequestPermissionAsync( string permission )`: Task-based overload of RequestPermissionAsync
53 |
54 | `Task RequestPermissionsAsync( string[] permissions )`: Task-based overload of RequestPermissionsAsync
55 |
56 | `void OpenSettings()`: opens the settings for this app, from where the user can manually grant permission(s) in case a needed permission's state is *Permission.Denied*
57 |
58 | ## EXAMPLE CODE
59 |
60 | The following code requests *ACCESS_FINE_LOCATION* permission (it must be declared in *AndroidManifest*) when bottom-right corner of the screen is touched:
61 |
62 | ```csharp
63 | void Update()
64 | {
65 | if( Input.GetMouseButtonDown( 0 ) && Input.mousePosition.x > Screen.width * 0.8f && Input.mousePosition.y < Screen.height * 0.2f )
66 | RequestPermission();
67 | }
68 |
69 | async void RequestPermission()
70 | {
71 | AndroidRuntimePermissions.Permission result = await AndroidRuntimePermissions.RequestPermissionAsync( "android.permission.ACCESS_FINE_LOCATION" );
72 | if( result == AndroidRuntimePermissions.Permission.Granted )
73 | Debug.Log( "We have permission to access fine location!" );
74 | else
75 | Debug.Log( "Permission state: " + result );
76 |
77 | // Requesting ACCESS_FINE_LOCATION and CAMERA permissions simultaneously
78 | //AndroidRuntimePermissions.Permission[] result = await AndroidRuntimePermissions.RequestPermissionsAsync( "android.permission.ACCESS_FINE_LOCATION", "android.permission.CAMERA" );
79 | //if( result[0] == AndroidRuntimePermissions.Permission.Granted && result[1] == AndroidRuntimePermissions.Permission.Granted )
80 | // Debug.Log( "We have all the permissions!" );
81 | //else
82 | // Debug.Log( "Some permission(s) are not granted..." );
83 | }
84 | ```
85 |
--------------------------------------------------------------------------------