I have prepared a simple test project for this question at GitHub.
I am trying to create an Android app, which would scan a QR code from a computer screen and then use the data (MAC address and PIN or hash) for easy pairing (bonding) with a Bluetooth device.
Similar to the popular InstaWifi app - but for Classic Bluetooth.
For testing purposes I don't do any barcode scanning yet, but just display a list of devices:
After user touches one of the devices, pairing is tried in MainActivity.java:
private void startBluetoothPairing(BluetoothDevice device) {
Intent pairingIntent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
pairingIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
pairingIntent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
BluetoothDevice.PAIRING_VARIANT_PIN);
pairingIntent.putExtra(BluetoothDevice.EXTRA_PAIRING_KEY, 1234);
//device.setPin(new byte[]{1,2,3,4}); <- DOES NOT CHANGE ANYTHING
//device.setPairingConfirmation(false);
startActivityForResult(pairingIntent, REQUEST_BT_SETTINGS);
}
Unfortunately, the popup still asks for PIN:
Because I have actually specified a PIN in my source code, I was actually expecting another, simpler system dialog to be shown (this one is shown when doing NFC OOB pairing):
From searching for solutions, I know that there is a setPin() method, but it is not applicable here (or is it?) - because I am trying to pair the whole smartphone to the Bluetooth device and not just the app...
My question: How to make Android OS to show the simple Cancel/Pair dialog?
Searching for Bluetooth pairing request string at GitHub has not shown any hints...
UPDATE: On unrealsoul007's suggestion (thanks) I have update the source code in MainActivity.java and now the simple Cancel/Pair dialog is displayed:
private void startBluetoothPairing(BluetoothDevice device) {
Intent pairingIntent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
pairingIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
pairingIntent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION);
pairingIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivityForResult(pairingIntent, REQUEST_BT_PAIRING);
}
However I am not sure how to complete the pairing process - because onActivityResult is called with resultCode=0 even before the dialog is closed:
#Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
// this is called before user clicks Cancel or Pair in the dialog
if (requestCode == REQUEST_BT_PAIRING) {
if (resultCode == Activity.RESULT_OK) { // 0 != -1
Log.d("XXX", "Let#s pair!!!!"); // NOT CALLED
}
return;
}
}
You are being prompted for entering the pin because that is what you are requesting in your pairingIntent.
Instead of using
pairingIntent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
BluetoothDevice.PAIRING_VARIANT_PIN);
pairingIntent.putExtra(BluetoothDevice.EXTRA_PAIRING_KEY, 1234);
Use
pairingIntent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, PAIRING_VARIANT_PASSKEY_CONFIRMATION);
As mentioned here,
The user will be prompted to confirm the passkey displayed on the
screen or an app will confirm the passkey for the user.
Related
I've got an app which connect itself programatically to a wifi connection. My problem is, I want to handle the case, that the password is wrong. I want to detect that the password is not correct in runtime. To be precise I've got a progressdialog running while the connection is established, so if the password is wrong the progressdialog is just shown all the time and can't be skipped. A further note: I handled a password which is less than 8 characters by using this code:
if(!m_wifiManager.enableNetwork(netId, true)) {
progressDialogConnecting.dismiss();
createInfoMessageDialog(CONST.WIFI_CON_FAILED_TITLE, CONST.WIFI_CON_FAILED_MSG_CONFAILURE);
m_wifiManager.reconnect();
return;
}
If the key for the wifi connection is less than 8 characters, this if-case gets triggered. But if it is longer than 8 characters and wrong I get an endless state of showing the progress dialog.
What I exactly want to ask: how do I handle 1. wrong password 2. connection states (just like Android system showing me the toasts "Connected to Wifi xyz") ? AND is it even possible to handel the first one (wrong password)?
Here is the code, that did not work for handling connection established event (this is just the wifirecevier, I also registered it in the activity):
public class WifiReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION)) {
if (intent.getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false)){
if(wrongNetworkConnected)
progressDialogConnecting.dismiss();
}
}
} else {
}
}
}
}
Edit: What I am currently doing, is that I have a Handler which tells me to whom I am connected. That's useful because I can say that after the reconnect() I am reconnected to the old network (current network) and not the new one - so apparently the password could be wrong (or something else), because I could not connect to the new network.
The problem about this method is that first of all it takes too much time and secondly it is not reliable. I can lie and say that if you will get reconnected to your current network it is the fault of a wrong password, but actually it is not 100% sure that you cannot reconnect because of this - it may also have other reasons. So I am still searching for a simple feedback/handle from the suplicant that the password is wrong, just like the android api does in the wifi settings of each android device...
My problem is, I want to handle the case, that the password is wrong.
After some research I found this post which is not marked as answered but it still worked for me very well.
Here is the if-case in which the program jumps (already tested several times by me) if there is an authentication error --> e.g. wrong password:
int supl_error=intent.getIntExtra(WifiManager.EXTRA_SUPPLICANT_ERROR, -1);
if(supl_error==WifiManager.ERROR_AUTHENTICATING){
// DO SOMETHING
}
NOTE: As seen in the linked post above this if-case should appear in a BroadcastReceiver adding the intent WifiManager.SUPPLICANT_STATE_CHANGED_ACTIONto the receiver-registration in your activity-class.
We have a custom scanner to scan barcode using, which works with SOFT trigger (Using App Button) by using Motorola's emdk library.
barcodeManager = (BarcodeManager) this.emdkManager.getInstance(EMDKManager.FEATURE_TYPE.BARCODE);
scanner = barcodeManager.getDevice(BarcodeManager.DeviceIdentifier.DEFAULT);
scanner.addStatusListener(articleListener);
scanner.addDataListener(new Scanner.DataListener() {
#Override
public void onData(ScanDataCollection scanDataCollection) {
processData(scanDataCollection);
}
});
scanner.addDataListener(dataListener);
scanner.triggerType = Scanner.TriggerType.SOFT_ALWAYS;
scanner.enable();
How can i have both soft and Hard trigger to scan the data?
and with datalistener process the data received from both?
Zebra Technologies acquire Motorola Solution enterprise business in Oct. 2014, most of the updated documentation is now available under the Zebra Launchpad.
Scanner.TriggerType controls how you want to activate the barcode scanner on Zebra Android devices, usually you can set it up or Hard (scan is activated pressing the hardware trigger button) or Soft (scan is activated as soon as you call the Scanner.read() method).
To have an application that can use the Hardware trigger and having an on screen button to activate the scanner you can leave set the triggerType to Scanner.TriggerType.HARD and implement a login in the click event handler for the soft scan button so that you set the TriggerType to Scanner.TriggerType.SOFT_ONCE and then you call the Scanner.read() method. You can eventually check if there's another read active.
This is a sample implementation that you can test adding a button in the Barcode API sample included in the EMDK for Android (latest is v4.0):
private void softScan() {
if (scanner != null) {
try {
// Reset continuous flag
bContinuousMode = false;
if (scanner.isReadPending()) {
// Cancel the pending read.
scanner.cancelRead();
}
scanner.triggerType = TriggerType.SOFT_ONCE;
scanner.read();
new AsyncUiControlUpdate().execute(true);
} catch (ScannerException e) {
textViewStatus.setText("Status: " + e.getMessage());
}
}
}
So, usually you work with a TriggerType.HARD, but when you press the SCAN button you disable any pending read and you switch to TriggerType.SCAN_ONCE.
The implementation of the status listener needs to switch back the scanner to TriggerType.HARD and call the read() method.
You can find a complete sample at this github repository where I've added a Soft Scan button to the standard Zebra's EMDK Barcode API sample.
All the data are received by the same Data Listener.
I created an Activity with a button, which when clicked, starts an Intent to launch the voice input as follows:
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
intent.putExtra(RecognizerIntent.EXTRA_PROMPT, "Speak Now");
startActivityForResult(intent, SPEECH_REQUEST_CODE);
Then, I get the results and display them in a dialog so that the user can select the closest match.
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == SPEECH_REQUEST_CODE && resultCode == RESULT_OK && data != null) {
final ArrayList<String> list_voice_input = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
// I use this ArrayList to create a dialog.
}
super.onActivityResult(requestCode, resultCode, data);
}
So the Activity works as follows: Click the button, speak, and then a dialog pops up listing the closest matches and you can select one.
I have used a button-click to implement this. But Android's keyboard already has inbuilt voice input. When I use it to speak, it types out what I am saying, automatically detecting the closest match.
What I need:
I wish to make use of the default keyboard instead of a button, detecting when the user has chosen to speak instead of type (in a text field), and display a dialog box with the closest matches.
Any ideas on how I can do this?
Edit:
My question was marked as a duplicate, but I am not trying to get rid of the pop-up dialog that comes when you are recording your voice. My question is different.
When you are typing text, in the Android keyboard, there is a voice input option already inbuilt. You can use that to speak text. So, my question is, instead of automically printing out the closest match, can I show the user a list of closest matches in a dialog and ask them to select one?
No, you can't change the behavior of the default keyboard like that. There's no API to do so, to override what a single key does. If you want this you need to interact with the speech to text API yourself, without using the keyboard. This is built into Android (assuming the phone has a voice provider downloaded), and the API allows the voice provider to return multiple possible results. Whether a given voice provider does or not (remember the user and OEM can install any speech to text provider they want) is something you'd have to experiment and find out.
I have a working application that I would like to add voice commands. The current application transmits data back and forth over bluetooth on a periodic (timer) basis. The user can press Buttons and NumberPickers to modify the data being sent over bluetooth. There is also data received from the bluetooth link, and displayed in textViews. This application is currently working correctly.
What I would like to do is add voice command capability, so that the user has either the choice of pressing the Buttons/NumberPickers, or can change the values with only voice commands.
I have tested some of the Speech-to-Text examples that can be found on various websites. I have succesfully tested an App that uses RecognizerIntent. Upon a button press, a dialog pops up and you can speak words or phrases, and it correctly displays the result on the screen.
So, I think that I am close, but I'm not really sure how I can combine the Speech-to-Text with my current Bluetooth App. I don't want the user to have to press a button, I just want the App to be constantly listening. Also, I don't want the pop-up Voice Dialog on the screen.
My hardware is a Samsung tablet running Android 4.1.
I am relatively new to Android programming, so any advice (no matter how basic) is appreciated. Thanks.
To prevent the pop-up Voice Dialog on the screen, you can use the ACTION_RECOGNIZE_SPEECH intent:
private static int SR_CODE = 123;
/**
* Initializes the speech recognizer and starts listening to the user input
*/
private void listen() {
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
//Specify language
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.ENGLISH)
// Specify language model
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
// Specify how many results to receive
intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 5);
// Start listening
startActivityForResult(intent, SR_CODE);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == SR_CODE && resultCode == RESULT_OK) {
if(data!=null) {
//Retrieves the best list SR result
ArrayList<String> nBestList = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
String bestResult = nBestList.get(0);
Toast.makeText(getApplicationContext(), bestResult, Toast.LENGTH_LONG).show;
}else {
//Reports error in recognition error in log
Log.e(LOGTAG, "Recognition was not successful");
}
}
Concerning the other issue, " I don't want the user to have to press a button, I just want the App to be constantly listening":
I'll recommend using CMUSphinx to recognize speech continuously. To achieve continuous speech recognition using google speech recognition api, you might have to resort to a loop in a background service which will take too much resources and drains the device battery.
On the other hand, Pocketsphinx works really great. It's fast enough to spot a key phrase and recognize voice commands behind the lock screen without users touching their device. And it does all this offline.
You can try the demo.
If you really want to use google's api as I've demonstrated above, see this
I am programming a Wifi Direct game, but have run into a problem with the WifiP2pDevice Status when one user declines the invitation to join the connection.
Phone A initiates connection to Phone B
Phone B status becomes INVITED (as displayed on Phone A)
Phone B decline invitation from Phone A
Phone B status is still INVITED (as displayed on Phone A)
Shouldn't the status be shifted back to AVAILABLE as displayed on Phone A?
I have refreshed the list of Available devices, but the status remains unchanged. even if I restart the app, it still shows the status of Phone B as invited?
Is this supposed to happen based on the API of WifiDirect? or am I missing something?
Edit: More Information
In the BroadcastReciever, when the intent is WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION, then the manager requests the peers using a Fragment that implements a PeerListListener, which just prints a list of available devices and information (including status) of those devices. So when Phone A sends an invitation with manager.connect() it changes the status to INVITED. But if Phone B declines, the status remains as INVITED. And those statuses are triggered on a notifyDataSetChanged(). It is not so much a question about code, but how wifi direct determines and changes statuses of the devices. But I can provide code if needed.
You can cancel the connection if the status is "Invited"
if (fragment.getDevice().status == WifiP2pDevice.AVAILABLE
|| fragment.getDevice().status == WifiP2pDevice.INVITED) {
manager.cancelConnect(channel, new ActionListener() {
#Override
public void onSuccess() {
Toast.makeText(WiFiDirectActivity.this, "Aborting connection",
Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(int reasonCode) {
Toast.makeText(WiFiDirectActivity.this,
"Connect abort request failed. Reason Code: " + reasonCode,
Toast.LENGTH_SHORT).show();
}
});
}