i have a problem in running my App on the simulator or even the real android phone even there are no syntax errors, when i run the vertual phone it opens but the application don't work and i get the message "can't initialize HUB" as in the image, and i get on console the following logs :
08-14 19:40:23.271 2342-2357/com.thalmic.android.sample.helloworld V/RenderScript: 0xae4be000 Launching thread(s), CPUs 2
08-14 19:40:24.090 2342-2357/com.thalmic.android.sample.helloworld E/Surface: getSlotFromBufferLocked: unknown buffer: 0xae4b12a0
08-14 23:40:24.165 5064-5078/com.thalmic.android.sample.helloworld D/OpenGLRenderer: Use EGL_SWAP_BEHAVIOR_PRESERVED: true
[ 08-14 23:40:24.192 5064: 5064 D/ ]
HostConnection::get() New Host Connection established 0xaa9e81f0, tid 5064
08-14 23:40:24.556 5064-5078/com.thalmic.android.sample.helloworld I/OpenGLRenderer: Initialized EGL, version 1.4
08-14 23:40:25.289 5064-5078/com.thalmic.android.sample.helloworld V/RenderScript: 0xae5b7000 Launching thread(s), CPUs 2
08-14 23:40:25.433 5064-5078/com.thalmic.android.sample.helloworld E/Surface: getSlotFromBufferLocked: unknown buffer: 0xae4b12a0
08-14 23:40:28.898 5064-5070/com.thalmic.android.sample.helloworld W/art: Suspending all threads took: 65.953ms
08-14 23:40:28.950 5064-5064/com.thalmic.android.sample.helloworld E/Hub: Bluetooth not supported
08-14 23:40:30.344 5064-5064/com.thalmic.android.sample.helloworld I/Choreographer: Skipped 52 frames! The application may be doing too much work on its main thread.
08-14 23:40:31.042 5064-5078/com.thalmic.android.sample.helloworld E/Surface: getSlotFromBufferLocked: unknown buffer: 0xae4b10e0
my source code is :
/*
* Copyright (C) 2014 Thalmic Labs Inc.
* Distributed under the Myo SDK license agreement. See LICENSE.txt for details.
*/
package com.thalmic.android.sample.helloworld;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.TextView;
import android.widget.Toast;
import com.thalmic.myo.AbstractDeviceListener;
import com.thalmic.myo.Arm;
import com.thalmic.myo.DeviceListener;
import com.thalmic.myo.Hub;
import com.thalmic.myo.Myo;
import com.thalmic.myo.Pose;
import com.thalmic.myo.Quaternion;
import com.thalmic.myo.XDirection;
import com.thalmic.myo.scanner.ScanActivity;
public class HelloWorldActivity extends Activity {
private TextView mLockStateView;
private TextView mTextView;
// Classes that inherit from AbstractDeviceListener can be used to receive events from Myo devices.
// If you do not override an event, the default behavior is to do nothing.
private DeviceListener mListener = new AbstractDeviceListener() {
// onConnect() is called whenever a Myo has been connected.
#Override
public void onConnect(Myo myo, long timestamp) {
// Set the text color of the text view to cyan when a Myo connects.
mTextView.setTextColor(Color.CYAN);
}
// onDisconnect() is called whenever a Myo has been disconnected.
#Override
public void onDisconnect(Myo myo, long timestamp) {
// Set the text color of the text view to red when a Myo disconnects.
mTextView.setTextColor(Color.RED);
}
// onArmSync() is called whenever Myo has recognized a Sync Gesture after someone has put it on their
// arm. This lets Myo know which arm it's on and which way it's facing.
#Override
public void onArmSync(Myo myo, long timestamp, Arm arm, XDirection xDirection) {
mTextView.setText(myo.getArm() == Arm.LEFT ? R.string.arm_left : R.string.arm_right);
}
// onArmUnsync() is called whenever Myo has detected that it was moved from a stable position on a person's arm after
// it recognized the arm. Typically this happens when someone takes Myo off of their arm, but it can also happen
// when Myo is moved around on the arm.
#Override
public void onArmUnsync(Myo myo, long timestamp) {
mTextView.setText(R.string.hello_world);
}
// onUnlock() is called whenever a synced Myo has been unlocked. Under the standard locking
// policy, that means poses will now be delivered to the listener.
#Override
public void onUnlock(Myo myo, long timestamp) {
mLockStateView.setText(R.string.unlocked);
}
// onLock() is called whenever a synced Myo has been locked. Under the standard locking
// policy, that means poses will no longer be delivered to the listener.
#Override
public void onLock(Myo myo, long timestamp) {
mLockStateView.setText(R.string.locked);
}
// onOrientationData() is called whenever a Myo provides its current orientation,
// represented as a quaternion.
#Override
public void onOrientationData(Myo myo, long timestamp, Quaternion rotation) {
// Calculate Euler angles (roll, pitch, and yaw) from the quaternion.
float roll = (float) Math.toDegrees(Quaternion.roll(rotation));
float pitch = (float) Math.toDegrees(Quaternion.pitch(rotation));
float yaw = (float) Math.toDegrees(Quaternion.yaw(rotation));
// Adjust roll and pitch for the orientation of the Myo on the arm.
if (myo.getXDirection() == XDirection.TOWARD_ELBOW) {
roll *= -1;
pitch *= -1;
}
// Next, we apply a rotation to the text view using the roll, pitch, and yaw.
mTextView.setRotation(roll);
mTextView.setRotationX(pitch);
mTextView.setRotationY(yaw);
}
// onPose() is called whenever a Myo provides a new pose.
#Override
public void onPose(Myo myo, long timestamp, Pose pose) {
// Handle the cases of the Pose enumeration, and change the text of the text view
// based on the pose we receive.
switch (pose) {
case UNKNOWN:
mTextView.setText(getString(R.string.hello_world));
break;
case REST:
case DOUBLE_TAP:
int restTextId = R.string.hello_world;
switch (myo.getArm()) {
case LEFT:
restTextId = R.string.arm_left;
break;
case RIGHT:
restTextId = R.string.arm_right;
break;
}
mTextView.setText(getString(restTextId));
break;
case FIST:
mTextView.setText(getString(R.string.pose_fist));
break;
case WAVE_IN:
mTextView.setText(getString(R.string.pose_wavein));
break;
case WAVE_OUT:
mTextView.setText(getString(R.string.pose_waveout));
break;
case FINGERS_SPREAD:
mTextView.setText(getString(R.string.pose_fingersspread));
break;
}
if (pose != Pose.UNKNOWN && pose != Pose.REST) {
// Tell the Myo to stay unlocked until told otherwise. We do that here so you can
// hold the poses without the Myo becoming locked.
myo.unlock(Myo.UnlockType.HOLD);
// Notify the Myo that the pose has resulted in an action, in this case changing
// the text on the screen. The Myo will vibrate.
myo.notifyUserAction();
} else {
// Tell the Myo to stay unlocked only for a short period. This allows the Myo to
// stay unlocked while poses are being performed, but lock after inactivity.
myo.unlock(Myo.UnlockType.TIMED);
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_hello_world);
mLockStateView = (TextView) findViewById(R.id.lock_state);
mTextView = (TextView) findViewById(R.id.text);
// First, we initialize the Hub singleton with an application identifier.
Hub hub = Hub.getInstance();
if (!hub.init(this, getPackageName())) {
// We can't do anything with the Myo device if the Hub can't be initialized, so exit.
Toast.makeText(this, "Couldn't initialize Hub", Toast.LENGTH_SHORT).show();
finish();
return;
}
// Next, register for DeviceListener callbacks.
hub.addListener(mListener);
}
#Override
protected void onDestroy() {
super.onDestroy();
// We don't want any callbacks when the Activity is gone, so unregister the listener.
Hub.getInstance().removeListener(mListener);
if (isFinishing()) {
// The Activity is finishing, so shutdown the Hub. This will disconnect from the Myo.
Hub.getInstance().shutdown();
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (R.id.action_scan == id) {
onScanActionSelected();
return true;
}
return super.onOptionsItemSelected(item);
}
private void onScanActionSelected() {
// Launch the ScanActivity to scan for Myos to connect to.
Intent intent = new Intent(this, ScanActivity.class);
startActivity(intent);
}
}
You are trying to establish a connection to your Myo. The hub can't be initialished. According to the documentation this occurs when your Android device doesn't support Bluetooth Low Energy. Bluetooth is not supported in the emulator, because of this the initialization will fail.
Creating a connection will also fail if the applicationIdentifier is not properly formatted, but in that case you should get an IllegalArgumentException.
Device recommended be Android 4.3 (Jelly Bean) and up and device must have Bluetooth radio that supports Bluetooth 4.0 LE! Emulator not supported Bluetooth 4.0 LE. You say your Android application is not opening on emulator and real phone. Check on your real phone version of Android and if phone support Bluetooth 4.0 LE. Check FAQs from Myo support: What devices is the Myo armband compatible with.
Related
I am facing this problem since 2 days and i didn't find any helpful solution, every time i signUp or SignIn using email, Password this what happen:
W/System: Ignoring header X-Firebase-Locale because its value was null.
response come after 4-5 minute.
I tried many emulators one of them worked fine and the other not,
emulators APIs:
33: didn't work
30: didn't work
28: Worked
26: didn't work
My Code:
package com.abdulghffar.gju_outgoings_app.fragments;
import static android.content.ContentValues.TAG;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import com.abdulghffar.gju_outgoings_app.R;
import com.abdulghffar.gju_outgoings_app.activites.MainActivity;
import com.abdulghffar.gju_outgoings_app.activites.authentication;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.AuthResult;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseAuthException;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.firestore.DocumentReference;
import com.google.firebase.firestore.DocumentSnapshot;
import com.google.firebase.firestore.FirebaseFirestore;
import javax.annotation.Nullable;
public class fragmentSignIn extends Fragment {
//Fields
TextView signUpTextView;
EditText emailField;
EditText passwordField;
//Buttons
Button signInButton;
//Firebase
private FirebaseAuth mAuth;
FirebaseFirestore db;
//Others
View view;
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup parent, #Nullable Bundle savedInstanceState) {
// Defines the xml file for the fragment
view = inflater.inflate(R.layout.activity_fragment_sign_in, parent, false);
setup();
signInButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//Check email
if (emailField.getText().toString().matches("")) {
toast("Please enter your email");
return;
}
if ((!emailField.getText().toString().contains("#")) || (!emailField.getText().toString().contains(".com"))) {
toast("Invalid email format");
return;
}
//Check password
if (passwordField.getText().toString().matches("")) {
toast("Please enter your password");
return;
}
if(getActivity()!=null){
InputMethodManager imm = (InputMethodManager)getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
signIn();
}
});
signUpTextView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
authentication registration = (authentication) getActivity();
fragmentSignUp fragmentSignUp = new fragmentSignUp();
assert registration != null;
registration.replaceFragment(fragmentSignUp);
}
});
return view;
}
// This event is triggered soon after onCreateView().
// Any view setup should occur here. E.g., view lookups and attaching view listeners.
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
// Setup any handles to view objects here
// EditText etFoo = (EditText) view.findViewById(R.id.etFoo);
}
void setup() {
//Fields
signUpTextView = (TextView) view.findViewById(R.id.signUp);
emailField = (EditText) view.findViewById(R.id.emailField);
passwordField = (EditText) view.findViewById(R.id.passwordField);
//Buttons
signInButton = (Button) view.findViewById(R.id.signInButton);
//Firebase
mAuth = FirebaseAuth.getInstance();
db = FirebaseFirestore.getInstance();
// Check if user is signed in (non-null) and update UI accordingly.
FirebaseUser currentUser = mAuth.getCurrentUser();
if (currentUser != null) {
checkUserApproval(currentUser);
}
}
void signIn() {
String email = emailField.getText().toString();
String password = passwordField.getText().toString();
mAuth.signInWithEmailAndPassword(email, password)
.addOnCompleteListener(getActivity(), new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
// Sign in success, update UI with the signed-in user's information
Log.d(TAG, "signInWithEmail:success");
FirebaseUser user = mAuth.getCurrentUser();
if (user != null) {
checkUserApproval(user);
}
} else {
// If sign in fails, display a message to the user.
Log.w(TAG, "signInWithEmail:failure", task.getException());
}
}
});
}
void updateUI(int x) {
if (x == 0) {
Intent i = new Intent(getActivity(), MainActivity.class);
startActivity(i);
} else {
authentication authentication = (authentication) getActivity();
fragmentWaiting fragmentWaiting = new fragmentWaiting();
assert authentication != null;
authentication.replaceFragment(fragmentWaiting);
}
}
void toast(String message) {
Toast toast = Toast.makeText(view.getContext(), message, Toast.LENGTH_LONG);
toast.show();
}
private void checkUserApproval(FirebaseUser user) {
DocumentReference docRef = db.collection("Users").document(user.getUid());
docRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
Log.d(TAG, "DocumentSnapshot data: " + document.getData());
if (document.get("approval").toString().matches("Not yet")) {
updateUI(1);
} else {
updateUI(0);
}
} else {
Log.d(TAG, "No such document");
}
} else {
Log.d(TAG, "get failed with ", task.getException());
}
}
});
}
}
Run:
08/14 09:57:53: Launching 'app' on Pixel 5 API 26.
App restart successful without requiring a re-install.
$ adb shell am start -n "com.abdulghffar.gju_outgoings_app/com.abdulghffar.gju_outgoings_app.activites.authentication" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER
Connected to process 10716 on device 'Pixel_5_API_26 [emulator-5554]'.
Capturing and displaying logcat messages from application. This behavior can be disabled in the "Logcat output" section of the "Debugger" settings page.
I/FirebaseApp: Device unlocked: initializing all Firebase APIs for app [DEFAULT]
I/FirebaseInitProvider: FirebaseApp initialization successful
W/zygote64: Skipping duplicate class check due to unrecognized classloader
I/DynamiteModule: Considering local module com.google.android.gms.measurement.dynamite:78 and remote module com.google.android.gms.measurement.dynamite:15
I/DynamiteModule: Selected local version of com.google.android.gms.measurement.dynamite
V/FA: onActivityCreated
V/FA: App measurement collection enabled
V/FA: App measurement enabled for app package, google app id: com.abdulghffar.gju_outgoings_app, 1:938336016999:android:1e16856c037883e29f12c2
I/FA: App measurement initialized, version: 68000
I/FA: To enable debug logging run: adb shell setprop log.tag.FA VERBOSE
I/FA: To enable faster debug mode event logging run:
adb shell setprop debug.firebase.analytics.app com.abdulghffar.gju_outgoings_app
D/FA: Debug-level message logging enabled
V/FA: Connecting to remote service
V/FA: Connection attempt already in progress
V/FA: Connection attempt already in progress
I/zygote64: Do partial code cache collection, code=29KB, data=26KB
I/zygote64: After code cache collection, code=27KB, data=25KB
I/zygote64: Increasing code cache capacity to 128KB
V/FA: Activity resumed, time: 4323080
I/FA: Tag Manager is not found and thus will not be used
D/OpenGLRenderer: HWUI GL Pipeline
V/FA: Connection attempt already in progress
V/FA: Connection attempt already in progress
D/: HostConnection::get() New Host Connection established 0x74aee132c0, tid 10768
I/OpenGLRenderer: Initialized EGL, version 1.4
D/OpenGLRenderer: Swap behavior 1
W/OpenGLRenderer: Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...
D/OpenGLRenderer: Swap behavior 0
D/EGL_emulation: eglCreateContext: 0x74b99cb5a0: maj 3 min 0 rcv 3
D/EGL_emulation: eglMakeCurrent: 0x74b99cb5a0: ver 3 0 (tinfo 0x74b99ff900)
D/EGL_emulation: eglMakeCurrent: 0x74b99cb5a0: ver 3 0 (tinfo 0x74b99ff900)
D/FA: Connected to remote service
V/FA: Processing queued up service tasks: 5
I/zygote64: Do partial code cache collection, code=59KB, data=52KB
I/zygote64: After code cache collection, code=59KB, data=52KB
I/zygote64: Increasing code cache capacity to 256KB
V/FA: Inactivity, disconnecting from the service
W/View: dispatchProvideAutofillStructure(): not laid out, ignoring
I/AssistStructure: Flattened final assist data: 2264 bytes, containing 1 windows, 8 views
I/zygote64: Do full code cache collection, code=124KB, data=103KB
I/zygote64: After code cache collection, code=108KB, data=63KB
D/NetworkSecurityConfig: No Network Security Config specified, using platform default
W/System: Ignoring header X-Firebase-Locale because its value was null.
W/System: Ignoring header X-Firebase-Locale because its value was null.
W/System: Ignoring header X-Firebase-Locale because its value was null.
W/System: Ignoring header X-Firebase-Locale because its value was null.
I/zygote64: Do partial code cache collection, code=123KB, data=83KB
I/zygote64: After code cache collection, code=123KB, data=83KB
I/zygote64: Increasing code cache capacity to 512KB
I/zygote64: Compiler allocated 6MB to compile void android.view.ViewRootImpl.performTraversals()
W/ContentValues: signInWithEmail:failure
com.google.firebase.FirebaseNetworkException: A network error (such as timeout, interrupted connection or unreachable host) has occurred.
at com.google.android.gms.internal.firebase-auth-api.zztu.zza(com.google.firebase:firebase-auth##21.0.6:17)
at com.google.android.gms.internal.firebase-auth-api.zzus.zza(com.google.firebase:firebase-auth##21.0.6:9)
at com.google.android.gms.internal.firebase-auth-api.zzut.zzl(com.google.firebase:firebase-auth##21.0.6:1)
at com.google.android.gms.internal.firebase-auth-api.zzuq.zzh(com.google.firebase:firebase-auth##21.0.6:25)
at com.google.android.gms.internal.firebase-auth-api.zzts.zzh(com.google.firebase:firebase-auth##21.0.6:1)
at com.google.android.gms.internal.firebase-auth-api.zzqh.zza(com.google.firebase:firebase-auth##21.0.6:2)
at com.google.android.gms.internal.firebase-auth-api.zzvb.zza(com.google.firebase:firebase-auth##21.0.6:16)
at com.google.android.gms.internal.firebase-auth-api.zzuh.zzs(com.google.firebase:firebase-auth##21.0.6:4)
at com.google.android.gms.internal.firebase-auth-api.zzrx.zzC(com.google.firebase:firebase-auth##21.0.6:5)
at com.google.android.gms.internal.firebase-auth-api.zztt.zzw(com.google.firebase:firebase-auth##21.0.6:8)
at com.google.android.gms.internal.firebase-auth-api.zztb.zzc(com.google.firebase:firebase-auth##21.0.6:1)
at com.google.android.gms.internal.firebase-auth-api.zzuu.run(com.google.firebase:firebase-auth##21.0.6:1)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
The problem only occurs when you use the emulator but on the actual device it's working and updating firebase authentication and realtime database as expected.
Update: Okay I still get this message, but everything is working again. I'm using a Ryzen CPU and installed the "android emulator hypervisor driver for amd processors". Together with the latest emulator patches, everything is working again.
I have the same problem and can only test an a real device since the emulator won't connect to Firebase after succesful Google Sign In.
Additionally I now upgraded my dependencies and the Anrdoird Target SDK.
Also the manifest containst the cleartext attribute.
Nonetheless this is currently not working.
I am a complete novice in Java and Android. I am trying to create a test app to listen for BLE and BT devices nearby. I have another device where I wrote some logic to broadcast its BLE beacons. I verified it using a playstore app. Now I am trying to write my own app on Android.
I have been reading the Android developer pages for guidance. I have literally followed every step of the following pages
https://developer.android.com/guide/topics/connectivity/bluetooth/setup
https://developer.android.com/guide/topics/connectivity/bluetooth/permissions
https://developer.android.com/guide/topics/connectivity/bluetooth/find-bluetooth-devices
https://developer.android.com/guide/topics/connectivity/bluetooth/find-ble-devices
Also, Note that I have used BARE MINIMUM CODE from the Android Developers page So here is what I have done.
1. First off I have added my permissions under AndroidManifest
Note1 : I am deploying this app to My phone running Android 11
Note2 : All this code is written inside MainActivity. I have not created any other activity class
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
2. Next I check if my BT is enabled.
if (bluetoothAdapter == null) {
blefinder.append("\nDEVICE DOES NOT SUPPORT BLUETOOTH");
}
else {
blefinder.append("\nDEVICE SUPPORTS BLUETOOTH");
}
I get the success message that BT is of course enabled
3. Next I check if my device supports BLE
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
blefinder.append("\nBLE NOT SUPPORTED ON THIS DEVICE : ");
finish();
}
else{
blefinder.append("\nBLE IS SUPPORTED ON THIS DEVICE : ");
}
I get the message that BLE is supported
4. Next I list my already paired/bonded devices
For this I call ListPairedAndBondedDevices(); in onCreate() itself right after the above steps. Function Definition Below.
private void ListPairedAndBondedDevices(){
#SuppressLint("MissingPermission") Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
// There are paired devices. Get the name and address of each paired device.
blefinder.append("\nPAIRED/BONDED DEVICES");
for (BluetoothDevice device : pairedDevices) {
blefinder.append("\n" + device.getName() + " | " + device.getAddress());
}
}
}
This also works like a charm and prints out my paired devices. The next 2 parts is where I face the problem.
5. The Problem Step | Part 1:
Here I register a Broadcast receiver to discover all BT devices in the vicinity. I've unbonded my BT headphones and kept it in pairing mode to verify this.
ListPairedAndBondedDevices(); // From previous code snippet
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); // New code statement
registerReceiver(BTReceiver, filter);// New code statement
Broadcast Receiver implementation
private final BroadcastReceiver BTReceiver = new BroadcastReceiver() {
#SuppressLint("MissingPermission")
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// Discovery has found a device. Get the BluetoothDevice
// object and its info from the Intent.
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
blefinder.append("\n" + device.getName() + " | " + device.getAddress());
}
}
};
So This part didn't Work :(
If you see above, I am registering the BTReceiver in onCreate right after listing the already paired devices (by calling ListPairedAndBondedDevices()).
When I ran the debugger, this broadcast receiver never gets called.
6. The Problem Step | Part 2:
Right after this I try to scan for BLE Devices as well by callin scanLeDevice()
ListPairedAndBondedDevices(); // From previous snippet
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); // From previous snippet
registerReceiver(BTReceiver, filter);// From previous snippet
scanLeDevice(); // ---------------->>> CALLING THIS FUNCTION TO SCAN FOR BLE DEVICES
Implementation of scanLeDevice()
private void scanLeDevice() {
if (!scanning) {
// Stops scanning after a predefined scan period.
handler.postDelayed(new Runnable() {
#Override
public void run() {
scanning = false;
bluetoothLeScanner.stopScan(leScanCallback);
blefinder.append("\nSTOPPING BLE SCAN... TIMEOUT REACHED");
}
}, SCAN_PERIOD);
scanning = true;
bluetoothLeScanner.startScan(leScanCallback);
} else {
scanning = false;
bluetoothLeScanner.stopScan(leScanCallback);
blefinder.append("\nSTOPPING BLE SCAN");
}
}
Unfortunately this also fails. The debugger tells me that this part of the code is getting called.
And after 30 seconds of SCAN_PERIOD (The TIMEOUT that I've set), I get the message that the scanning has stopped (STOPPING BLE SCAN)
Now I have implemented the leScanCallback as well (i.e the Device Scan Callback)
private ScanCallback leScanCallback =
new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
blefinder.append("SOMETHING GOT SCANNED?");
blefinder.append("\n"+result.getDevice().toString());
// leDeviceListAdapter.addDevice(result.getDevice());
// leDeviceListAdapter.notifyDataSetChanged();
}
};
Notice that I am not using a ListAdapter since I have no idea about that concept. Hence for starters I am just trying to dump the results in a TextView represented by blefinder . This blefinder prints all the other texts so there is nothing wrong with that TextView variable. When I ran using the, debugger, it is not entering into the leScanCallback piece of code definition at all, even after 30 seconds, after scanLeDevice() function is executed.
I am a little lost here. Is there something I may be missing or doing wrong. It is supposed to be a simple, list the ble/bt devices around my vicinity.
I am happy to share any further information if I have missed. Just let me know in the comments.
Assuming you've done with the permissions that I've mentioned in the comments, we can implement a clean bluetooth LE scanner object and then use it in the UI.
First we implement a result consumer interface in order to deliver the results to the consumers which call the BleScanner.scan() method.
public interface ScanResultConsumer {
public void onDeviceFound(BluetoothDevice device, byte[] scanRecord, int rssi);
public void onScanningStarted();
public void onScanningStopped();
}
Now we need to implement the scanner object that manages the scanning events:
public class BleScanner {
private static final String TAG = BleScanner.class.getSimpleName();
private BluetoothLeScanner leScanner = null;
private BluetoothAdapter bleAdapter = null;
private Handler uiHandler = new Handler(Looper.getMainLooper);
private ScanResultConsumer scanResultConsumer;
private boolean scanning = false;
private final ArrayList<BluetoothDevice> foundDeviceList = new ArrayList<>();
public BleScanner(Context context) {
final BluetoothManager bluetoothManager = (BluetoothManager)
context.getSystemService(Context.BLUETOOTH_SERVICE);
bleAdapter = bluetoothManager.getAdapter();
if(bleAdapter == null) {
Log.d(TAG, "No bluetooth hardware.");
}
else if(!bleAdapter.isEnabled()){
Log.d(TAG, "Blutooth is off.");
}
}
public void scan(ScanResultConsumer scanResultConsumer, long scanTime){
foundDeviceList.clear();
if (scanning){
Log.d(TAG, "Already scanning.");
return;
}
Log.d(TAG, "Scanning...");
if(leScanner == null){
leScanner = bleAdapter.getBluetoothLeScanner();
}
if(scanTimeMs > 0) {
uiHandler.postDelayed(()-> {
if (scanning) {
Log.d(TAG, "Scanning is stopping.");
if(leScanner != null)
leScanner.stopScan(scanCallBack);
else
Log.d(TAG,"Scanner null");
setScanning(false);
}
}, scanTimeMs);
}
this.scanResultConsumer = scanResultConsumer;
leScanner.startScan(scanCallBack);
setScanning(true);
}
private final ScanCallback scanCallBack = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
if (!scanning){
return;
}
if(foundDeviceList.contains(result.getDevice())) {
// This device has already been found
return;
}
// New device found, add it to the list in order to prevent duplications
foundDeviceList.add(result.getDevice());
if(scanResultConsumer != null) {
uiHandler.post(() -> {
scanResultConsumer.onDeviceFound(result.getDevice(),
result.getScanRecord().getBytes(), result.getRssi());
});
}
}
};
public boolean isScanning(){
return scanning;
}
void setScanning(boolean scanning){
this.scanning = scanning;
uiHandler.post(() -> {
if(scanResultConsumer == null) return;
if(!scanning){
scanResultConsumer.onScanningStopped();
// Nullify the consumer in order to prevent UI crashes
scanResultConsumer = null;
} else{
scanResultConsumer.onScanningStarted();
}
});
}
}
Finally we can use this clean implementation in anywhere we need. But do note that a context must be provided in order to create a BleScanner object.
public class MainActivity extends AppCompatActivity {
private BleScanner bleScanner;
private Button buttonScan
// Other codes...
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Other codes...
bleScanner = new BleScanner(getApplicationContext());
// Other codes...
// For example if you want to start scanning on a button press
// Let's say you have a button called buttonScan and initiated it
buttonScan = findViewById(R.id.scan_button);
buttonScan.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
bleScanner.scan(new ScanResultConsumer {
#Override
public void onDeviceFound(BluetoothDevice device, byte[] scanRecord, int rssi) {
// TODO Here you have a newly found device, do something.
}
#Override
q public void onScanningStarted() {
// TODO Scanning has just started, you may want to make some UI changes.
}
#Override
public void onScanningStopped() {
// TODO Scanning has just stopped, you may want to make some UI changes.
}
});
}
});
}
}
Note: I written this code in a plain editor not in Android Studio. So there may be some errors, let me know if any.
First you should check if your app was granted the location permission(s) in the Settings app > Apps <your_app> > permissions. Some permissions (like ACCESS_*_LOCATION and BLUETOOTH_ADMIN) need to be requested at runtime and granted by the user through a popup. Normally you should get a SecurityException or a logcat warning when trying to execute code requiring permissions which your app doesn't have, but it's not uncommon for android to skip over error handling.
Consider using this method to start the scan in order check its result code for potential additional info about what is (not) going on.
You might also get some clues by logging all actions received in BTReceiver.onReceive(), not just action found.
Lastly check if the location settings on your device to ensure that bluetooth scanning is turned on (Settings app > location > wifi and bluetooth scanning )
The problem is that, during transition from firstActivity to thisServiceActivity, the screen will freeze and then turn black. If service has performed it process and send the broadcast and receive by the broadcast receiver of thisServiceActivity, then it will finally show the layout of the thisServiceActivity and quickly transition to ResultActivity.
Is it because there is something I do wrong in its UI thread? I try to find similar discussion to this problem but can't find it.
firstActivity class will start a new activity which is thisServiceActivity class. thisServiceActivity have a layout that show a progressbar and textview. At the same time, it will start a service class name as myForegroundService with pending intent notification.
If the process in the service completed, the service will send a local broadcast that will be pickup by thisServiceActivity using broadcast receiver and it then stop the service and transition to resultActivity and finish its activity.
public class firstActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first_activity);
}
//using button onclick
public void startActivity(View view) {
Intent startService = new Intent(this, thisServiceActivity.class);
startActivity(startService);
}
}
package example.myapplication;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
public class thisServiceActivity extends AppCompatActivity {
Intent goToResult, stopMyService, startMyService;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_this_service);
LocalBroadcastManager.getInstance(this).registerReceiver(myForegroundReceiver,
new IntentFilter("end-of-process"));
startService();
}
public void startService() {
startMyService = new Intent(this, myForegroundService.class);
ContextCompat.startForegroundService(this, startMyService);
}
public void stopService() {
stopMyService = new Intent(this, myForegroundService.class);
stopService(stopMyService);
}
#Override
protected void onDestroy() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(myForegroundReceiver);
super.onDestroy();
}
private BroadcastReceiver myForegroundReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.i("onReceive", "Received");
stopService();
goToResult = new Intent(getApplication(), ResultActivity.class);
startActivity(goToResult);
finish();
}
};
}
package example.myapplication;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.IBinder;
import android.os.SystemClock;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import java.util.concurrent.ExecutionException;
import static example.myapplication.App.CHANNEL_ID;
public class myForegroundService extends Service {
Boolean process;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("Service Running", "True");
Intent toThisServiceActivity = new Intent(this, thisServiceActivity.class);
PendingIntent servicePendingIntent = PendingIntent.getActivity(this, 0, toThisServiceActivity, 0);
Notification notify = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Sit back and relax, process is in progress")
.setContentText("You look more charming if you have patient, please wait")
.setContentIntent(servicePendingIntent)
.build();
startForeground(1, notify);
try {
process = new MyLoop().execute().get();
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
if (process) {
Intent killswitch = new Intent("end-of-process");
Log.i("Finish", "Service");
LocalBroadcastManager.getInstance(this).sendBroadcast(killswitch);
}
return START_NOT_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
private class MyLoop extends AsyncTask<Void, String, Boolean> {
#Override
protected Boolean doInBackground(Void... voids) {
for (int i = 0; i < 10; i++) {
publishProgress("i = " + i);
SystemClock.sleep(1000);
}
return true;
}
#Override
protected void onProgressUpdate(String... values) {
String value = values[0];
Log.i("Foreground Service", value);
}
}
}
Logcat output
02/25 17:41:41: Launching 'app' on Google.
$ adb shell am start -n "example.myapplication/example.myapplication.firstActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER
Waiting for process to come online...
Connected to process 5060 on device '-google-192.168.42.106:5555'.
Capturing and displaying logcat messages from application. This behavior can be disabled in the "Logcat output" section of the "Debugger" settings page.
I/Zygote: seccomp disabled by setenforce 0
I/e.myapplicatio: Late-enabling -Xcheck:jni
W/e.myapplicatio: Unexpected CPU variant for X86 using defaults: x86
D/libEGL: Emulator has host GPU support, qemu.gles is set to 1.
I/example.myapplication: type=1400 audit(0.0:1122): avc: denied { write } for comm=45474C20496E6974 name="property_service" dev="tmpfs" ino=9335 scontext=u:r:untrusted_app:s0:c96,c256,c512,c768 tcontext=u:object_r:property_socket:s0 tclass=sock_file permissive=1
type=1400 audit(0.0:1123): avc: denied { connectto } for comm=45474C20496E6974 path="/dev/socket/property_service" scontext=u:r:untrusted_app:s0:c96,c256,c512,c768 tcontext=u:r:init:s0 tclass=unix_stream_socket permissive=1
D/vndksupport: Loading /vendor/lib/egl/libGLES_emulation.so from current namespace instead of sphal namespace.
E/libEGL: load_driver(/vendor/lib/egl/libGLES_emulation.so): dlopen failed: library "/vendor/lib/egl/libGLES_emulation.so" not found
D/vndksupport: Loading /vendor/lib/egl/libEGL_emulation.so from current namespace instead of sphal namespace.
D/libEGL: loaded /vendor/lib/egl/libEGL_emulation.so
D/vndksupport: Loading /vendor/lib/egl/libGLESv1_CM_emulation.so from current namespace instead of sphal namespace.
D/libEGL: loaded /vendor/lib/egl/libGLESv1_CM_emulation.so
D/vndksupport: Loading /vendor/lib/egl/libGLESv2_emulation.so from current namespace instead of sphal namespace.
D/libEGL: loaded /vendor/lib/egl/libGLESv2_emulation.so
W/e.myapplicatio: Accessing hidden method Landroid/view/View;->computeFitSystemWindows(Landroid/graphics/Rect;Landroid/graphics/Rect;)Z (light greylist, reflection)
W/e.myapplicatio: Accessing hidden method Landroid/view/ViewGroup;->makeOptionalFitsSystemWindows()V (light greylist, reflection)
D/OpenGLRenderer: Skia GL Pipeline
D/: HostConnection::get() New Host Connection established 0xe7b6d5c0, tid 5087
I/RenderThread: type=1400 audit(0.0:1124): avc: denied { connectto } for path=006C6F63616C5F6F70656E676C scontext=u:r:untrusted_app:s0:c96,c256,c512,c768 tcontext=u:r:local_opengl:s0 tclass=unix_stream_socket permissive=1
W/: Unrecognized GLES max version string in extensions:
W/: Process pipe failed
I/ConfigStore: android::hardware::configstore::V1_0::ISurfaceFlingerConfigs::hasWideColorDisplay retrieved: 0
android::hardware::configstore::V1_0::ISurfaceFlingerConfigs::hasHDRDisplay retrieved: 0
I/OpenGLRenderer: Initialized EGL, version 1.4
D/OpenGLRenderer: Swap behavior 1
D/EGL_emulation: eglCreateContext: 0xe1fc8e20: maj 2 min 0 rcv 2
D/vndksupport: Loading /vendor/lib/hw/android.hardware.graphics.mapper#2.0-impl.so from current namespace instead of sphal namespace.
D/vndksupport: Loading /vendor/lib/hw/gralloc.vbox86.so from current namespace instead of sphal namespace.
E/EGL_emulation: tid 5087: eglSurfaceAttrib(1354): error 0x3009 (EGL_BAD_MATCH)
W/OpenGLRenderer: Failed to set EGL_SWAP_BEHAVIOR on surface 0xe1fc8d60, error=EGL_BAD_MATCH
I/Choreographer: Skipped 31 frames! The application may be doing too much work on its main thread.
W/ActivityThread: handleWindowVisibility: no activity for token android.os.BinderProxy#112c025
E/EGL_emulation: tid 5087: eglSurfaceAttrib(1354): error 0x3009 (EGL_BAD_MATCH)
W/OpenGLRenderer: Failed to set EGL_SWAP_BEHAVIOR on surface 0xe0846b60, error=EGL_BAD_MATCH
I/Service Running: True
I/Finish: Service
W/ViewRootImpl[thisServiceActivity]: Dropping event due to no window focus: KeyEvent { action=ACTION_DOWN, keyCode=KEYCODE_BACK, scanCode=0, metaState=0, flags=0x48, repeatCount=0, eventTime=7949110, downTime=7949110, deviceId=-1, source=0x101 }
W/ViewRootImpl[thisServiceActivity]: Cancelling event due to no window focus: KeyEvent { action=ACTION_UP, keyCode=KEYCODE_BACK, scanCode=0, metaState=0, flags=0x68, repeatCount=0, eventTime=7950511, downTime=7949110, deviceId=-1, source=0x101 }
I/Choreographer: Skipped 625 frames! The application may be doing too much work on its main thread.
I/chatty: uid=10096(example.myapplication) identical 7 lines
W/ViewRootImpl[thisServiceActivity]: Cancelling event due to no window focus: KeyEvent { action=ACTION_UP, keyCode=KEYCODE_BACK, scanCode=0, metaState=0, flags=0x68, repeatCount=0, eventTime=7950511, downTime=7949110, deviceId=-1, source=0x101 }
I/OpenGLRenderer: Davey! duration=10603ms; Flags=0, IntendedVsync=7941734767100, Vsync=7952151433350, OldestInputEvent=9223372036854775807, NewestInputEvent=0, HandleInputStart=7952160308707, AnimationStart=7952160414256, PerformTraversalsStart=7952161151710, DrawStart=7952165102838, SyncQueued=7952174190092, SyncStart=7952180127743, IssueDrawCommandsStart=7952180180543, SwapBuffers=7952293099225, FrameCompleted=7952343853615, DequeueBufferDuration=63000, QueueBufferDuration=161000,
I/Foreground Service: i = 0
i = 1
I/Foreground Service: i = 2
i = 3
I/OpenGLRenderer: Davey! duration=10618ms; Flags=0, IntendedVsync=7941734767100, Vsync=7952151433350, OldestInputEvent=9223372036854775807, NewestInputEvent=0, HandleInputStart=7952160308707, AnimationStart=7952160414256, PerformTraversalsStart=7952161151710, DrawStart=7952344526709, SyncQueued=7952345327938, SyncStart=7952355852722, IssueDrawCommandsStart=7952355900616, SwapBuffers=7952356170648, FrameCompleted=7952363877516, DequeueBufferDuration=148000, QueueBufferDuration=246000,
I/Foreground Service: i = 4
I/Foreground Service: i = 5
i = 6
i = 7
i = 8
I/Foreground Service: i = 9
I/onReceive: Received
Process 5060 terminated.
You're blocking the UI thread for 10 seconds by waiting for the result of your AsyncTask:
process = new MyLoop().execute().get();
The Javadoc for get() states:
Waits if necessary for the computation to complete, and then retrieves its result.
This is eliminating any advantage to running it on a background thread and causing your app to freeze. If you need to do something with the result, you should do it in AsyncTask.onPostExecute.
I am working on an app that can connect to an adafruit flora BLE device to receive information from it. I want the app to display the list of found devices and when an item in the list view is clicked the connection is made and data can be received. Ultimately I want to take said data over to another activity to graph in realtime (if possible). There are a few things going on that i dont understand and I hope someone can shed light on.
When it is scanning for devices the list view shows them but there are multiples of each.
For some reason the onPause and onResume make the list view glitchy (displays devices and then removes them)
How do I know when there is a connection?
When I have getRemoteDevice in my code I get a runtime error ( Attempt to invoke virtual method 'android.bluetooth.BluetoothDevice android.bluetooth.BluetoothAdapter.getRemoteDevice(java.lang.String)' on a null object reference)
When I try and use filters and settings in the startScan method I get nothing in my list, I have also tried null filters w/ settings and still nothing.
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.content.Intent;
import android.os.Build;
import android.os.ParcelUuid;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import no.nordicsemi.android.support.v18.scanner.BluetoothLeScannerCompat;
import no.nordicsemi.android.support.v18.scanner.ScanFilter;
import no.nordicsemi.android.support.v18.scanner.ScanResult;
import no.nordicsemi.android.support.v18.scanner.ScanSettings;
public class BluetoothDiscovery extends AppCompatActivity {
private String TAG = "Bluetooth Device";
private int REQUEST_ENABLE_BT = 5;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothLeScannerCompat scanner;
private ScanSettings settings;
private UUID baseUUID = UUID.fromString("6e400001-b5a3-f393-e0a9-e50e24dcca9e"); // service UUID
private UUID txUUID = UUID.fromString("6e400002-b5a3-f393-e0a9-e50e24dcca9e"); // TX UUID characteristic
private UUID rxUUID = UUID.fromString("6e400003-b5a3-f393-e0a9-e50e24dcca9e"); // RX UUID characteristic
private ScanFilter scanFilter;
private BluetoothDevice device, mdevice;
private BluetoothGatt mGatt;
private boolean mScanning = false;
private ArrayList<deviceShowFormat> foundDevices = new ArrayList<>();
formattingAdapter BTadapter;
Button scanButton;
TextView fancyWords;
ListView deviceList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bluetooth_discovery);
mBluetoothAdapter.getDefaultAdapter();
scanButton = findViewById(R.id.scanButt);
scanButton.setText(getString(R.string.notScanning));
fancyWords = findViewById(R.id.discoverText);
fancyWords.setText(getString(R.string.nonScanTitle));
deviceList = findViewById(R.id.deviceList);
BTadapter = new formattingAdapter(BluetoothDiscovery.this, foundDevices);
deviceList.setAdapter(BTadapter);
scanner = BluetoothLeScannerCompat.getScanner();
settings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_BALANCED).setReportDelay(500).build();
scanFilter = new ScanFilter.Builder().setServiceUuid(new ParcelUuid(baseUUID)).build();
//scanner.startScan(Arrays.asList(scanFilter), settings, mScanCallback);
deviceList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#SuppressLint("LongLogTag")
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
deviceShowFormat mBTDevice = foundDevices.get(i);
BluetoothDevice Device = mBTDevice.get_device();
String deviceName = mBTDevice.get_device_name();
String deviceAddress = mBTDevice.get_device_address();
Log.d(TAG, "Selected device: " + Device.toString());
Log.d(TAG, "Selected device name: " + deviceName);
Log.d(TAG, "Selected device address: " + deviceAddress);
//BluetoothDevice deviceConnect = mBluetoothAdapter.getRemoteDevice(deviceAddress);
//deviceConnect.createBond();
mGatt = Device.connectGatt(BluetoothDiscovery.this, false, mGattCallback);
Toast.makeText(BluetoothDiscovery.this, "Selected device: " + deviceName, Toast.LENGTH_SHORT).show();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Device.createBond();
}
Log.d(TAG, "" + Device.getBondState());
if(Device.getBondState() == BluetoothDevice.BOND_BONDED){
Toast.makeText(BluetoothDiscovery.this, "Bluetooth device connected successfully", Toast.LENGTH_SHORT).show();
}
mGatt.getServices();
mGatt.getConnectedDevices();
Log.d("THIS IS THE DEVICES UUID", String.valueOf(Device.getUuids()));
Log.d("DEVICE SERVICES", String.valueOf(mGatt.getServices()));
}
});
}
private final no.nordicsemi.android.support.v18.scanner.ScanCallback mScanCallback = new no.nordicsemi.android.support.v18.scanner.ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
Log.i("onScanResult", "device detected");
device = result.getDevice();
String deviceName = device.getName();
String deviceAddress = device.getAddress();
Log.d(TAG, "Scanned device: " + device.toString());
Log.d(TAG, "Scanned device name: " + deviceName);
Log.d(TAG, "Scanned device address: " + deviceAddress);
foundDevices.add(new deviceShowFormat(device, deviceName, deviceAddress));
BTadapter.notifyDataSetChanged();
}
};
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
Log.i("onConnectionStateChange", "State Changed from: " + status + " to " + newState);
if (newState == BluetoothProfile.STATE_CONNECTED){
Toast.makeText(BluetoothDiscovery.this, "Attempting service discovery", Toast.LENGTH_SHORT).show();
Log.i("onConnectionStateChange", "Attempting service discovery: " + gatt.discoverServices());
gatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED){
Toast.makeText(BluetoothDiscovery.this, "Connection has been terminated", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status){
super.onServicesDiscovered(gatt, status);
Log.i("onServicesDiscovered", "Hey, we found a service");
if (status != BluetoothGatt.GATT_SUCCESS){
// Handle error
Log.d("onServicesDiscovered" , "" + status);
return;
}
BluetoothGattCharacteristic characteristic = gatt.getService(baseUUID).getCharacteristic(rxUUID);
gatt.setCharacteristicNotification(characteristic, true);
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(txUUID);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status){
Log.i("onCharacteristicRead", "Characteristic has been read");
readCounterCharacteristic(characteristic);
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
if (mGatt.writeCharacteristic(characteristic)){
Log.d("Characteristic changed", "Possibly looking for a write");
}
if (mGatt.readCharacteristic(characteristic)){
readCounterCharacteristic(characteristic);
}
}
private void readCounterCharacteristic(BluetoothGattCharacteristic characteristic){
if (mGatt.readCharacteristic(characteristic)){
byte[] data = characteristic.getValue();
Log.d("READ DATA", data.toString());
}
// if (rxUUID.equals(characteristic.getUuid())){
// //byte[] data = characteristic.getValue();
// byte[] data = mGatt.readCharacteristic(characteristic);
// //int value = Ints.fromByteArray(data);
// Log.d("READ DATA", data.toString());
// }
}
};
public void toggleScan(View view){
mScanning = !mScanning;
if(mScanning){
scanner.startScan(mScanCallback); //Arrays.asList(scanFilter) null, settings,
scanButton.setText(getString(R.string.scanInProgress));
fancyWords.setText(getString(R.string.ScanTitle));
} else {
scanner.stopScan(mScanCallback);
scanButton.setText(getString(R.string.notScanning));
}
}
// #Override
// public void onPause(){
// super.onPause();
//
//// if(mScanning){
//// mScanning = !mScanning;
// scanner.stopScan(mScanCallback);
//// }
//
// //Empty Adapter
// //BTadapter.clear();
// //BTadapter.notifyDataSetChanged();
//
// //mdevice = device;
//
// }
//
// #Override
// public void onResume(){
// super.onResume();
//
// if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
// Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
// startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
// }
//
// //device = mdevice;
// }
}
The code is the second activity in my app, in the first bluetooth is initialized and what-not. The above code works but I dont receive any data from the device and am not sure its truly connected. from the logs shown in the code I get:
Logcat
Logcat2
Resources im using:
https://github.com/NordicSemiconductor/Android-Scanner-Compat-Library
https://learn.adafruit.com/introducing-the-adafruit-bluefruit-le-uart-friend/uart-service
http://nilhcem.com/android-things/bluetooth-low-energy
You missed to initialize mBluetoothAdapter
As of (at oncreate):
BluetoothManager manager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = manager.getAdapter();
instead of:
mBluetoothAdapter.getDefaultAdapter();
After that your variable is initialized and ready to use, just check if you need RuntimePermissions for using bluetooth.
Much of the stuff in android BLE communication is asynchronous.
So when you call Device.connectGatt(), you need to wait for the onConnectionStateChange callback before doing the next thing. Your attempts to enumerate the services will fail (probably quietly) because your connection state hasn't gone from 0 to 2 at that point. In your log you can see that you do the getServices() call before it has called you back to say the connection state has gone to connected.
Making things trickier, in my experience you should only be doing your BluetoothDevice interaction from the main thread. (Edit: Only sending requests from the main thread is apparently not a requirement)
So each command you do (connecting, reading characteristic values, writing, disconnecting, enumerating services, etc) needs to look like:
Main Thread: Call the (connect|read|write|enumerate) function
Callback thread: Java calls your BluetoothGattCallback, and you should find a way (Handler?) to signal the main thread to do your next thing.
Main thread: process the result from BluetoothGattCallback and trigger the next thing you'll be doing.
Mild aside: This is a lot easier in objective-c and react-native-ble-plx, because they handle the threading for you. If you design a good way to get the results from the callback thread to the main thread from the beginning, you will save yourself a lot of pain.
Example code for getting stuff back to the UI thread:
Handler m_handler;
public void onResume() {
m_handler = new Handler(); // creates a Handler bound to the UI thread, which lets us fire messages back and forth between threads.
}
// ... other stuff ...
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
Log.i("onConnectionStateChange", "State Changed from: " + status + " to " + newState);
if (newState == BluetoothProfile.STATE_CONNECTED){
// oh boy, we're connected. Let's try to discover services!
// m_handler.post() is a good way to make sure the thing you pass runs on the UI thread.
m_handler.post(new Runnable() {
#Override
public void run() {
gatt.discoverServices();
}
})
}
}
For anyone looking this up in the future: If you receive a message similar to D/BluetoothGatt: onConnectionUpdated() - Device=xx:xx:xx:xx:xx:xx interval=6 latency=0 timeout=500 status=0 you are connecting to the device but not "enough" to recieve info. For me it turned out i had to click the device to connect to a second time to actually recieve the data. I dont know why yet
in your answer
For anyone looking this up in the future: If you receive a message
similar to D/BluetoothGatt: onConnectionUpdated() -
Device=xx:xx:xx:xx:xx:xx interval=6 latency=0 timeout=500 status=0 you
are connecting to the device but not "enough" to recieve info. For me
it turned out i had to click the device to connect to a second time to
actually recieve the data. I dont know why yet
I Unfortunately got this problem, you can see in my question
BLE onDescriptorWrite is not trigger when writeDescriptor receive true
And What do you say is the second click device, your app or other ble device.
Thanks a lot.
I'm trying to get a notification when input devices are added/removed, and from what I understand that's what registerInputDeviceListener should do... but my listener isn't being called!
Here's a snippet of my code:
InputManager im = (InputManager) getSystemService(Context.INPUT_SERVICE);
im.registerInputDeviceListener(new InputManager.InputDeviceListener() {
#Override
public void onInputDeviceAdded(int deviceId) {
Log.d("Input", "InputDeviceAdded: " + deviceId);
}
#Override
public void onInputDeviceRemoved(int deviceId) {
Log.d("Input", "InputDeviceRemoved: " + deviceId);
}
#Override
public void onInputDeviceChanged(int deviceId) {
Log.d("Input", "InputDeviceChanged: " + deviceId);
}
}, null);
And here's what I see in logcat when I unplug my usb mouse:
01-15 19:19:04.025: INFO/EventHub(5935): Removing device '/dev/input/event0' due to inotify event
01-15 19:19:04.025: INFO/EventHub(5935): Removed device: path=/dev/input/event0 name=Primax USB OPTICAL MOUSE id=11 fd=245 classes=0x80000008
01-15 19:19:04.045: INFO/InputReader(5935): Device removed: id=11, name='Primax USB OPTICAL MOUSE', sources=0x00002002
But my listener never gets called...
Turns out that the InputManager doesn't register itself for device changes unless getInputDevice or getInputDeviceByDescriptor is called previously.
Calling getInputDevice first (and ignoring the result) makes my callbacks get called.