├── .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 | ![runtime_permission](Images/permission.png) 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 | --------------------------------------------------------------------------------