I want to be able to trigger an event when a user connects or disconnects a USB device. I have the following code:
public static boolean isConnected(Context context) {
Intent intent = context.registerReceiver(null, new IntentFilter("android.hardware.usb.action.USB_STATE"));
return intent.getExtras().getBoolean("connected");
}
The above is from another StackOverflow question.
How can I go about the problem I'm facing? I don't think it would be practical to check every x second if the state has changed. So, I checked online for an event listener about USB_CONNECTION, but I can't seem to find any. Actually, I did find a few but they were confusing and quite old. (A year or two ago.)
TRIED CODE
while(notFinished) {
if (!isConnected(this)) {
pause();
} else if (!mediaPlayer.isPlaying() && isConnected(this)) {
play();
} else {
Log.e("[ERROR]: ", "An error has occured at startSong!");
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
^^^ Clearly not efficient, but it does work (somewhat). So, that's why I came over here to know if there was a listener for USB_CONNECTION_STATUS. I think this would be a lot faster.
You can use BroadcastReceiver for receiving connected and disconnected actions:
BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getExtras().getBoolean("connected")) {
//start doing something for state - connected
} else {
//start doing something for state - disconnected
}
}
};
IntentFilter filter = new IntentFilter();
filter.addAction("android.hardware.usb.action.USB_STATE");
registerReceiver(receiver, filter);
This BroadcastReceiver will receive actions every time wen usb connected or disconnected.
Related
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 )
I created a floating widget that works even when the app closed. In addition I have a class that is responsible for detecting incoming and outgoing calls on the device And send a suitable toast. it also works when the app is closed.
Now I want the floating widget to appear on the screen when a call on the device ends.
I tried to do this but the widget does not appear. Both work separately, but not together.
There are two classs:
-Creates the widget : FloatingViewService extends Service.
-Call Recognition: CallReceiver extends BroadcastReceiver.
CallReceiver class
public class CallReceiver extends BroadcastReceiver {
// ^ detecting incoming and outgoing calls ^
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_OFFHOOK)){
showToast(context,"Call started...");
}
else if(intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_IDLE)){
showToast(context,"Call ended...");
//*****Activate the floating widget after the call ends.
startWidget( context );
}
else if(intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_RINGING)){
showToast(context,"Incoming call...");
}
}
//*****Activate widget
void startWidget(Context cotx) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { //Permission checks
if (Settings.canDrawOverlays(cotx)) {
// Start widget service
startService( new Intent( cotx, FloatingViewService.class));
}
}
}
//*****Action: Start a particular service
public ComponentName startService(Intent service) {
throw new RuntimeException("Stub!");
}
//Another action unrelated to my question
}
I believe the problem is related to the "startService" operation.
I was able to solve the problem for myself in a relatively simple way.
All I had to do was delete "startService" and update "startWidget" as follows:
void startWidget(Context cotx) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { //Permission checks
if (Settings.canDrawOverlays(cotx)) {
Intent intent = new Intent(cotx,FloatingViewService.class);
cotx.startService(intent);
Log.i("Autostart", "started");
}
}
}
public class GetReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
SharedPrefHelper sharedPrefHelper = new SharedPrefHelper(context);
NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
manager.cancel(1);
if (intent != null) {
if (intent.getStringExtra("result") == "ok") {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty(Web.Register.User_Id, sharedPrefHelper.getUserId());
jsonObject.addProperty(Web.BookingRequest.ACTIVITY, "ok");
new BasePresenter<>().createApiRequest(BaseApplication.getRetrofit().create(ApisHelper.class)
.okResponse(jsonObject), new BaseCallBack<BasicApiModel>() {
#Override
public void onCallBack(BasicApiModel output) {
if (Log.isLoggable("qwert", Log.DEBUG)) {
Log.d("qwert", "Receiver's onCallBack: " + output.getMessage());
}
}
});
} else {
Log.d("qwert", "onReceive: testing_cancelled");
}
}
}
what is the correct way of starting api request from receiver ?
Don't start any long running task in your broadcast receiver, otherwise your app may crash if onReceive runs more than 10 sec.
It's better to trigger a service from onReceive method and put your network operation inside the service.
Also keep in mind to use intent service / any other service depending on your requirement, with a worker thread to run your network operation so that your UI thread doesn't get blocked.
Is there anyway to check if onResume was called from the device waking up from sleep state in Android?
The reason why I need to check that is I don't want it to call a particular method if resumed from sleep state:
#Override
public void onResume() {
super.onResume();
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())
&& !SomeCrazyMethodOrPropertyCheckingIfDeviceWakedUpFromSleep) {
processIntent(getIntent());
}
}
You might say "Take that processintent method out of onResume"... It's not an option, NFC P2P mode requires you to process the received NDEF message inside onResume.
I would recommend overriding onNewIntent() to handle the NFC intents:
#Override
public void onNewIntent(final Intent intent) {
setIntent(intent);
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
processIntent(intent);
}
}
In processIntent() you can check whether the intent was handled already:
private void processIntent(final Intent intent) {
if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {
// Log.v(TAG, "Ignoring intent; already treated this intent.");
return;
}
intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
// new intent; process it
...
}
Likely this will solve your problem.
I think you can try to do something with ACTION_SCREEN_ON :
register a receiver for it (you need to it in code, it won't work in manifest).
in the onReceive do something like:
MY_FLAG_JUST_WAKE_UP = true;
and in the onResume() :
if(!MY_FLAG_JUST_WAKE_UP){
doStuff();
}
MY_FLAG_JUST_WAKE_UP = false;
But, it need to be tested, I don't know if you will always receive the intent before the onResume() get called.
I have a strange problem with my app. The App consists of one Service and one Activity, the service has to run "always" while the Activity is just an interface to control the service, and doesnt need to run a lot.
So I want to create the service in my Activity's onCreate() if it doesn't run yet, and then bind to it. The code looks like this:
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder binder) {
mService= ((MyService.LocalBinder) binder).getService();
mService.setView(ChatdroidActivity.this);
}
public void onServiceDisconnected(ComponentName className) {
Controler = null;
}
};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initUI();
if(!MyService.isRunning()){
Intent s= new Intent(this, MyService.class);
startService(s);
}
bindService(new Intent(this, MyService.class), mConnection,
Context.BIND_AUTO_CREATE);
while(mService == null){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
mService.doSomeStuff();
}
The problem is the Service doesn't seem to start. The app is in the while loop till i disconnect the debugger, after the Android message, that my app freezed.
And while I was searching the Web for a solution, suddenly my Eclipse came up, with an active breakpoint in my Service's onCreate()-method, for like 2 seconds, then it terminates.
Logcat doesn't display any errors until I disconnect the debugger.
And the first line of my Services onCreate() is android.os.Debug.waitForDebugger();
Before I added that call, the debugger never attached to the service.
Does anyone know this problem or better, a solution?
What happens if you move the mService.doSomeStuff() to the onServiceConnected() ? You don't need to do a while() loop to wait for it
what does bindService return? It should return true in case nothing wrong happened
put some logging in onServiceConnected() to see when it gets called
what does your service do in the contructor? Is it hanging somewhere there?