Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions UnityResources/Singularity/BluetoothUIManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ private enum uistate { Disconnected, Connecting, Connected };
// Start is called before the first frame update
void Start()
{
}

public void onBluetoothReady()
{
// only setup the UI after the bluetooth is ready
updateDeviceOptions();
}

Expand Down
96 changes: 66 additions & 30 deletions UnityResources/Singularity/SingularityManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace Sngty
{
public class SingularityManager : MonoBehaviour
{
public UnityEvent onBluetoothReady;
public UnityEvent onConnected;
public UnityEvent<string> onMessageRecieved;
public UnityEvent<string> onError;
Expand All @@ -17,41 +18,67 @@ public class SingularityManager : MonoBehaviour

private List<AndroidJavaObject> connectedDevices;

private HashSet<string> BtPermissionsNeeded = new HashSet<string> {
"android.permission.BLUETOOTH_CONNECT",
"android.permission.BLUETOOTH",
"android.permission.BLUETOOTH_ADMIN",
"android.permission.BLUETOOTH_SCAN"
};



internal void PermissionCallbacks_PermissionDeniedAndDontAskAgain(string permissionName)
{
Debug.Log($"{permissionName} PermissionDeniedAndDontAskAgain");
}

internal void PermissionCallbacks_PermissionGranted(string permissionName)
{
Debug.Log($"{permissionName} PermissionCallbacks_PermissionGranted");

BtPermissionsNeeded.Remove(permissionName);
if (BtPermissionsNeeded.Count == 0){
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.
// 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<string> permissionsNeeded = new List<string>();
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");
// 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<string> permissionsNeeded = new List<string>(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();
}
Debug.LogWarning("May need to restart the app. Requesting permissions: " + string.Join(", ", permissionsNeeded));
Permission.RequestUserPermissions(permissionsNeeded.ToArray());
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.

BluetoothManager = new AndroidJavaClass("com.harrysoft.androidbluetoothserial.BluetoothManager");
bluetoothManager = BluetoothManager.CallStatic<AndroidJavaObject>("getInstance");
Debug.LogWarning("May need to restart the app. Requesting permissions: " + string.Join(", ", permissionsNeeded));

connectedDevices = new List<AndroidJavaObject>();
var callbacks = new PermissionCallbacks();
callbacks.PermissionDenied += PermissionCallbacks_PermissionDenied;
callbacks.PermissionGranted += PermissionCallbacks_PermissionGranted;
callbacks.PermissionDeniedAndDontAskAgain += PermissionCallbacks_PermissionDeniedAndDontAskAgain;

Permission.RequestUserPermissions(permissionsNeeded.ToArray(), callbacks);
}
}


Expand All @@ -63,15 +90,24 @@ void Start()
// Update is called once per frame
void Update()
{
}

private void BluetoothPermissionsGranted()
{
BluetoothManager = new AndroidJavaClass("com.harrysoft.androidbluetoothserial.BluetoothManager");
bluetoothManager = BluetoothManager.CallStatic<AndroidJavaObject>("getInstance");

connectedDevices = new List<AndroidJavaObject>();

onBluetoothReady.Invoke();
}

public void ConnectToDevice(DeviceSignature sig)
{
AndroidJavaClass Schedulers = new AndroidJavaClass("io.reactivex.schedulers.Schedulers");
AndroidJavaClass AndroidSchedulers = new AndroidJavaClass("io.reactivex.android.schedulers.AndroidSchedulers");
bluetoothManager.Call<AndroidJavaObject>("openSerialDevice", sig.mac)
.Call<AndroidJavaObject>("subscribeOn",Schedulers.CallStatic<AndroidJavaObject>("io"))
.Call<AndroidJavaObject>("subscribeOn", Schedulers.CallStatic<AndroidJavaObject>("io"))
.Call<AndroidJavaObject>("observeOn", AndroidSchedulers.CallStatic<AndroidJavaObject>("mainThread"))
.Call("subscribe", new RxSingleObserver(onError, onConnected, onMessageRecieved, connectedDevices));

Expand Down Expand Up @@ -135,7 +171,7 @@ class RxSingleObserver : AndroidJavaProxy
private List<AndroidJavaObject> connectedDevices;
public RxSingleObserver(UnityEvent<string> onErrorEvent, UnityEvent onConnectedEvent, UnityEvent<string> onMessageRecievedEvent, List<AndroidJavaObject> connectedDevices) : base("io.reactivex.SingleObserver")
{
this.onErrorEvent = onErrorEvent;
this.onErrorEvent = onErrorEvent;
this.onConnectedEvent = onConnectedEvent;
this.onMessageRecievedEvent = onMessageRecievedEvent;
this.connectedDevices = connectedDevices;
Expand Down