I've got a problem with this tutorial from the Book "Getting Started with Bluetooth Low Energy".
I only want to scan for BLE devices and post the results via logcat. But anytime I do start the scan, the app shut down and give me a NullPointerException (See Logcat below). For this example I'm using the BleWrapper Class, but I also try to set up BLE Scan without it in another app. But it is always the same error... NullPointerException...
I also tried out 2 different demo apps, import them into my Android studio(v1.1) and they perform well. But if I try to write the same code in its own activity it will crash with this error.
I guess it's something wrong with the callback, or the scanning-method, maybe I forget some reference?
Here is my main activity:
package com.example.oliver.blebuch;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
public class MainActivity extends ActionBarActivity {
private BleWrapper mBleWrapper = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d("tag", "Test");
mBleWrapper = new BleWrapper(this, new BleWrapperUiCallbacks.Null()
{
#Override
public void uiDeviceFound(final BluetoothDevice device,
final int rssi,
final byte[] scanRecord)
{
Log.d("tag", "uiDeviceFound: "+device.getName()+", "+rssi+", "+scanRecord.toString());
}
});
if(mBleWrapper.checkBleHardwareAvailable() == false)
{
Toast.makeText(this,"No BLE-compatible hardware detected",
Toast.LENGTH_SHORT).show();
finish();
}
}
protected void onResume(){
super.onResume();
//check for Bluetooth enabled on each resume
if (mBleWrapper.isBtEnabled() == false)
{
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivity(enableBtIntent);
finish();
}
}
#Override
protected void onPause(){
super.onPause();
mBleWrapper.disconnect();
mBleWrapper.close();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId())
{
case R.id.action_scan:
mBleWrapper.startScanning();
break;
case R.id.action_stop:
mBleWrapper.stopScanning();
break;
default:
break;
}
return super.onOptionsItemSelected(item);
}
}
Logcat:
03-26 10:28:49.986 26288-26288/com.example.oliver.blebuch D/OpenGLRenderer﹕ Enabling debug mode 0
03-26 10:28:53.236 26288-26288/com.example.oliver.blebuch W/dalvikvm﹕ method Landroid/support/v7/internal/widget/ListViewCompat;.lookForSelectablePosition incorrectly overrides package-private method with same name in Landroid/widget/ListView;
03-26 10:28:53.266 26288-26288/com.example.oliver.blebuch D/AbsListView﹕ Get MotionRecognitionManager
03-26 10:28:54.366 26288-26288/com.example.oliver.blebuch D/AndroidRuntime﹕ Shutting down VM
03-26 10:28:54.366 26288-26288/com.example.oliver.blebuch W/dalvikvm﹕ threadid=1: thread exiting with uncaught exception (group=0x41783da0)
03-26 10:28:54.376 26288-26288/com.example.oliver.blebuch E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.example.oliver.blebuch, PID: 26288
java.lang.NullPointerException
at com.example.oliver.blebuch.BleWrapper.startScanning(BleWrapper.java:79)
at com.example.oliver.blebuch.MainActivity.onOptionsItemSelected(MainActivity.java:78)
at android.app.Activity.onMenuItemSelected(Activity.java:2708)
at android.support.v4.app.FragmentActivity.onMenuItemSelected(FragmentActivity.java:350)
at android.support.v7.app.ActionBarActivity.onMenuItemSelected(ActionBarActivity.java:155)
at android.support.v7.app.ActionBarActivityDelegate$1.onMenuItemSelected(ActionBarActivityDelegate.java:74)
at android.support.v7.app.ActionBarActivityDelegateBase.onMenuItemSelected(ActionBarActivityDelegateBase.java:556)
at android.support.v7.internal.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:802)
at android.support.v7.internal.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:153)
at android.support.v7.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:949)
at android.support.v7.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:939)
at android.support.v7.internal.view.menu.MenuPopupHelper.onItemClick(MenuPopupHelper.java:187)
at android.widget.AdapterView.performItemClick(AdapterView.java:308)
at android.widget.AbsListView.performItemClick(AbsListView.java:1495)
at android.widget.AbsListView$PerformClick.run(AbsListView.java:3453)
at android.widget.AbsListView$3.run(AbsListView.java:4816)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5479)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1283)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1099)
at dalvik.system.NativeStart.main(Native Method)
BLE Wrapper Class:
package com.example.oliver.blebuch;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
import android.app.Activity;
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.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.util.Log;
public class BleWrapper {
/* defines (in milliseconds) how often RSSI should be updated */
private static final int RSSI_UPDATE_TIME_INTERVAL = 1500; // 1.5 seconds
/* callback object through which we are returning results to the caller */
private BleWrapperUiCallbacks mUiCallback = null;
/* define NULL object for UI callbacks */
private static final BleWrapperUiCallbacks NULL_CALLBACK = new BleWrapperUiCallbacks.Null();
/* creates BleWrapper object, set its parent activity and callback object */
public BleWrapper(Activity parent, BleWrapperUiCallbacks callback) {
this.mParent = parent;
mUiCallback = callback;
if(mUiCallback == null) mUiCallback = NULL_CALLBACK;
}
public BluetoothManager getManager() { return mBluetoothManager; }
public BluetoothAdapter getAdapter() { return mBluetoothAdapter; }
public BluetoothDevice getDevice() { return mBluetoothDevice; }
public BluetoothGatt getGatt() { return mBluetoothGatt; }
public BluetoothGattService getCachedService() { return mBluetoothSelectedService; }
public List<BluetoothGattService> getCachedServices() { return mBluetoothGattServices; }
public boolean isConnected() { return mConnected; }
/* run test and check if this device has BT and BLE hardware available */
public boolean checkBleHardwareAvailable() {
// First check general Bluetooth Hardware:
// get BluetoothManager...
final BluetoothManager manager = (BluetoothManager) mParent.getSystemService(Context.BLUETOOTH_SERVICE);
if(manager == null) return false;
// .. and then get adapter from manager
final BluetoothAdapter adapter = manager.getAdapter();
if(adapter == null) return false;
// and then check if BT LE is also available
boolean hasBle = mParent.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
return hasBle;
}
/* before any action check if BT is turned ON and enabled for us
* call this in onResume to be always sure that BT is ON when Your
* application is put into the foreground */
public boolean isBtEnabled() {
final BluetoothManager manager = (BluetoothManager) mParent.getSystemService(Context.BLUETOOTH_SERVICE);
if(manager == null) return false;
final BluetoothAdapter adapter = manager.getAdapter();
if(adapter == null) return false;
return adapter.isEnabled();
}
/* start scanning for BT LE devices around */
public void startScanning() {
mBluetoothAdapter.startLeScan(mDeviceFoundCallback);
}
/* stops current scanning */
public void stopScanning() {
mBluetoothAdapter.stopLeScan(mDeviceFoundCallback);
}
/* initialize BLE and get BT Manager & Adapter */
public boolean initialize() {
if (mBluetoothManager == null) {
mBluetoothManager = (BluetoothManager) mParent.getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager == null) {
return false;
}
}
if(mBluetoothAdapter == null) mBluetoothAdapter = mBluetoothManager.getAdapter();
if (mBluetoothAdapter == null) {
return false;
}
return true;
}
/* connect to the device with specified address */
public boolean connect(final String deviceAddress) {
if (mBluetoothAdapter == null || deviceAddress == null) return false;
mDeviceAddress = deviceAddress;
// check if we need to connect from scratch or just reconnect to previous device
if(mBluetoothGatt != null && mBluetoothGatt.getDevice().getAddress().equals(deviceAddress)) {
// just reconnect
return mBluetoothGatt.connect();
}
else {
// connect from scratch
// get BluetoothDevice object for specified address
mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(mDeviceAddress);
if (mBluetoothDevice == null) {
// we got wrong address - that device is not available!
return false;
}
// connect with remote device
mBluetoothGatt = mBluetoothDevice.connectGatt(mParent, false, mBleCallback);
}
return true;
}
/* disconnect the device. It is still possible to reconnect to it later with this Gatt client */
public void disconnect() {
if(mBluetoothGatt != null) mBluetoothGatt.disconnect();
mUiCallback.uiDeviceDisconnected(mBluetoothGatt, mBluetoothDevice);
}
/* close GATT client completely */
public void close() {
if(mBluetoothGatt != null) mBluetoothGatt.close();
mBluetoothGatt = null;
}
/* request new RSSi value for the connection*/
public void readPeriodicalyRssiValue(final boolean repeat) {
mTimerEnabled = repeat;
// check if we should stop checking RSSI value
if(mConnected == false || mBluetoothGatt == null || mTimerEnabled == false) {
mTimerEnabled = false;
return;
}
mTimerHandler.postDelayed(new Runnable() {
#Override
public void run() {
if(mBluetoothGatt == null ||
mBluetoothAdapter == null ||
mConnected == false)
{
mTimerEnabled = false;
return;
}
// request RSSI value
mBluetoothGatt.readRemoteRssi();
// add call it once more in the future
readPeriodicalyRssiValue(mTimerEnabled);
}
}, RSSI_UPDATE_TIME_INTERVAL);
}
/* starts monitoring RSSI value */
public void startMonitoringRssiValue() {
readPeriodicalyRssiValue(true);
}
/* stops monitoring of RSSI value */
public void stopMonitoringRssiValue() {
readPeriodicalyRssiValue(false);
}
/* request to discover all services available on the remote devices
* results are delivered through callback object */
public void startServicesDiscovery() {
if(mBluetoothGatt != null) mBluetoothGatt.discoverServices();
}
/* gets services and calls UI callback to handle them
* before calling getServices() make sure service discovery is finished! */
public void getSupportedServices() {
if(mBluetoothGattServices != null && mBluetoothGattServices.size() > 0) mBluetoothGattServices.clear();
// keep reference to all services in local array:
if(mBluetoothGatt != null) mBluetoothGattServices = mBluetoothGatt.getServices();
mUiCallback.uiAvailableServices(mBluetoothGatt, mBluetoothDevice, mBluetoothGattServices);
}
/* get all characteristic for particular service and pass them to the UI callback */
public void getCharacteristicsForService(final BluetoothGattService service) {
if(service == null) return;
List<BluetoothGattCharacteristic> chars = null;
chars = service.getCharacteristics();
mUiCallback.uiCharacteristicForService(mBluetoothGatt, mBluetoothDevice, service, chars);
// keep reference to the last selected service
mBluetoothSelectedService = service;
}
/* request to fetch newest value stored on the remote device for particular characteristic */
public void requestCharacteristicValue(BluetoothGattCharacteristic ch) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) return;
mBluetoothGatt.readCharacteristic(ch);
// new value available will be notified in Callback Object
}
/* get characteristic's value (and parse it for some types of characteristics)
* before calling this You should always update the value by calling requestCharacteristicValue() */
public void getCharacteristicValue(BluetoothGattCharacteristic ch) {
if (mBluetoothAdapter == null || mBluetoothGatt == null || ch == null) return;
byte[] rawValue = ch.getValue();
String strValue = null;
int intValue = 0;
// lets read and do real parsing of some characteristic to get meaningful value from it
UUID uuid = ch.getUuid();
if(uuid.equals(BleDefinedUUIDs.Characteristic.HEART_RATE_MEASUREMENT)) { // heart rate
// follow https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml
// first check format used by the device - it is specified in bit 0 and tells us if we should ask for index 1 (and uint8) or index 2 (and uint16)
int index = ((rawValue[0] & 0x01) == 1) ? 2 : 1;
// also we need to define format
int format = (index == 1) ? BluetoothGattCharacteristic.FORMAT_UINT8 : BluetoothGattCharacteristic.FORMAT_UINT16;
// now we have everything, get the value
intValue = ch.getIntValue(format, index);
strValue = intValue + " bpm"; // it is always in bpm units
}
else if (uuid.equals(BleDefinedUUIDs.Characteristic.HEART_RATE_MEASUREMENT) || // manufacturer name string
uuid.equals(BleDefinedUUIDs.Characteristic.MODEL_NUMBER_STRING) || // model number string)
uuid.equals(BleDefinedUUIDs.Characteristic.FIRMWARE_REVISION_STRING)) // firmware revision string
{
// follow https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.manufacturer_name_string.xml etc.
// string value are usually simple utf8s string at index 0
strValue = ch.getStringValue(0);
}
else if(uuid.equals(BleDefinedUUIDs.Characteristic.APPEARANCE)) { // appearance
// follow: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.gap.appearance.xml
intValue = ((int)rawValue[1]) << 8;
intValue += rawValue[0];
strValue = BleNamesResolver.resolveAppearance(intValue);
}
else if(uuid.equals(BleDefinedUUIDs.Characteristic.BODY_SENSOR_LOCATION)) { // body sensor location
// follow: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.body_sensor_location.xml
intValue = rawValue[0];
strValue = BleNamesResolver.resolveHeartRateSensorLocation(intValue);
}
else if(uuid.equals(BleDefinedUUIDs.Characteristic.BATTERY_LEVEL)) { // battery level
// follow: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.battery_level.xml
intValue = rawValue[0];
strValue = "" + intValue + "% battery level";
}
else {
// not known type of characteristic, so we need to handle this in "general" way
// get first four bytes and transform it to integer
intValue = 0;
if(rawValue.length > 0) intValue = (int)rawValue[0];
if(rawValue.length > 1) intValue = intValue + ((int)rawValue[1] << 8);
if(rawValue.length > 2) intValue = intValue + ((int)rawValue[2] << 8);
if(rawValue.length > 3) intValue = intValue + ((int)rawValue[3] << 8);
if (rawValue.length > 0) {
final StringBuilder stringBuilder = new StringBuilder(rawValue.length);
for(byte byteChar : rawValue) {
stringBuilder.append(String.format("%c", byteChar));
}
strValue = stringBuilder.toString();
}
}
String timestamp = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss.SSS").format(new Date());
mUiCallback.uiNewValueForCharacteristic(mBluetoothGatt,
mBluetoothDevice,
mBluetoothSelectedService,
ch,
strValue,
intValue,
rawValue,
timestamp);
}
/* reads and return what what FORMAT is indicated by characteristic's properties
* seems that value makes no sense in most cases */
public int getValueFormat(BluetoothGattCharacteristic ch) {
int properties = ch.getProperties();
if((BluetoothGattCharacteristic.FORMAT_FLOAT & properties) != 0) return BluetoothGattCharacteristic.FORMAT_FLOAT;
if((BluetoothGattCharacteristic.FORMAT_SFLOAT & properties) != 0) return BluetoothGattCharacteristic.FORMAT_SFLOAT;
if((BluetoothGattCharacteristic.FORMAT_SINT16 & properties) != 0) return BluetoothGattCharacteristic.FORMAT_SINT16;
if((BluetoothGattCharacteristic.FORMAT_SINT32 & properties) != 0) return BluetoothGattCharacteristic.FORMAT_SINT32;
if((BluetoothGattCharacteristic.FORMAT_SINT8 & properties) != 0) return BluetoothGattCharacteristic.FORMAT_SINT8;
if((BluetoothGattCharacteristic.FORMAT_UINT16 & properties) != 0) return BluetoothGattCharacteristic.FORMAT_UINT16;
if((BluetoothGattCharacteristic.FORMAT_UINT32 & properties) != 0) return BluetoothGattCharacteristic.FORMAT_UINT32;
if((BluetoothGattCharacteristic.FORMAT_UINT8 & properties) != 0) return BluetoothGattCharacteristic.FORMAT_UINT8;
return 0;
}
/* set new value for particular characteristic */
public void writeDataToCharacteristic(final BluetoothGattCharacteristic ch, final byte[] dataToWrite) {
if (mBluetoothAdapter == null || mBluetoothGatt == null || ch == null) return;
// first set it locally....
ch.setValue(dataToWrite);
// ... and then "commit" changes to the peripheral
mBluetoothGatt.writeCharacteristic(ch);
}
/* enables/disables notification for characteristic */
public void setNotificationForCharacteristic(BluetoothGattCharacteristic ch, boolean enabled) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) return;
boolean success = mBluetoothGatt.setCharacteristicNotification(ch, enabled);
if(!success) {
Log.e("------", "Seting proper notification status for characteristic failed!");
}
// This is also sometimes required (e.g. for heart rate monitors) to enable notifications/indications
// see: https://developer.bluetooth.org/gatt/descriptors/Pages/DescriptorViewer.aspx?u=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
BluetoothGattDescriptor descriptor = ch.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
if(descriptor != null) {
byte[] val = enabled ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
descriptor.setValue(val);
mBluetoothGatt.writeDescriptor(descriptor);
}
}
/* defines callback for scanning results */
private BluetoothAdapter.LeScanCallback mDeviceFoundCallback = new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
mUiCallback.uiDeviceFound(device, rssi, scanRecord);
}
};
/* callbacks called for any action on particular Ble Device */
private final BluetoothGattCallback mBleCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
mConnected = true;
mUiCallback.uiDeviceConnected(mBluetoothGatt, mBluetoothDevice);
// now we can start talking with the device, e.g.
mBluetoothGatt.readRemoteRssi();
// response will be delivered to callback object!
// in our case we would also like automatically to call for services discovery
startServicesDiscovery();
// and we also want to get RSSI value to be updated periodically
startMonitoringRssiValue();
}
else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
mConnected = false;
mUiCallback.uiDeviceDisconnected(mBluetoothGatt, mBluetoothDevice);
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
// now, when services discovery is finished, we can call getServices() for Gatt
getSupportedServices();
}
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status)
{
// we got response regarding our request to fetch characteristic value
if (status == BluetoothGatt.GATT_SUCCESS) {
// and it success, so we can get the value
getCharacteristicValue(characteristic);
}
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic)
{
// characteristic's value was updated due to enabled notification, lets get this value
// the value itself will be reported to the UI inside getCharacteristicValue
getCharacteristicValue(characteristic);
// also, notify UI that notification are enabled for particular characteristic
mUiCallback.uiGotNotification(mBluetoothGatt, mBluetoothDevice, mBluetoothSelectedService, characteristic);
}
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
String deviceName = gatt.getDevice().getName();
String serviceName = BleNamesResolver.resolveServiceName(characteristic.getService().getUuid().toString().toLowerCase(Locale.getDefault()));
String charName = BleNamesResolver.resolveCharacteristicName(characteristic.getUuid().toString().toLowerCase(Locale.getDefault()));
String description = "Device: " + deviceName + " Service: " + serviceName + " Characteristic: " + charName;
// we got response regarding our request to write new value to the characteristic
// let see if it failed or not
if(status == BluetoothGatt.GATT_SUCCESS) {
mUiCallback.uiSuccessfulWrite(mBluetoothGatt, mBluetoothDevice, mBluetoothSelectedService, characteristic, description);
}
else {
mUiCallback.uiFailedWrite(mBluetoothGatt, mBluetoothDevice, mBluetoothSelectedService, characteristic, description + " STATUS = " + status);
}
};
#Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
if(status == BluetoothGatt.GATT_SUCCESS) {
// we got new value of RSSI of the connection, pass it to the UI
mUiCallback.uiNewRssiAvailable(mBluetoothGatt, mBluetoothDevice, rssi);
}
};
};
private Activity mParent = null;
private boolean mConnected = false;
private String mDeviceAddress = "";
private BluetoothManager mBluetoothManager = null;
private BluetoothAdapter mBluetoothAdapter = null;
private BluetoothDevice mBluetoothDevice = null;
private BluetoothGatt mBluetoothGatt = null;
private BluetoothGattService mBluetoothSelectedService = null;
private List<BluetoothGattService> mBluetoothGattServices = null;
private Handler mTimerHandler = new Handler();
private boolean mTimerEnabled = false;
}
replace your startScanning method of BleWrapper class to below code and it should work.
public void startScanning() {
if(mBluetoothAdapter == null)
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mBluetoothAdapter.startLeScan(mDeviceFoundCallback);
}
mBluetoothAdapter is null and therefore the startLeScan method call fails
Hope this helps :)
Related
My goal is to record audio from a Bluetooth Microphone while in Background Mode.
Expected result:
The A2DPService records audio while in the background mode.
Actual result:
The A2DPService can not record audio after in the background mode after 2 minutes. The audio file will be empty.
How to reproduce the issue:
use the #ReactMethod connectDevice(address: string).
went to background mode, wait until 2 minutes.
use the #ReactMethod startBluetoothSco() to record the audio
use the #ReactMethod stopBluetoothSco() to stop the recorder.
play the audio file. the audio file will not run as there is no input from the Bluetooth Microphone. Getting the app to Foreground Mode fixed the issue, but the app is intended to be used most of the time while in Background Mode.
A2DPService.java
package com.satpam.RNBluetoothNM.A2DP;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.media.AudioManager;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class A2DPService {
private BluetoothDevice mBluetoothDevice;
private BluetoothA2dp mBluetoothA2dp;
private AudioManager mAudioManager;
public A2DPService(ReactApplicationContext reactContext) {
mAudioManager = (AudioManager) reactContext.getSystemService(Context.AUDIO_SERVICE);
BluetoothAdapter
.getDefaultAdapter()
.getProfileProxy(reactContext, new BluetoothProfile.ServiceListener() {
#Override
public void onServiceConnected(int i, BluetoothProfile bluetoothProfile) {
if (i == BluetoothProfile.A2DP) {
mBluetoothA2dp = (BluetoothA2dp) bluetoothProfile;
}
}
#Override
public void onServiceDisconnected(int i) {
if (i == BluetoothProfile.A2DP) {
mBluetoothA2dp = null;
}
}
}, BluetoothProfile.A2DP);
}
public boolean createBond(BluetoothDevice bluetoothDevice) {
mBluetoothDevice = bluetoothDevice;
return mBluetoothDevice.createBond();
}
public boolean connectA2DP(BluetoothDevice bluetoothDevice) {
if (mBluetoothDevice == null) {
return false;
} else {
try {
Method method = BluetoothA2dp.class.getMethod("connect", BluetoothDevice.class);
method.invoke(mBluetoothA2dp, bluetoothDevice);
return true;
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
return false;
}
}
}
public int getConnectedState() {
return mBluetoothA2dp.getConnectionState(mBluetoothDevice);
}
public boolean startBluetoothSco() {
if (getConnectedState() != BluetoothProfile.STATE_CONNECTED) {
return false;
} else {
mAudioManager.setBluetoothScoOn(true);
mAudioManager.startBluetoothSco();
return true;
}
}
public boolean stopBluetoothSco() {
if (getConnectedState() != BluetoothProfile.STATE_CONNECTED) {
return false;
} else {
mAudioManager.setBluetoothScoOn(false);
mAudioManager.stopBluetoothSco();
return true;
}
}
}
RNBluetoothNM.java
package com.satpam.RNBluetoothNM;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Intent;
import android.util.Log;
import androidx.annotation.NonNull;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.BaseActivityEventListener;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.satpam.RNBluetoothNM.A2DP.A2DPService;
import com.satpam.RNBluetoothNM.SPP.SPPService;
import java.util.Set;
public class RNBluetoothNMPackage extends ReactContextBaseJavaModule {
#NonNull
#Override
public String getName() {
return "RNBluetoothNM";
}
private static final int getBondedDeviceRequestCode = 0;
private Promise mPromise;
private final ActivityEventListener activityListener = new BaseActivityEventListener() {
#Override
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
super.onActivityResult(activity, requestCode, resultCode, data);
if (requestCode == getBondedDeviceRequestCode && resultCode == activity.RESULT_OK) {
getBondedDevices(mPromise);
mPromise = null;
}
}
};
public RNBluetoothNMPackage(ReactApplicationContext reactContext) {
super(reactContext);
// Add a listener for `onActivityResult`
reactContext.addActivityEventListener(activityListener);
}
private void _requestEnableBluetooth(Promise promise) {
Intent enableAdapter = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
Activity activity = getCurrentActivity();
// Store the promise to resolve/reject when onActivityResult returns value;
mPromise = promise;
activity.startActivityForResult(enableAdapter, getBondedDeviceRequestCode);
}
#ReactMethod
public void getBondedDevices(final Promise promise) {
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null) {
promise.reject("getBondedDevices", "BluetoothAdapter is not supported");
} else {
if (!bluetoothAdapter.isEnabled()) {
_requestEnableBluetooth(promise);
}
Set<BluetoothDevice> devices = bluetoothAdapter.getBondedDevices();
if (devices.size() > 0) {
WritableArray array = Arguments.createArray();
for (BluetoothDevice device : devices) {
WritableMap writableMap = Arguments.createMap();
writableMap.putString("name", device.getName());
writableMap.putString("address", device.getAddress());
writableMap.putInt("bondState", device.getBondState());
array.pushMap(writableMap);
}
promise.resolve(array);
}
}
}
private A2DPService a2DPService;
private SPPService sppService;
#ReactMethod
public void connectDevice(String address, final Promise promise) {
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null) {
promise.reject("connectDevice", "BluetoothAdapter is not supported");
} else {
if (!bluetoothAdapter.isEnabled()) {
_requestEnableBluetooth(promise);
}
BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(address);
a2DPService = new A2DPService(getReactApplicationContext());
boolean a2dpIsBonded = a2DPService.createBond(bluetoothDevice);
if (a2dpIsBonded == false) {
promise.reject("A2DPService", "A2DP failed to bond");
} else {
boolean a2dpIsConnected = a2DPService.connectA2DP(bluetoothDevice);
if (a2dpIsConnected == false) {
promise.reject("A2DP", "A2DP failed to connect");
} else {
sppService = new SPPService(getReactApplicationContext());
boolean sppIsConnected = sppService.connectSPP(bluetoothDevice);
if (sppIsConnected == false) {
promise.reject("SPPService", "A2DP failed to bond");
} else {
promise.resolve(true);
Thread listener = new Thread(sppService);
listener.start();
}
}
}
}
}
#ReactMethod
public void startBluetoothSco(final Promise promise) {
if (a2DPService == null) {
promise.reject("startBluetoothSco", "A2DPService is null");
} else {
boolean isBluetoothScoStarted = a2DPService.startBluetoothSco();
if (isBluetoothScoStarted == false) {
promise.reject("startBluetoothSco", "A2DPService is not connected");
} else {
promise.resolve(true);
}
}
}
#ReactMethod
public void stopBluetoothSco(final Promise promise) {
if (a2DPService == null) {
promise.reject("stopBluetoothSco", "A2DPService is null");
} else {
boolean isBluetoothScoStopped = a2DPService.stopBluetoothSco();
if (isBluetoothScoStopped == false) {
promise.reject("stopBluetoothSco", "A2DPService is not connected");
} else {
promise.resolve(true);
}
}
}
}
You're using a background service. There's a limited amount of time those are allowed to run. Use a foreground service, which has no time limitations. This doesn't require your app to be in front, it just causes a notification to appear saying you're running.
The only things you need to change is to start the service with startForegroundService() and to call startForegound in the service itself.
I read almost every single one thread about BLE on the stackoverflow, nordicsemi devzone, online tutorials etc but still can't solve problem of connecting 10> BLE devices to android in parallel communication. I know that theoretically android limit is 7 so when I write 10 I mean 10 in minimum amount of time.
Setup is standard. Service, GATT for every device, broadcast listeners, leScanCallbacks etc.
Problem occurs when few devices are communicating with android and it occurs on different communication steps.
Cases are like this:
One device is connected and communicating, second also, third one is trying to connect but it keeps failing. After successful connection of teh third one, two new devices are trying to connect but keep failing.
Problem also occurs when few devices are communicating but are on different steps. One is on discovery, second on reading battery level and the third one can't connect until those two finish and disconnect/close.
Sometimes response doesn't come and after programatically set timeout requests retry nothing happens etc
I could write xy failed cases but there wouldn't be pattern which could point out obvious error and that is the main problem. Only thing certain is that some kind of collision exists and "block" response from device to my broadcast receiver but that collision is very odd since android device supports 7 connections at the time and sometimes i don't get response even when <7 devices are communicating simultaneously.
When I set communication to go one after another(when previous disconnects start next) everything goes smoothly but that takes time and time is the main reason I am trying to sync max number of devices asynchronously.
My question is does multiple communicating through bluetooth could work and if it can how to achieve it on android? I saw example of iOS handling it through multiple threads but I don't have the idea how to achieve in android with broadcast receiver so I put communication steps in different threads but it changed nothing.
This is an example of how I use BLE threads:
From the main java I start it:
uartBluetoothLaser = new UartBluetooth(this, mHandler);
uartBluetoothRF = new UartBluetooth(this, mHandler);
btAdapter = bluetoothManager.getAdapter();
btAdapter.startLeScan(scanCallback);
uartBluetoothLaser.connect(mainActivity,btAdapter,bluetoothDevice);
And this is the thread code:
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.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
import android.util.Log;
import java.util.ArrayList;
import java.util.UUID;
import com.rsradar.app.MainActivity;
import com.rsradar.app.model.Constants;
public class UartBluetooth {
private final static String TAG = UartBluetooth.class.getSimpleName();
private BluetoothManager mBluetoothManager;
private BluetoothAdapter mBluetoothAdapter;
private String mBluetoothDeviceAddress;
private BluetoothGatt mBluetoothGatt;
private int mConnectionState = STATE_DISCONNECTED;
private BluetoothDevice device;
private MainActivity mainActivity;
private static final int STATE_DISCONNECTED = 0;
private static final int STATE_CONNECTING = 1;
private static final int STATE_CONNECTED = 2;
public final static String ACTION_GATT_CONNECTED =
"com.nordicsemi.nrfUART.ACTION_GATT_CONNECTED";
public final static String ACTION_DATA_AVAILABLE =
"com.nordicsemi.nrfUART.ACTION_DATA_AVAILABLE";
public final static String EXTRA_DATA =
"com.nordicsemi.nrfUART.EXTRA_DATA";
public final static String DEVICE_DOES_NOT_SUPPORT_UART =
"com.nordicsemi.nrfUART.DEVICE_DOES_NOT_SUPPORT_UART";
public static final UUID TX_POWER_UUID = UUID.fromString("00001804-0000-1000-8000-00805f9b34fb");
public static final UUID TX_POWER_LEVEL_UUID = UUID.fromString("00002a07-0000-1000-8000-00805f9b34fb");
public static final UUID CCCD = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
public static final UUID FIRMWARE_REVISON_UUID = UUID.fromString("00002a26-0000-1000-8000-00805f9b34fb");
public static final UUID DIS_UUID = UUID.fromString("0000180a-0000-1000-8000-00805f9b34fb");
public static final UUID RX_SERVICE_UUID = UUID.fromString("6e400001-b5a3-f393-e0a9-e50e24dcca9e");
public static final UUID RX_CHAR_UUID = UUID.fromString("6e400002-b5a3-f393-e0a9-e50e24dcca9e");
public static final UUID TX_CHAR_UUID = UUID.fromString("6e400003-b5a3-f393-e0a9-e50e24dcca9e");
// Debugging
private static final boolean D = true;
// Name for the SDP record when creating server socket
private static final String NAME = "BluetoothMulti";
// Member fields
private final BluetoothAdapter mAdapter;
private final Handler mHandler;
private int mState;
private ArrayList<String> mDeviceAddresses;
private ArrayList<BluetoothSocket> mSockets;
/**
* A bluetooth piconet can support up to 7 connections. This array holds 7 unique UUIDs.
* When attempting to make a connection, the UUID on the client must match one that the server
* is listening for. When accepting incoming connections server listens for all 7 UUIDs.
* When trying to form an outgoing connection, the client tries each UUID one at a time.
*/
private ArrayList<UUID> mUuids;
// Constants that indicate the current connection state
public static final int STATE_NONE = 0; // we're doing nothing
public static final int STATE_LISTEN = 1; // now listening for incoming connections
//public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection
//public static final int STATE_CONNECTED = 3; // now connected to a remote device
public UartBluetooth(Context context, Handler handler) {
mAdapter = BluetoothAdapter.getDefaultAdapter();
mState = STATE_NONE;
mHandler = handler;
mDeviceAddresses = new ArrayList<String>();
mSockets = new ArrayList<BluetoothSocket>();
mUuids = new ArrayList<UUID>();
// 7 randomly-generated UUIDs. These must match on both server and client.
mUuids.add(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
mUuids.add(UUID.fromString("2d64189d-5a2c-4511-a074-77f199fd0834"));
}
/**
* Set the current state of the chat connection
* #param state An integer defining the current connection state
*/
private synchronized void setState(int state) {
if (D) Log.d(TAG, "setState() " + mState + " -> " + state);
mState = state;
// Give the new state to the Handler so the UI Activity can update
mHandler.obtainMessage(Constants.MESSAGE_STATE_CHANGE, state, -1).sendToTarget();
}
/**
* Return the current connection state. */
public synchronized int getState() {
return mState;
}
/**
* Start the chat service. Specifically start AcceptThread to begin a
* session in listening (server) mode. Called by the Activity onResume() */
public synchronized void start() {
if (D) Log.d(TAG, "start");
// Cancel any thread currently running a connection
//if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}
setState(STATE_LISTEN);
}
/**
* Start the ConnectThread to initiate a connection to a remote device.
* #param device The BluetoothDevice to connect
*/
public synchronized void connect(MainActivity mainActivity, BluetoothAdapter bluetoothAdapter, BluetoothDevice device) {
this.mBluetoothAdapter = bluetoothAdapter;
this.mainActivity = mainActivity;
this.device = device;
Log.w(TAG, "test on try connect");
if (mBluetoothAdapter == null || device.getAddress() == null) {
Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
return;
}
Log.w(TAG, "run is running");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
mBluetoothGatt = device.connectGatt(mainActivity, true, mGattCallback);
}
mConnectionState = STATE_CONNECTING;
}
/**
* Stop all threads
*/
public synchronized void stop() {
if (D) Log.d(TAG, "stop");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
if(mBluetoothGatt != null){
mBluetoothGatt.disconnect();
}
}
try {
this.finalize();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
setState(STATE_NONE);
}
/**
* This thread runs while listening for incoming connections. It behaves
* like a server-side client. It runs until a connection is accepted
* (or until cancelled).
*/
private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
String intentAction;
if (newState == BluetoothProfile.STATE_CONNECTED) {
intentAction = ACTION_GATT_CONNECTED;
mConnectionState = STATE_CONNECTED;
// TODO: handler
Log.i(TAG, "Connected to GATT server.");
// Attempts to discover services after successful connection.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
Log.i(TAG, "Attempting to start service discovery:" +
mBluetoothGatt.discoverServices());
}
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
intentAction = Constants.ACTION_GATT_LASER_DISCONNECTED;
mConnectionState = STATE_DISCONNECTED;
Log.i(TAG, "Disconnected from GATT server.");
// TODO: handler
mainActivity.btListener.listenerSet(false,device.getName());
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.w(TAG, "mBluetoothGatt = " + mBluetoothGatt );
// TODO: handler
mainActivity.btListener.listenerSet(true,device.getName());
enableTXNotification();
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
// TODO: handler
System.out.println(" read ok");
}
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
//System.out.println("RUN");
// TODO: handler
byte[] buffer = characteristic.getValue();
int deviceType = 0;
if (device.getName().equals(Constants.DEVICE_LASER)){
deviceType = 1;
} else if (device.getName().equals(Constants.DEVICE_RF)){
deviceType = 2;
}
mHandler.obtainMessage(Constants.MESSAGE_READ, deviceType, -1, buffer).sendToTarget();
//System.out.println(" change ok");
}
};
public void enableTXNotification()
{
/*
if (mBluetoothGatt == null) {
showMessage("mBluetoothGatt null" + mBluetoothGatt);
broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_UART);
return;
}
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
BluetoothGattService RxService = mBluetoothGatt.getService(RX_SERVICE_UUID);
if (RxService == null) {
return;
}
BluetoothGattCharacteristic TxChar = RxService.getCharacteristic(TX_CHAR_UUID);
if (TxChar == null) {
return;
}
mBluetoothGatt.setCharacteristicNotification(TxChar,true);
BluetoothGattDescriptor descriptor = TxChar.getDescriptor(CCCD);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
}
}
public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
mBluetoothGatt.readCharacteristic(characteristic);
}
}
}
I am following the this ExampleMediaController tutorial.
This is VideoControlView.java,
package com.otwar.deepak.video_styling;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ProgressBar;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import java.lang.ref.WeakReference;
import java.util.Formatter;
import java.util.Locale;
public class VideoControllerView extends FrameLayout {
private static final String TAG = "VideoControllerView";
private MediaPlayerControl mPlayer;
private Context mContext;
private ViewGroup mAnchor;
private View mRoot;
private ProgressBar mProgress;
private TextView mEndTime, mCurrentTime;
private boolean mShowing;
private boolean mDragging;
private static final int sDefaultTimeout = 3000;
private static final int FADE_OUT = 1;
private static final int SHOW_PROGRESS = 2;
private boolean mUseFastForward;
private boolean mFromXml;
private boolean mListenersSet;
private OnClickListener mNextListener, mPrevListener;
StringBuilder mFormatBuilder;
Formatter mFormatter;
private ImageButton mPauseButton;
private ImageButton mFfwdButton;
private ImageButton mRewButton;
private ImageButton mNextButton;
private ImageButton mPrevButton;
private ImageButton mFullscreenButton;
private Handler mHandler = new MessageHandler(this);
public VideoControllerView(Context context, AttributeSet attrs) {
super(context, attrs);
mRoot = null;
mContext = context;
mUseFastForward = true;
mFromXml = true;
Log.i(TAG, TAG);
}
public VideoControllerView(Context context, boolean useFastForward) {
super(context);
mContext = context;
mUseFastForward = useFastForward;
Log.i(TAG, TAG);
}
public VideoControllerView(Context context) {
this(context, true);
Log.i(TAG, TAG);
}
#Override
public void onFinishInflate() {
if (mRoot != null)
initControllerView(mRoot);
}
public void setMediaPlayer(MediaPlayerControl player) {
mPlayer = player;
updatePausePlay();
updateFullScreen();
}
/**
* Set the view that acts as the anchor for the control view.
* This can for example be a VideoView, or your Activity's main view.
* #param view The view to which to anchor the controller when it is visible.
*/
public void setAnchorView(ViewGroup view) {
mAnchor = view;
LayoutParams frameParams = new LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
);
removeAllViews();
View v = makeControllerView();
addView(v, frameParams);
}
/**
* Create the view that holds the widgets that control playback.
* Derived classes can override this to create their own.
* #return The controller view.
* #hide This doesn't work as advertised
*/
protected View makeControllerView() {
LayoutInflater inflate = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mRoot = inflate.inflate(R.layout.media_controller, null);
initControllerView(mRoot);
return mRoot;
}
private void initControllerView(View v) {
mPauseButton = (ImageButton) v.findViewById(R.id.pause);
if (mPauseButton != null) {
mPauseButton.requestFocus();
mPauseButton.setOnClickListener(mPauseListener);
}
mFullscreenButton = (ImageButton) v.findViewById(R.id.fullscreen);
if (mFullscreenButton != null) {
mFullscreenButton.requestFocus();
mFullscreenButton.setOnClickListener(mFullscreenListener);
}
mFfwdButton = (ImageButton) v.findViewById(R.id.ffwd);
if (mFfwdButton != null) {
mFfwdButton.setOnClickListener(mFfwdListener);
if (!mFromXml) {
mFfwdButton.setVisibility(mUseFastForward ? View.VISIBLE : View.GONE);
}
}
mRewButton = (ImageButton) v.findViewById(R.id.rew);
if (mRewButton != null) {
mRewButton.setOnClickListener(mRewListener);
if (!mFromXml) {
mRewButton.setVisibility(mUseFastForward ? View.VISIBLE : View.GONE);
}
}
// By default these are hidden. They will be enabled when setPrevNextListeners() is called
mNextButton = (ImageButton) v.findViewById(R.id.next);
if (mNextButton != null && !mFromXml && !mListenersSet) {
mNextButton.setVisibility(View.GONE);
}
mPrevButton = (ImageButton) v.findViewById(R.id.prev);
if (mPrevButton != null && !mFromXml && !mListenersSet) {
mPrevButton.setVisibility(View.GONE);
}
mProgress = (ProgressBar) v.findViewById(R.id.mediacontroller_progress);
if (mProgress != null) {
if (mProgress instanceof SeekBar) {
SeekBar seeker = (SeekBar) mProgress;
seeker.setOnSeekBarChangeListener(mSeekListener);
}
mProgress.setMax(1000);
}
mEndTime = (TextView) v.findViewById(R.id.time);
mCurrentTime = (TextView) v.findViewById(R.id.time_current);
mFormatBuilder = new StringBuilder();
mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());
installPrevNextListeners();
}
/**
* Show the controller on screen. It will go away
* automatically after 3 seconds of inactivity.
*/
public void show() {
show(sDefaultTimeout);
}
/**
* Disable pause or seek buttons if the stream cannot be paused or seeked.
* This requires the control interface to be a MediaPlayerControlExt
*/
private void disableUnsupportedButtons() {
if (mPlayer == null) {
return;
}
try {
if (mPauseButton != null && !mPlayer.canPause()) {
mPauseButton.setEnabled(false);
}
if (mRewButton != null && !mPlayer.canSeekBackward()) {
mRewButton.setEnabled(false);
}
if (mFfwdButton != null && !mPlayer.canSeekForward()) {
mFfwdButton.setEnabled(false);
}
} catch (IncompatibleClassChangeError ex) {
// We were given an old version of the interface, that doesn't have
// the canPause/canSeekXYZ methods. This is OK, it just means we
// assume the media can be paused and seeked, and so we don't disable
// the buttons.
}
}
/**
* Show the controller on screen. It will go away
* automatically after 'timeout' milliseconds of inactivity.
* #param timeout The timeout in milliseconds. Use 0 to show
* the controller until hide() is called.
*/
public void show(int timeout) {
if (!mShowing && mAnchor != null) {
setProgress();
if (mPauseButton != null) {
mPauseButton.requestFocus();
}
disableUnsupportedButtons();
LayoutParams tlp = new LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
Gravity.BOTTOM
);
mAnchor.addView(this, tlp);
mShowing = true;
}
updatePausePlay();
updateFullScreen();
// cause the progress bar to be updated even if mShowing
// was already true. This happens, for example, if we're
// paused with the progress bar showing the user hits play.
mHandler.sendEmptyMessage(SHOW_PROGRESS);
Message msg = mHandler.obtainMessage(FADE_OUT);
if (timeout != 0) {
mHandler.removeMessages(FADE_OUT);
mHandler.sendMessageDelayed(msg, timeout);
}
}
public boolean isShowing() {
return mShowing;
}
/**
* Remove the controller from the screen.
*/
public void hide() {
if (mAnchor == null) {
return;
}
try {
mAnchor.removeView(this);
mHandler.removeMessages(SHOW_PROGRESS);
} catch (IllegalArgumentException ex) {
Log.w("MediaController", "already removed");
}
mShowing = false;
}
private String stringForTime(int timeMs) {
int totalSeconds = timeMs / 1000;
int seconds = totalSeconds % 60;
int minutes = (totalSeconds / 60) % 60;
int hours = totalSeconds / 3600;
mFormatBuilder.setLength(0);
if (hours > 0) {
return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString();
} else {
return mFormatter.format("%02d:%02d", minutes, seconds).toString();
}
}
private int setProgress() {
if (mPlayer == null || mDragging) {
return 0;
}
int position = mPlayer.getCurrentPosition();
int duration = mPlayer.getDuration();
if (mProgress != null) {
if (duration > 0) {
// use long to avoid overflow
long pos = 1000L * position / duration;
mProgress.setProgress( (int) pos);
}
int percent = mPlayer.getBufferPercentage();
mProgress.setSecondaryProgress(percent * 10);
}
if (mEndTime != null)
mEndTime.setText(stringForTime(duration));
if (mCurrentTime != null)
mCurrentTime.setText(stringForTime(position));
return position;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
show(sDefaultTimeout);
return true;
}
#Override
public boolean onTrackballEvent(MotionEvent ev) {
show(sDefaultTimeout);
return false;
}
#Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (mPlayer == null) {
return true;
}
int keyCode = event.getKeyCode();
final boolean uniqueDown = event.getRepeatCount() == 0
&& event.getAction() == KeyEvent.ACTION_DOWN;
if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK
|| keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
|| keyCode == KeyEvent.KEYCODE_SPACE) {
if (uniqueDown) {
doPauseResume();
show(sDefaultTimeout);
if (mPauseButton != null) {
mPauseButton.requestFocus();
}
}
return true;
} else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
if (uniqueDown && !mPlayer.isPlaying()) {
mPlayer.start();
updatePausePlay();
show(sDefaultTimeout);
}
return true;
} else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
|| keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
if (uniqueDown && mPlayer.isPlaying()) {
mPlayer.pause();
updatePausePlay();
show(sDefaultTimeout);
}
return true;
} else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
|| keyCode == KeyEvent.KEYCODE_VOLUME_UP
|| keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) {
// don't show the controls for volume adjustment
return super.dispatchKeyEvent(event);
} else if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU) {
if (uniqueDown) {
hide();
}
return true;
}
show(sDefaultTimeout);
return super.dispatchKeyEvent(event);
}
private OnClickListener mPauseListener = new OnClickListener() {
public void onClick(View v) {
doPauseResume();
show(sDefaultTimeout);
}
};
private OnClickListener mFullscreenListener = new OnClickListener() {
public void onClick(View v) {
doToggleFullscreen();
show(sDefaultTimeout);
}
};
public void updatePausePlay() {
if (mRoot == null || mPauseButton == null || mPlayer == null) {
return;
}
if (mPlayer.isPlaying()) {
mPauseButton.setImageResource(R.drawable.ic_media_pause);
} else {
mPauseButton.setImageResource(R.drawable.ic_media_play);
}
}
public void updateFullScreen() {
if (mRoot == null || mFullscreenButton == null || mPlayer == null) {
return;
}
if (mPlayer.isFullScreen()) {
mFullscreenButton.setImageResource(R.drawable.ic_media_fullscreen_shrink);
}
else {
mFullscreenButton.setImageResource(R.drawable.ic_media_fullscreen_stretch);
}
}
private void doPauseResume() {
if (mPlayer == null) {
return;
}
if (mPlayer.isPlaying()) {
mPlayer.pause();
} else {
mPlayer.start();
}
updatePausePlay();
}
private void doToggleFullscreen() {
if (mPlayer == null) {
return;
}
mPlayer.toggleFullScreen();
}
// There are two scenarios that can trigger the seekbar listener to trigger:
//
// The first is the user using the touchpad to adjust the posititon of the
// seekbar's thumb. In this case onStartTrackingTouch is called followed by
// a number of onProgressChanged notifications, concluded by onStopTrackingTouch.
// We're setting the field "mDragging" to true for the duration of the dragging
// session to avoid jumps in the position in case of ongoing playback.
//
// The second scenario involves the user operating the scroll ball, in this
// case there WON'T BE onStartTrackingTouch/onStopTrackingTouch notifications,
// we will simply apply the updated position without suspending regular updates.
private OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
public void onStartTrackingTouch(SeekBar bar) {
show(3600000);
mDragging = true;
// By removing these pending progress messages we make sure
// that a) we won't update the progress while the user adjusts
// the seekbar and b) once the user is done dragging the thumb
// we will post one of these messages to the queue again and
// this ensures that there will be exactly one message queued up.
mHandler.removeMessages(SHOW_PROGRESS);
}
public void onProgressChanged(SeekBar bar, int progress, boolean fromuser) {
if (mPlayer == null) {
return;
}
if (!fromuser) {
// We're not interested in programmatically generated changes to
// the progress bar's position.
return;
}
long duration = mPlayer.getDuration();
long newposition = (duration * progress) / 1000L;
mPlayer.seekTo( (int) newposition);
if (mCurrentTime != null)
mCurrentTime.setText(stringForTime( (int) newposition));
}
public void onStopTrackingTouch(SeekBar bar) {
mDragging = false;
setProgress();
updatePausePlay();
show(sDefaultTimeout);
// Ensure that progress is properly updated in the future,
// the call to show() does not guarantee this because it is a
// no-op if we are already showing.
mHandler.sendEmptyMessage(SHOW_PROGRESS);
}
};
#Override
public void setEnabled(boolean enabled) {
if (mPauseButton != null) {
mPauseButton.setEnabled(enabled);
}
if (mFfwdButton != null) {
mFfwdButton.setEnabled(enabled);
}
if (mRewButton != null) {
mRewButton.setEnabled(enabled);
}
if (mNextButton != null) {
mNextButton.setEnabled(enabled && mNextListener != null);
}
if (mPrevButton != null) {
mPrevButton.setEnabled(enabled && mPrevListener != null);
}
if (mProgress != null) {
mProgress.setEnabled(enabled);
}
disableUnsupportedButtons();
super.setEnabled(enabled);
}
private OnClickListener mRewListener = new OnClickListener() {
public void onClick(View v) {
if (mPlayer == null) {
return;
}
int pos = mPlayer.getCurrentPosition();
pos -= 5000; // milliseconds
mPlayer.seekTo(pos);
setProgress();
show(sDefaultTimeout);
}
};
private OnClickListener mFfwdListener = new OnClickListener() {
public void onClick(View v) {
if (mPlayer == null) {
return;
}
int pos = mPlayer.getCurrentPosition();
pos += 15000; // milliseconds
mPlayer.seekTo(pos);
setProgress();
show(sDefaultTimeout);
}
};
private void installPrevNextListeners() {
if (mNextButton != null) {
mNextButton.setOnClickListener(mNextListener);
mNextButton.setEnabled(mNextListener != null);
}
if (mPrevButton != null) {
mPrevButton.setOnClickListener(mPrevListener);
mPrevButton.setEnabled(mPrevListener != null);
}
}
public void setPrevNextListeners(OnClickListener next, OnClickListener prev) {
mNextListener = next;
mPrevListener = prev;
mListenersSet = true;
if (mRoot != null) {
installPrevNextListeners();
if (mNextButton != null && !mFromXml) {
mNextButton.setVisibility(View.VISIBLE);
}
if (mPrevButton != null && !mFromXml) {
mPrevButton.setVisibility(View.VISIBLE);
}
}
}
public interface MediaPlayerControl {
void start();
void pause();
int getDuration();
int getCurrentPosition();
void seekTo(int pos);
boolean isPlaying();
int getBufferPercentage();
boolean canPause();
boolean canSeekBackward();
boolean canSeekForward();
boolean isFullScreen();
void toggleFullScreen();
}
private static class MessageHandler extends Handler {
private final WeakReference<VideoControllerView> mView;
MessageHandler(VideoControllerView view) {
mView = new WeakReference<VideoControllerView>(view);
}
#Override
public void handleMessage(Message msg) {
VideoControllerView view = mView.get();
if (view == null || view.mPlayer == null) {
return;
}
int pos;
switch (msg.what) {
case FADE_OUT:
view.hide();
break;
case SHOW_PROGRESS:
pos = view.setProgress();
if (!view.mDragging && view.mShowing && view.mPlayer.isPlaying()) {
msg = obtainMessage(SHOW_PROGRESS);
sendMessageDelayed(msg, 1000 - (pos % 1000));
}
break;
}
}
}
}
My videoPlayerActivity.java
package com.otwar.deepak.video_styling;
import android.app.Activity;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.FrameLayout;
import java.io.IOException;
public class VideoPlayerActivity extends Activity implements SurfaceHolder.Callback, MediaPlayer.OnPreparedListener, VideoControllerView.MediaPlayerControl {
SurfaceView videoSurface;
MediaPlayer player;
VideoControllerView controller;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video_player);
videoSurface = (SurfaceView) findViewById(R.id.videoSurface);
SurfaceHolder videoHolder = videoSurface.getHolder();
videoHolder.addCallback(this);
player = new MediaPlayer();
controller = new VideoControllerView(this);
try {
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
player.setDataSource(this, Uri.parse("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"));
player.setOnPreparedListener(this);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
controller.show();
return false;
}
// Implement SurfaceHolder.Callback
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
player.setDisplay(holder);
player.prepareAsync();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
// End SurfaceHolder.Callback
// Implement MediaPlayer.OnPreparedListener
#Override
public void onPrepared(MediaPlayer mp) {
controller.setMediaPlayer(this);
controller.setAnchorView((FrameLayout) findViewById(R.id.videoSurfaceContainer));
player.start();
}
// End MediaPlayer.OnPreparedListener
// Implement VideoMediaController.MediaPlayerControl
#Override
public boolean canPause() {
return true;
}
#Override
public boolean canSeekBackward() {
return true;
}
#Override
public boolean canSeekForward() {
return true;
}
#Override
public int getBufferPercentage() {
return 0;
}
#Override
public int getCurrentPosition() {
return player.getCurrentPosition();
}
#Override
public int getDuration() {
return player.getDuration();
}
#Override
public boolean isPlaying() {
return player.isPlaying();
}
#Override
public void pause() {
player.pause();
}
#Override
public void seekTo(int i) {
player.seekTo(i);
}
#Override
public void start() {
player.start();
}
#Override
public boolean isFullScreen() {
return false;
}
#Override
public void toggleFullScreen() {
}
// End VideoMediaController.MediaPlayerControl
}
How can I put the result of the QR "SCAN_RESULT" in different activity, this is my problem.
In the splash screen I need to go to the QRScanner (CaptureActivity), and then, to the ActivityQR who will send the info. to the SQLite.
The problem is: When I get de QRCode in string ("SCAN_RESULT") on "CaptureActivity.java", the app close because the app need "back" to the previous activity with this:
getIntent();
I try to modify this codeline, but this not solve my problem.
How can I put the "SCAN_RESULT" in the ActivityQR? I don't know how open other activity (this case ActivityQR) when the code had gottenm and put this String value on the ActivityQR
I Use "CaptureActivity" of ZXing (ZebraCrossing)
Thanks in advance.
SplashScreen.java :
{
Intent mainIntent = new Intent().setClass(SplashScreenActivity.this, com.google.zxing.client.android.CaptureActivity.class);
startActivity(mainIntent)
}
ActivityQR.java
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data){
if(REQUEST_CODE == requestCode && RESULT_OK == resultCode){
txResult.setText(data.getStringExtra("SCAN_RESULT"));
}
}
CaptureActivity.java
public class CaptureActivity extends Activity implements SurfaceHolder.Callback {
private static final String TAG = CaptureActivity.class.getSimpleName();
private static final long DEFAULT_INTENT_RESULT_DURATION_MS = 1500L;
private static final long BULK_MODE_SCAN_DELAY_MS = 1000L;
private static final String[] ZXING_URLS = { "http://zxing.appspot.com/scan", "zxing://scan/" };
public static final int HISTORY_REQUEST_CODE = 0x0000bacc;
private static final Collection<ResultMetadataType> DISPLAYABLE_METADATA_TYPES =
EnumSet.of(ResultMetadataType.ISSUE_NUMBER,
ResultMetadataType.SUGGESTED_PRICE,
ResultMetadataType.ERROR_CORRECTION_LEVEL,
ResultMetadataType.POSSIBLE_COUNTRY);
private CameraManager cameraManager;
private CaptureActivityHandler handler;
private Result savedResultToShow;
private ViewfinderView viewfinderView;
private TextView statusView;
private View resultView;
private Result lastResult;
private boolean hasSurface;
private boolean copyToClipboard;
private IntentSource source;
private String sourceUrl;
private ScanFromWebPageManager scanFromWebPageManager;
private Collection<BarcodeFormat> decodeFormats;
private Map<DecodeHintType,?> decodeHints;
private String characterSet;
private HistoryManager historyManager;
private InactivityTimer inactivityTimer;
private BeepManager beepManager;
private AmbientLightManager ambientLightManager;
ViewfinderView getViewfinderView() {
return viewfinderView;
}
public Handler getHandler() {
return handler;
}
CameraManager getCameraManager() {
return cameraManager;
}
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.capture);
hasSurface = false;
historyManager = new HistoryManager(this);
historyManager.trimHistory();
inactivityTimer = new InactivityTimer(this);
beepManager = new BeepManager(this);
ambientLightManager = new AmbientLightManager(this);
PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
}
#Override
protected void onResume() {
super.onResume();
// CameraManager must be initialized here, not in onCreate(). This is necessary because we don't
// want to open the camera driver and measure the screen size if we're going to show the help on
// first launch. That led to bugs where the scanning rectangle was the wrong size and partially
// off screen.
cameraManager = new CameraManager(getApplication());
viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view);
viewfinderView.setCameraManager(cameraManager);
resultView = findViewById(R.id.result_view);
statusView = (TextView) findViewById(R.id.status_view);
handler = null;
lastResult = null;
resetStatusView();
SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
SurfaceHolder surfaceHolder = surfaceView.getHolder();
if (hasSurface) {
// The activity was paused but not stopped, so the surface still exists. Therefore
// surfaceCreated() won't be called, so init the camera here.
initCamera(surfaceHolder);
} else {
// Install the callback and wait for surfaceCreated() to init the camera.
surfaceHolder.addCallback(this);
}
beepManager.updatePrefs();
ambientLightManager.start(cameraManager);
inactivityTimer.onResume();
Intent intent = getIntent();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
copyToClipboard = prefs.getBoolean(PreferencesActivity.KEY_COPY_TO_CLIPBOARD, true)
&& (intent == null || intent.getBooleanExtra(Intents.Scan.SAVE_HISTORY, true));
source = IntentSource.NONE;
decodeFormats = null;
characterSet = null;
if (intent != null) {
String action = intent.getAction();
String dataString = intent.getDataString();
if (Intents.Scan.ACTION.equals(action)) {
// Scan the formats the intent requested, and return the result to the calling activity.
source = IntentSource.NATIVE_APP_INTENT;
decodeFormats = DecodeFormatManager.parseDecodeFormats(intent);
decodeHints = DecodeHintManager.parseDecodeHints(intent);
if (intent.hasExtra(Intents.Scan.WIDTH) && intent.hasExtra(Intents.Scan.HEIGHT)) {
int width = intent.getIntExtra(Intents.Scan.WIDTH, 0);
int height = intent.getIntExtra(Intents.Scan.HEIGHT, 0);
if (width > 0 && height > 0) {
cameraManager.setManualFramingRect(width, height);
}
}
String customPromptMessage = intent.getStringExtra(Intents.Scan.PROMPT_MESSAGE);
if (customPromptMessage != null) {
statusView.setText(customPromptMessage);
}
} else if (dataString != null &&
dataString.contains("http://www.google") &&
dataString.contains("/m/products/scan")) {
// Scan only products and send the result to mobile Product Search.
source = IntentSource.PRODUCT_SEARCH_LINK;
sourceUrl = dataString;
decodeFormats = DecodeFormatManager.PRODUCT_FORMATS;
} else if (isZXingURL(dataString)) {
// Scan formats requested in query string (all formats if none specified).
// If a return URL is specified, send the results there. Otherwise, handle it ourselves.
source = IntentSource.ZXING_LINK;
sourceUrl = dataString;
Uri inputUri = Uri.parse(dataString);
scanFromWebPageManager = new ScanFromWebPageManager(inputUri);
decodeFormats = DecodeFormatManager.parseDecodeFormats(inputUri);
// Allow a sub-set of the hints to be specified by the caller.
decodeHints = DecodeHintManager.parseDecodeHints(inputUri);
}
characterSet = intent.getStringExtra(Intents.Scan.CHARACTER_SET);
}
}
private static boolean isZXingURL(String dataString) {
if (dataString == null) {
return false;
}
for (String url : ZXING_URLS) {
if (dataString.startsWith(url)) {
return true;
}
}
return false;
}
#Override
protected void onPause() {
if (handler != null) {
handler.quitSynchronously();
handler = null;
}
inactivityTimer.onPause();
ambientLightManager.stop();
cameraManager.closeDriver();
if (!hasSurface) {
SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
SurfaceHolder surfaceHolder = surfaceView.getHolder();
surfaceHolder.removeCallback(this);
}
super.onPause();
}
#Override
protected void onDestroy() {
inactivityTimer.shutdown();
super.onDestroy();
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
if (source == IntentSource.NATIVE_APP_INTENT) {
setResult(RESULT_CANCELED);
finish();
return true;
}
if ((source == IntentSource.NONE || source == IntentSource.ZXING_LINK) && lastResult != null) {
restartPreviewAfterDelay(0L);
return true;
}
break;
case KeyEvent.KEYCODE_FOCUS:
case KeyEvent.KEYCODE_CAMERA:
// Handle these events so they don't launch the Camera app
return true;
// Use volume up/down to turn on light
case KeyEvent.KEYCODE_VOLUME_DOWN:
cameraManager.setTorch(false);
return true;
case KeyEvent.KEYCODE_VOLUME_UP:
cameraManager.setTorch(true);
return true;
}
return super.onKeyDown(keyCode, event);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(R.menu.capture, menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
switch (item.getItemId()) {
case R.id.menu_share:
intent.setClassName(this, ShareActivity.class.getName());
startActivity(intent);
break;
case R.id.menu_history:
intent.setClassName(this, HistoryActivity.class.getName());
startActivityForResult(intent, HISTORY_REQUEST_CODE);
break;
case R.id.menu_settings:
intent.setClassName(this, PreferencesActivity.class.getName());
startActivity(intent);
break;
case R.id.menu_help:
intent.setClassName(this, HelpActivity.class.getName());
startActivity(intent);
break;
default:
return super.onOptionsItemSelected(item);
}
return true;
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (resultCode == RESULT_OK) {
if (requestCode == HISTORY_REQUEST_CODE) {
int itemNumber = intent.getIntExtra(Intents.History.ITEM_NUMBER, -1);
if (itemNumber >= 0) {
HistoryItem historyItem = historyManager.buildHistoryItem(itemNumber);
decodeOrStoreSavedBitmap(null, historyItem.getResult());
}
}
}
}
private void decodeOrStoreSavedBitmap(Bitmap bitmap, Result result) {
// Bitmap isn't used yet -- will be used soon
if (handler == null) {
savedResultToShow = result;
} else {
if (result != null) {
savedResultToShow = result;
}
if (savedResultToShow != null) {
Message message = Message.obtain(handler, R.id.decode_succeeded, savedResultToShow);
handler.sendMessage(message);
}
savedResultToShow = null;
}
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
if (holder == null) {
Log.e(TAG, "*** WARNING *** surfaceCreated() gave us a null surface!");
}
if (!hasSurface) {
hasSurface = true;
initCamera(holder);
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
hasSurface = false;
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
/**
* A valid barcode has been found, so give an indication of success and show the results.
*
* #param rawResult The contents of the barcode.
* #param scaleFactor amount by which thumbnail was scaled
* #param barcode A greyscale bitmap of the camera data which was decoded.
*/
public void handleDecode(Result rawResult, Bitmap barcode, float scaleFactor) {
Intent it = getIntent();
it.putExtra("SCAN_RESULT", rawResult.getText());
it.putExtra("SCAN_FORMAT", rawResult.getBarcodeFormat().toString());
setResult(Activity.RESULT_OK, it);
finish();
inactivityTimer.onActivity();
lastResult = rawResult;
ResultHandler resultHandler = ResultHandlerFactory.makeResultHandler(this, rawResult);
boolean fromLiveScan = barcode != null;
if (fromLiveScan) {
historyManager.addHistoryItem(rawResult, resultHandler);
// Then not from history, so beep/vibrate and we have an image to draw on
beepManager.playBeepSoundAndVibrate();
drawResultPoints(barcode, scaleFactor, rawResult);
}
switch (source) {
case NATIVE_APP_INTENT:
case PRODUCT_SEARCH_LINK:
handleDecodeExternally(rawResult, resultHandler, barcode);
break;
case ZXING_LINK:
if (scanFromWebPageManager == null || !scanFromWebPageManager.isScanFromWebPage()) {
handleDecodeInternally(rawResult, resultHandler, barcode);
} else {
handleDecodeExternally(rawResult, resultHandler, barcode);
}
break;
case NONE:
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
if (fromLiveScan && prefs.getBoolean(PreferencesActivity.KEY_BULK_MODE, false)) {
Toast.makeText(getApplicationContext(),
getResources().getString(R.string.msg_bulk_mode_scanned) + " (" + rawResult.getText() + ')',
Toast.LENGTH_SHORT).show();
// Wait a moment or else it will scan the same barcode continuously about 3 times
restartPreviewAfterDelay(BULK_MODE_SCAN_DELAY_MS);
} else {
handleDecodeInternally(rawResult, resultHandler, barcode);
}
break;
}
}
/**
* Superimpose a line for 1D or dots for 2D to highlight the key features of the barcode.
*
* #param barcode A bitmap of the captured image.
* #param scaleFactor amount by which thumbnail was scaled
* #param rawResult The decoded results which contains the points to draw.
*/
private void drawResultPoints(Bitmap barcode, float scaleFactor, Result rawResult) {
ResultPoint[] points = rawResult.getResultPoints();
if (points != null && points.length > 0) {
Canvas canvas = new Canvas(barcode);
Paint paint = new Paint();
paint.setColor(getResources().getColor(R.color.result_points));
if (points.length == 2) {
paint.setStrokeWidth(4.0f);
drawLine(canvas, paint, points[0], points[1], scaleFactor);
} else if (points.length == 4 &&
(rawResult.getBarcodeFormat() == BarcodeFormat.UPC_A ||
rawResult.getBarcodeFormat() == BarcodeFormat.EAN_13)) {
// Hacky special case -- draw two lines, for the barcode and metadata
drawLine(canvas, paint, points[0], points[1], scaleFactor);
drawLine(canvas, paint, points[2], points[3], scaleFactor);
} else {
paint.setStrokeWidth(10.0f);
for (ResultPoint point : points) {
if (point != null) {
canvas.drawPoint(scaleFactor * point.getX(), scaleFactor * point.getY(), paint);
}
}
}
}
}
private static void drawLine(Canvas canvas, Paint paint, ResultPoint a, ResultPoint b, float scaleFactor) {
if (a != null && b != null) {
canvas.drawLine(scaleFactor * a.getX(),
scaleFactor * a.getY(),
scaleFactor * b.getX(),
scaleFactor * b.getY(),
paint);
}
}
// Put up our own UI for how to handle the decoded contents.
private void handleDecodeInternally(Result rawResult, ResultHandler resultHandler, Bitmap barcode) {
statusView.setVisibility(View.GONE);
viewfinderView.setVisibility(View.GONE);
resultView.setVisibility(View.VISIBLE);
ImageView barcodeImageView = (ImageView) findViewById(R.id.barcode_image_view);
if (barcode == null) {
barcodeImageView.setImageBitmap(BitmapFactory.decodeResource(getResources(),
R.drawable.launcher_icon));
} else {
barcodeImageView.setImageBitmap(barcode);
}
TextView formatTextView = (TextView) findViewById(R.id.format_text_view);
formatTextView.setText(rawResult.getBarcodeFormat().toString());
TextView typeTextView = (TextView) findViewById(R.id.type_text_view);
typeTextView.setText(resultHandler.getType().toString());
DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
TextView timeTextView = (TextView) findViewById(R.id.time_text_view);
timeTextView.setText(formatter.format(new Date(rawResult.getTimestamp())));
TextView metaTextView = (TextView) findViewById(R.id.meta_text_view);
View metaTextViewLabel = findViewById(R.id.meta_text_view_label);
metaTextView.setVisibility(View.GONE);
metaTextViewLabel.setVisibility(View.GONE);
Map<ResultMetadataType,Object> metadata = rawResult.getResultMetadata();
if (metadata != null) {
StringBuilder metadataText = new StringBuilder(20);
for (Map.Entry<ResultMetadataType,Object> entry : metadata.entrySet()) {
if (DISPLAYABLE_METADATA_TYPES.contains(entry.getKey())) {
metadataText.append(entry.getValue()).append('\n');
}
}
if (metadataText.length() > 0) {
metadataText.setLength(metadataText.length() - 1);
metaTextView.setText(metadataText);
metaTextView.setVisibility(View.VISIBLE);
metaTextViewLabel.setVisibility(View.VISIBLE);
}
}
TextView contentsTextView = (TextView) findViewById(R.id.contents_text_view);
CharSequence displayContents = resultHandler.getDisplayContents();
contentsTextView.setText(displayContents);
// Crudely scale betweeen 22 and 32 -- bigger font for shorter text
int scaledSize = Math.max(22, 32 - displayContents.length() / 4);
contentsTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, scaledSize);
TextView supplementTextView = (TextView) findViewById(R.id.contents_supplement_text_view);
supplementTextView.setText("");
supplementTextView.setOnClickListener(null);
if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean(
PreferencesActivity.KEY_SUPPLEMENTAL, true)) {
SupplementalInfoRetriever.maybeInvokeRetrieval(supplementTextView,
resultHandler.getResult(),
historyManager,
this);
}
int buttonCount = resultHandler.getButtonCount();
ViewGroup buttonView = (ViewGroup) findViewById(R.id.result_button_view);
buttonView.requestFocus();
for (int x = 0; x < ResultHandler.MAX_BUTTON_COUNT; x++) {
TextView button = (TextView) buttonView.getChildAt(x);
if (x < buttonCount) {
button.setVisibility(View.VISIBLE);
button.setText(resultHandler.getButtonText(x));
button.setOnClickListener(new ResultButtonListener(resultHandler, x));
} else {
button.setVisibility(View.GONE);
}
}
if (copyToClipboard && !resultHandler.areContentsSecure()) {
ClipboardInterface.setText(displayContents, this);
}
}
// Briefly show the contents of the barcode, then handle the result outside Barcode Scanner.
private void handleDecodeExternally(Result rawResult, ResultHandler resultHandler, Bitmap barcode) {
if (barcode != null) {
viewfinderView.drawResultBitmap(barcode);
}
long resultDurationMS;
if (getIntent() == null) {
resultDurationMS = DEFAULT_INTENT_RESULT_DURATION_MS;
} else {
resultDurationMS = getIntent().getLongExtra(Intents.Scan.RESULT_DISPLAY_DURATION_MS,
DEFAULT_INTENT_RESULT_DURATION_MS);
}
if (resultDurationMS > 0) {
String rawResultString = String.valueOf(rawResult);
if (rawResultString.length() > 32) {
rawResultString = rawResultString.substring(0, 32) + " ...";
}
statusView.setText(getString(resultHandler.getDisplayTitle()) + " : " + rawResultString);
}
if (copyToClipboard && !resultHandler.areContentsSecure()) {
CharSequence text = resultHandler.getDisplayContents();
ClipboardInterface.setText(text, this);
}
if (source == IntentSource.NATIVE_APP_INTENT) {
// Hand back whatever action they requested - this can be changed to Intents.Scan.ACTION when
// the deprecated intent is retired.
Intent intent = new Intent(getIntent().getAction());
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
intent.putExtra(Intents.Scan.RESULT, rawResult.toString());
intent.putExtra(Intents.Scan.RESULT_FORMAT, rawResult.getBarcodeFormat().toString());
byte[] rawBytes = rawResult.getRawBytes();
if (rawBytes != null && rawBytes.length > 0) {
intent.putExtra(Intents.Scan.RESULT_BYTES, rawBytes);
}
Map<ResultMetadataType,?> metadata = rawResult.getResultMetadata();
if (metadata != null) {
if (metadata.containsKey(ResultMetadataType.UPC_EAN_EXTENSION)) {
intent.putExtra(Intents.Scan.RESULT_UPC_EAN_EXTENSION,
metadata.get(ResultMetadataType.UPC_EAN_EXTENSION).toString());
}
Number orientation = (Number) metadata.get(ResultMetadataType.ORIENTATION);
if (orientation != null) {
intent.putExtra(Intents.Scan.RESULT_ORIENTATION, orientation.intValue());
}
String ecLevel = (String) metadata.get(ResultMetadataType.ERROR_CORRECTION_LEVEL);
if (ecLevel != null) {
intent.putExtra(Intents.Scan.RESULT_ERROR_CORRECTION_LEVEL, ecLevel);
}
#SuppressWarnings("unchecked")
Iterable<byte[]> byteSegments = (Iterable<byte[]>) metadata.get(ResultMetadataType.BYTE_SEGMENTS);
if (byteSegments != null) {
int i = 0;
for (byte[] byteSegment : byteSegments) {
intent.putExtra(Intents.Scan.RESULT_BYTE_SEGMENTS_PREFIX + i, byteSegment);
i++;
}
}
}
sendReplyMessage(R.id.return_scan_result, intent, resultDurationMS);
} else if (source == IntentSource.PRODUCT_SEARCH_LINK) {
// Reformulate the URL which triggered us into a query, so that the request goes to the same
// TLD as the scan URL.
int end = sourceUrl.lastIndexOf("/scan");
String replyURL = sourceUrl.substring(0, end) + "?q=" + resultHandler.getDisplayContents() + "&source=zxing";
sendReplyMessage(R.id.launch_product_query, replyURL, resultDurationMS);
} else if (source == IntentSource.ZXING_LINK) {
if (scanFromWebPageManager != null && scanFromWebPageManager.isScanFromWebPage()) {
String replyURL = scanFromWebPageManager.buildReplyURL(rawResult, resultHandler);
sendReplyMessage(R.id.launch_product_query, replyURL, resultDurationMS);
}
}
}
private void sendReplyMessage(int id, Object arg, long delayMS) {
if (handler != null) {
Message message = Message.obtain(handler, id, arg);
if (delayMS > 0L) {
handler.sendMessageDelayed(message, delayMS);
} else {
handler.sendMessage(message);
}
}
}
private void initCamera(SurfaceHolder surfaceHolder) {
if (surfaceHolder == null) {
throw new IllegalStateException("No SurfaceHolder provided");
}
if (cameraManager.isOpen()) {
Log.w(TAG, "initCamera() while already open -- late SurfaceView callback?");
return;
}
try {
cameraManager.openDriver(surfaceHolder);
// Creating the handler starts the preview, which can also throw a RuntimeException.
if (handler == null) {
handler = new CaptureActivityHandler(this, decodeFormats, decodeHints, characterSet, cameraManager);
}
decodeOrStoreSavedBitmap(null, null);
} catch (IOException ioe) {
Log.w(TAG, ioe);
displayFrameworkBugMessageAndExit();
} catch (RuntimeException e) {
// Barcode Scanner has seen crashes in the wild of this variety:
// java.?lang.?RuntimeException: Fail to connect to camera service
Log.w(TAG, "Unexpected error initializing camera", e);
displayFrameworkBugMessageAndExit();
}
}
private void displayFrameworkBugMessageAndExit() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.app_name));
builder.setMessage(getString(R.string.msg_camera_framework_bug));
builder.setPositiveButton(R.string.button_ok, new FinishListener(this));
builder.setOnCancelListener(new FinishListener(this));
builder.show();
}
public void restartPreviewAfterDelay(long delayMS) {
if (handler != null) {
handler.sendEmptyMessageDelayed(R.id.restart_preview, delayMS);
}
resetStatusView();
}
private void resetStatusView() {
resultView.setVisibility(View.GONE);
statusView.setText(R.string.msg_default_status);
statusView.setVisibility(View.VISIBLE);
viewfinderView.setVisibility(View.VISIBLE);
lastResult = null;
}
public void drawViewfinder() {
viewfinderView.drawViewfinder();
}
}
your SplashScreen Java should look like this:
{
Intent mainIntent = new Intent().setClass(SplashScreenActivity.this, com.google.zxing.client.android.CaptureActivity.class);
// use startActivityForResult instead of startActivity
startActivityForResult(mainIntent)
}
but I guess you already solved you problem!
I'm trying to create an App that sends a data text via USB Serial from a Android to a computer and receive. I'm using a Sample from Android called AdbTestActivity to try to connect with my device. I am able to find the device but when I try to open the device using UsbDeviceConnection connection = mManager.openDevice(device); It returned me response 'null'
AdbTestActivity.java
package com.android.adb;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Rect;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.widget.TextView;
/* Main activity for the adb test program */
public class AdbTestActivity extends Activity {
private static final String TAG = "AdbTestActivity";
private TextView mLog;
private UsbManager mManager;
private UsbDevice mDevice;
private UsbDeviceConnection mDeviceConnection;
private UsbInterface mInterface;
private AdbDevice mAdbDevice;
private static final int MESSAGE_LOG = 1;
private static final int MESSAGE_DEVICE_ONLINE = 2;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.adb);
mLog = (TextView)findViewById(R.id.log);
mManager = (UsbManager)getSystemService(Context.USB_SERVICE);
// check for existing devices
for (UsbDevice device : mManager.getDeviceList().values()) {
UsbInterface intf = findAdbInterface(device);
if (setAdbInterface(device, intf)) {
break;
}
}
// listen for new devices
IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
registerReceiver(mUsbReceiver, filter);
}
#Override
public void onDestroy() {
unregisterReceiver(mUsbReceiver);
setAdbInterface(null, null);
super.onDestroy();
}
public void log(String s) {
Message m = Message.obtain(mHandler, MESSAGE_LOG);
m.obj = s;
mHandler.sendMessage(m);
}
private void appendLog(String text) {
Rect r = new Rect();
mLog.getDrawingRect(r);
int maxLines = r.height() / mLog.getLineHeight() - 1;
text = mLog.getText() + "\n" + text;
// see how many lines we have
int index = text.lastIndexOf('\n');
int count = 0;
while (index > 0 && count <= maxLines) {
count++;
index = text.lastIndexOf('\n', index - 1);
}
// truncate to maxLines
if (index > 0) {
text = text.substring(index + 1);
}
mLog.setText(text);
}
public void deviceOnline(AdbDevice device) {
Message m = Message.obtain(mHandler, MESSAGE_DEVICE_ONLINE);
m.obj = device;
mHandler.sendMessage(m);
}
private void handleDeviceOnline(AdbDevice device) {
log("device online: " + device.getSerial());
device.openSocket("shell:exec logcat");
}
// Sets the current USB device and interface
private boolean setAdbInterface(UsbDevice device, UsbInterface intf) {
if (mDeviceConnection != null) {
if (mInterface != null) {
mDeviceConnection.releaseInterface(mInterface);
mInterface = null;
}
mDeviceConnection.close();
mDevice = null;
mDeviceConnection = null;
}
if (device != null && intf != null) {
//Here returns null connection
UsbDeviceConnection connection = mManager.openDevice(device);
if (connection != null) {
log("open succeeded");
if (connection.claimInterface(intf, false)) {
log("claim interface succeeded");
mDevice = device;
mDeviceConnection = connection;
mInterface = intf;
mAdbDevice = new AdbDevice(this, mDeviceConnection, intf);
log("call start");
mAdbDevice.start();
return true;
} else {
log("claim interface failed");
connection.close();
}
} else {
log("open failed");
}
}
if (mDeviceConnection == null && mAdbDevice != null) {
mAdbDevice.stop();
mAdbDevice = null;
}
return false;
}
// searches for an adb interface on the given USB device
static private UsbInterface findAdbInterface(UsbDevice device) {
Log.d(TAG, "findAdbInterface " + device);
int count = device.getInterfaceCount();
for (int i = 0; i < count; i++) {
UsbInterface intf = device.getInterface(i);
if (intf.getInterfaceClass() == 255 && intf.getInterfaceSubclass() == 66 &&
intf.getInterfaceProtocol() == 1) {
return intf;
}
if (intf.getInterfaceClass() == 2 && intf.getInterfaceSubclass() == 2 &&
intf.getInterfaceProtocol() == 1) {
return intf;
}
}
return null;
}
BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
UsbInterface intf = findAdbInterface(device);
if (intf != null) {
log("Found adb interface " + intf);
setAdbInterface(device, intf);
}
} else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
String deviceName = device.getDeviceName();
if (mDevice != null && mDevice.equals(deviceName)) {
log("adb interface removed");
setAdbInterface(null, null);
}
}
}
};
Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_LOG:
appendLog((String)msg.obj);
break;
case MESSAGE_DEVICE_ONLINE:
handleDeviceOnline((AdbDevice)msg.obj);
break;
}
}
};
}
I also tried to insert my vendor and the product in the device_filter.xml, but it didn't work.
<resources>
<usb-device class="255" subclass="66" protocol="0" />
<usb-device vendor-id="5401" product-id="32" />
</resources>
Manifest
...
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="#xml/device_filter" />
You should ask before permission to open usb device, like that:
final String usbPermissionName = "com.android.example.USB_PERMISSION";
BroadcastReceiver usbPermissionReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
if (usbPermissionName.equals(intent.getAction())) {
UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if(intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
synchronized (this) {
mManager.openDevice(device);
}
}
}
}
};
registerReceiver(usbPermissionReceiver, new IntentFilter(usbPermissionName));
PendingIntent permissionPendingIntent = PendingIntent.getBroadcast(instance, 0, new Intent(usbPermissionName), 0);
usbManager.requestPermission(usbDevice, permissionPendingIntent);