From de274b4c6cf0750add70f337ae10ba257a73f411 Mon Sep 17 00:00:00 2001 From: wangyijenny <52047217+wangyijenny@users.noreply.github.com> Date: Tue, 16 Jan 2024 23:14:57 -0500 Subject: [PATCH 1/4] set up callbacks for bluetooth --- .../Singularity/SingularityManager.cs | 60 +++++++++++++++---- 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/UnityResources/Singularity/SingularityManager.cs b/UnityResources/Singularity/SingularityManager.cs index 6a830f8..e79e0a6 100644 --- a/UnityResources/Singularity/SingularityManager.cs +++ b/UnityResources/Singularity/SingularityManager.cs @@ -8,6 +8,7 @@ namespace Sngty { public class SingularityManager : MonoBehaviour { + public UnityEvent onBluetoothReady; public UnityEvent onConnected; public UnityEvent onMessageRecieved; public UnityEvent onError; @@ -17,6 +18,24 @@ public class SingularityManager : MonoBehaviour private List connectedDevices; + + internal void PermissionCallbacks_PermissionDeniedAndDontAskAgain(string permissionName) + { + Debug.Log($"{permissionName} PermissionDeniedAndDontAskAgain"); + } + + internal void PermissionCallbacks_PermissionGranted(string permissionName) + { + Debug.Log($"{permissionName} PermissionCallbacks_PermissionGranted"); + + BluetoothPermissionsGranted(); + } + + internal void PermissionCallbacks_PermissionDenied(string permissionName) + { + Debug.Log($"{permissionName} PermissionCallbacks_PermissionDenied"); + } + // Awake is called before any object's Start(). // Set up bluetooth using Awake() so it's ready for other objects. // Trying to use the singularity manager in other script's Awake() methods may not work properly. @@ -33,25 +52,37 @@ void Awake() bool hasBtScanPermission = Permission.HasUserAuthorizedPermission("android.permission.BLUETOOTH_SCAN"); List permissionsNeeded = new List(); - if (!hasBtConnectPermission) { + if (!hasBtConnectPermission) + { permissionsNeeded.Add("android.permission.BLUETOOTH_CONNECT"); } - if (!hasBtPermission) { + if (!hasBtPermission) + { permissionsNeeded.Add("android.permission.BLUETOOTH"); } - if (!hasBtAdminPermission) { + if (!hasBtAdminPermission) + { permissionsNeeded.Add("android.permission.BLUETOOTH_ADMIN"); } - if (!hasBtScanPermission) { + if (!hasBtScanPermission) + { permissionsNeeded.Add("android.permission.BLUETOOTH_SCAN"); } Debug.LogWarning("May need to restart the app. Requesting permissions: " + string.Join(", ", permissionsNeeded)); - Permission.RequestUserPermissions(permissionsNeeded.ToArray()); - BluetoothManager = new AndroidJavaClass("com.harrysoft.androidbluetoothserial.BluetoothManager"); - bluetoothManager = BluetoothManager.CallStatic("getInstance"); + if (permissionsNeeded.Count == 0) + { + BluetoothPermissionsGranted(); + } + else + { + var callbacks = new PermissionCallbacks(); + callbacks.PermissionDenied += PermissionCallbacks_PermissionDenied; + callbacks.PermissionGranted += PermissionCallbacks_PermissionGranted; + callbacks.PermissionDeniedAndDontAskAgain += PermissionCallbacks_PermissionDeniedAndDontAskAgain; - connectedDevices = new List(); + Permission.RequestUserPermissions(permissionsNeeded.ToArray(), callbacks); + } } @@ -63,7 +94,16 @@ void Start() // Update is called once per frame void Update() { + } + + private void BluetoothPermissionsGranted() + { + BluetoothManager = new AndroidJavaClass("com.harrysoft.androidbluetoothserial.BluetoothManager"); + bluetoothManager = BluetoothManager.CallStatic("getInstance"); + + connectedDevices = new List(); + onBluetoothReady.Invoke(); } public void ConnectToDevice(DeviceSignature sig) @@ -71,7 +111,7 @@ public void ConnectToDevice(DeviceSignature sig) AndroidJavaClass Schedulers = new AndroidJavaClass("io.reactivex.schedulers.Schedulers"); AndroidJavaClass AndroidSchedulers = new AndroidJavaClass("io.reactivex.android.schedulers.AndroidSchedulers"); bluetoothManager.Call("openSerialDevice", sig.mac) - .Call("subscribeOn",Schedulers.CallStatic("io")) + .Call("subscribeOn", Schedulers.CallStatic("io")) .Call("observeOn", AndroidSchedulers.CallStatic("mainThread")) .Call("subscribe", new RxSingleObserver(onError, onConnected, onMessageRecieved, connectedDevices)); @@ -135,7 +175,7 @@ class RxSingleObserver : AndroidJavaProxy private List connectedDevices; public RxSingleObserver(UnityEvent onErrorEvent, UnityEvent onConnectedEvent, UnityEvent onMessageRecievedEvent, List connectedDevices) : base("io.reactivex.SingleObserver") { - this.onErrorEvent = onErrorEvent; + this.onErrorEvent = onErrorEvent; this.onConnectedEvent = onConnectedEvent; this.onMessageRecievedEvent = onMessageRecievedEvent; this.connectedDevices = connectedDevices; From 93de5e390819fa6bb2e30466418790994c097402 Mon Sep 17 00:00:00 2001 From: wangyijenny <52047217+wangyijenny@users.noreply.github.com> Date: Wed, 17 Jan 2024 20:24:46 -0500 Subject: [PATCH 2/4] call BluetoothPermissionsGranted only when all permissions granted in callbacks --- .../Singularity/SingularityManager.cs | 56 +++++++++---------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/UnityResources/Singularity/SingularityManager.cs b/UnityResources/Singularity/SingularityManager.cs index e79e0a6..eab7dac 100644 --- a/UnityResources/Singularity/SingularityManager.cs +++ b/UnityResources/Singularity/SingularityManager.cs @@ -18,6 +18,14 @@ public class SingularityManager : MonoBehaviour private List connectedDevices; + private HashSet BtPermissionsNeeded = new HashSet { + "android.permission.BLUETOOTH_CONNECT", + "android.permission.BLUETOOTH", + "android.permission.BLUETOOTH_ADMIN", + "android.permission.BLUETOOTH_SCAN" + }; + + internal void PermissionCallbacks_PermissionDeniedAndDontAskAgain(string permissionName) { @@ -28,7 +36,10 @@ internal void PermissionCallbacks_PermissionGranted(string permissionName) { Debug.Log($"{permissionName} PermissionCallbacks_PermissionGranted"); - BluetoothPermissionsGranted(); + BtPermissionsNeeded.Remove(permissionName); + if (BtPermissionsNeeded.Count == 0){ + BluetoothPermissionsGranted(); + } } internal void PermissionCallbacks_PermissionDenied(string permissionName) @@ -38,44 +49,29 @@ internal void PermissionCallbacks_PermissionDenied(string permissionName) // Awake is called before any object's Start(). // Set up bluetooth using Awake() so it's ready for other objects. - // Trying to use the singularity manager in other script's Awake() methods may not work properly. + // Trying to use SingularityManager in other script's Awake() methods may not work properly + // unless you set up callbacks correctly. void Awake() { - // Check for necessary bluetooth permissions and request if necessary - // You may need to restart the app on the headset after granting permissions. - // Sometimes, you may need to restart the app twice for the permissions to fully work. - // This is a quick and dirty solution for getting the permissions. - // A better way is to use callbacks to let everything else know when the permissions have been granted. - bool hasBtConnectPermission = Permission.HasUserAuthorizedPermission("android.permission.BLUETOOTH_CONNECT"); - bool hasBtPermission = Permission.HasUserAuthorizedPermission("android.permission.BLUETOOTH"); - bool hasBtAdminPermission = Permission.HasUserAuthorizedPermission("android.permission.BLUETOOTH_ADMIN"); - bool hasBtScanPermission = Permission.HasUserAuthorizedPermission("android.permission.BLUETOOTH_SCAN"); - - List permissionsNeeded = new List(); - if (!hasBtConnectPermission) - { - permissionsNeeded.Add("android.permission.BLUETOOTH_CONNECT"); - } - if (!hasBtPermission) - { - permissionsNeeded.Add("android.permission.BLUETOOTH"); - } - if (!hasBtAdminPermission) - { - permissionsNeeded.Add("android.permission.BLUETOOTH_ADMIN"); - } - if (!hasBtScanPermission) - { - permissionsNeeded.Add("android.permission.BLUETOOTH_SCAN"); - } - Debug.LogWarning("May need to restart the app. Requesting permissions: " + string.Join(", ", permissionsNeeded)); + // Remove permissions that have already been granted + BtPermissionsNeeded.RemoveWhere(item => Permission.HasUserAuthorizedPermission(item)); + // Convert what is left in the hashset to a list as preparation to ask for permissions + List permissionsNeeded = new List(BtPermissionsNeeded); + + // If there are no permissions left to ask for, then we can proceed with bluetooth setup + // else, ask for the permissions if (permissionsNeeded.Count == 0) { BluetoothPermissionsGranted(); } else { + // If you have set up callbacks correctly, you should not need to restart the app. + // Otherwise, you may need to restart the app (up to 2 times) to get bluetooth working. + + Debug.LogWarning("May need to restart the app. Requesting permissions: " + string.Join(", ", permissionsNeeded)); + var callbacks = new PermissionCallbacks(); callbacks.PermissionDenied += PermissionCallbacks_PermissionDenied; callbacks.PermissionGranted += PermissionCallbacks_PermissionGranted; From cb49afdf0f95fbf5331b8d313ee009f042c21484 Mon Sep 17 00:00:00 2001 From: wangyijenny <52047217+wangyijenny@users.noreply.github.com> Date: Sun, 21 Jan 2024 11:57:51 -0500 Subject: [PATCH 3/4] have BluetoothUIManager use callbacks --- UnityResources/Singularity/BluetoothUIManager.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/UnityResources/Singularity/BluetoothUIManager.cs b/UnityResources/Singularity/BluetoothUIManager.cs index 231bf80..e11cb0f 100644 --- a/UnityResources/Singularity/BluetoothUIManager.cs +++ b/UnityResources/Singularity/BluetoothUIManager.cs @@ -26,6 +26,11 @@ private enum uistate { Disconnected, Connecting, Connected }; public int maxMessages = 7; // Start is called before the first frame update void Start() + { + //updateDeviceOptions(); + } + + public void onBluetoothReady() { updateDeviceOptions(); } From 30391765a71cbac621a069719dc9dcc50b920229 Mon Sep 17 00:00:00 2001 From: wangyijenny <52047217+wangyijenny@users.noreply.github.com> Date: Sun, 21 Jan 2024 12:16:07 -0500 Subject: [PATCH 4/4] code clean up --- UnityResources/Singularity/BluetoothUIManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UnityResources/Singularity/BluetoothUIManager.cs b/UnityResources/Singularity/BluetoothUIManager.cs index e11cb0f..d6e8ec6 100644 --- a/UnityResources/Singularity/BluetoothUIManager.cs +++ b/UnityResources/Singularity/BluetoothUIManager.cs @@ -27,11 +27,11 @@ private enum uistate { Disconnected, Connecting, Connected }; // Start is called before the first frame update void Start() { - //updateDeviceOptions(); } public void onBluetoothReady() { + // only setup the UI after the bluetooth is ready updateDeviceOptions(); }