diff options
Diffstat (limited to 'src/android')
4 files changed, 640 insertions, 17 deletions
diff --git a/src/android/bluetooth/bluetooth.pri b/src/android/bluetooth/bluetooth.pri index 16d3b612..fa811ac1 100644 --- a/src/android/bluetooth/bluetooth.pri +++ b/src/android/bluetooth/bluetooth.pri @@ -1,6 +1,6 @@ CONFIG += java DESTDIR = $$[QT_INSTALL_PREFIX/get]/jar -API_VERSION = android-18 +API_VERSION = android-21 PATHPREFIX = $$PWD/src/org/qtproject/qt5/android/bluetooth @@ -9,7 +9,8 @@ JAVASOURCES += \ $$PATHPREFIX/QtBluetoothBroadcastReceiver.java \ $$PATHPREFIX/QtBluetoothSocketServer.java \ $$PATHPREFIX/QtBluetoothInputStreamThread.java \ - $$PATHPREFIX/QtBluetoothLE.java + $$PATHPREFIX/QtBluetoothLE.java \ + $$PATHPREFIX/QtBluetoothLEServer.java # install target.path = $$[QT_INSTALL_PREFIX]/jar diff --git a/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLE.java b/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLE.java index d2373051..59ed0992 100644 --- a/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLE.java +++ b/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLE.java @@ -53,6 +53,7 @@ import android.os.Looper; import android.util.Log; import java.lang.reflect.Method; import java.util.concurrent.atomic.AtomicInteger; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Hashtable; @@ -61,6 +62,7 @@ import java.util.List; import java.util.NoSuchElementException; import java.util.UUID; + public class QtBluetoothLE { private static final String TAG = "QtBluetoothGatt"; private final BluetoothAdapter mBluetoothAdapter; @@ -1315,6 +1317,32 @@ public class QtBluetoothLE { return modifiedHandle; } + // Directly called from public Qt API + public boolean requestConnectionUpdatePriority(double minimalInterval) + { + if (mBluetoothGatt == null) + return false; + + try { + //Android API v21 + Method connectionUpdateMethod = mBluetoothGatt.getClass().getDeclaredMethod( + "requestConnectionPriority", int.class); + if (connectionUpdateMethod == null) + return false; + + int requestPriority = 0; // BluetoothGatt.CONNECTION_PRIORITY_BALANCED + if (minimalInterval < 30) + requestPriority = 1; // BluetoothGatt.CONNECTION_PRIORITY_HIGH + else if (minimalInterval > 100) + requestPriority = 2; //BluetoothGatt/CONNECTION_PRIORITY_LOW_POWER + + Object result = connectionUpdateMethod.invoke(mBluetoothGatt, requestPriority); + return (Boolean) result; + } catch (Exception ex) { + return false; + } + } + public native void leConnectionStateChange(long qtObject, int wasErrorTransition, int newState); public native void leServicesDiscovered(long qtObject, int errorCode, String uuidList); public native void leServiceDetailDiscoveryFinished(long qtObject, final String serviceUuid, diff --git a/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLEServer.java b/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLEServer.java new file mode 100644 index 00000000..00217904 --- /dev/null +++ b/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLEServer.java @@ -0,0 +1,585 @@ +/**************************************************************************** + ** + ** Copyright (C) 2016 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the QtBluetooth module of the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:LGPL$ + ** Commercial License Usage + ** Licensees holding valid commercial Qt licenses may use this file in + ** accordance with the commercial license agreement provided with the + ** Software or, alternatively, in accordance with the terms contained in + ** a written agreement between you and The Qt Company. For licensing terms + ** and conditions see https://www.qt.io/terms-conditions. For further + ** information use the contact form at https://www.qt.io/contact-us. + ** + ** GNU Lesser General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU Lesser + ** General Public License version 3 as published by the Free Software + ** Foundation and appearing in the file LICENSE.LGPL3 included in the + ** packaging of this file. Please review the following information to + ** ensure the GNU Lesser General Public License version 3 requirements + ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. + ** + ** GNU General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU + ** General Public License version 2.0 or (at your option) the GNU General + ** Public license version 3 or any later version approved by the KDE Free + ** Qt Foundation. The licenses are as published by the Free Software + ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 + ** included in the packaging of this file. Please review the following + ** information to ensure the GNU General Public License requirements will + ** be met: https://www.gnu.org/licenses/gpl-2.0.html and + ** https://www.gnu.org/licenses/gpl-3.0.html. + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + +package org.qtproject.qt5.android.bluetooth; + +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattDescriptor; +import android.bluetooth.BluetoothGattService; +import android.content.Context; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattServer; +import android.bluetooth.BluetoothGattServerCallback; +import android.bluetooth.BluetoothManager; +import android.bluetooth.BluetoothProfile; +import android.bluetooth.le.AdvertiseCallback; +import android.bluetooth.le.AdvertiseData; +import android.bluetooth.le.AdvertiseData.Builder; +import android.bluetooth.le.AdvertiseSettings; +import android.bluetooth.le.BluetoothLeAdvertiser; +import android.os.ParcelUuid; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.HashMap; +import java.util.UUID; + +public class QtBluetoothLEServer { + private static final String TAG = "QtBluetoothGattServer"; + + /* Pointer to the Qt object that "owns" the Java object */ + @SuppressWarnings({"CanBeFinal", "WeakerAccess"}) + long qtObject = 0; + @SuppressWarnings("WeakerAccess") + + private Context qtContext = null; + + // Bluetooth members + private final BluetoothAdapter mBluetoothAdapter; + private BluetoothGattServer mGattServer = null; + private BluetoothLeAdvertiser mLeAdvertiser = null; + + /* + As per Bluetooth specification each connected device can have individual and persistent + Client characteristic configurations (see Bluetooth Spec 5.0 Vol 3 Part G 3.3.3.3) + This class manages the existing configurrations. + */ + private class ClientCharacteristicManager { + private final HashMap<BluetoothGattCharacteristic, List<Entry>> notificationStore = new HashMap<BluetoothGattCharacteristic, List<Entry>>(); + + private class Entry { + BluetoothDevice device = null; + byte[] value = null; + boolean isConnected = false; + } + + public void insertOrUpdate(BluetoothGattCharacteristic characteristic, + BluetoothDevice device, byte[] newValue) + { + if (notificationStore.containsKey(characteristic)) { + + List<Entry> entries = notificationStore.get(characteristic); + for (int i = 0; i < entries.size(); i++) { + if (entries.get(i).device.equals(device)) { + Entry e = entries.get(i); + e.value = newValue; + entries.set(i, e); + return; + } + } + + // not match so far -> add device to list + Entry e = new Entry(); + e.device = device; + e.value = newValue; + e.isConnected = true; + entries.add(e); + return; + } + + // new characteristic + Entry e = new Entry(); + e.device = device; + e.value = newValue; + e.isConnected = true; + List<Entry> list = new LinkedList<Entry>(); + list.add(e); + notificationStore.put(characteristic, list); + } + + /* + Marks client characteristic configuration entries as (in)active based the associated + devices general connectivity state. + This function avoids that existing configurations are not acted + upon when the associated device is not connected. + */ + public void markDeviceConnectivity(BluetoothDevice device, boolean isConnected) + { + final Iterator<BluetoothGattCharacteristic> keys = notificationStore.keySet().iterator(); + while (keys.hasNext()) { + final BluetoothGattCharacteristic characteristic = keys.next(); + final List<Entry> entries = notificationStore.get(characteristic); + if (entries == null) + continue; + + ListIterator<Entry> charConfig = entries.listIterator(); + while (charConfig.hasNext()) { + Entry e = charConfig.next(); + if (e.device.equals(device)) + e.isConnected = isConnected; + } + } + } + + // Returns list of all BluetoothDevices which require notification or indication. + // No match returns an empty list. + List<BluetoothDevice> getToBeUpdatedDevices(BluetoothGattCharacteristic characteristic) + { + ArrayList<BluetoothDevice> result = new ArrayList<BluetoothDevice>(); + if (!notificationStore.containsKey(characteristic)) + return result; + + final ListIterator<Entry> iter = notificationStore.get(characteristic).listIterator(); + while (iter.hasNext()) + result.add(iter.next().device); + + return result; + } + + // Returns null if no match; otherwise the configured actual client characteristic + // configuration value + byte[] valueFor(BluetoothGattCharacteristic characteristic, BluetoothDevice device) + { + if (!notificationStore.containsKey(characteristic)) + return null; + + List<Entry> entries = notificationStore.get(characteristic); + for (int i = 0; i < entries.size(); i++) { + final Entry entry = entries.get(i); + if (entry.device.equals(device) && entry.isConnected == true) + return entries.get(i).value; + } + + return null; + } + } + + private static final UUID CLIENT_CHARACTERISTIC_CONFIGURATION_UUID = UUID + .fromString("00002902-0000-1000-8000-00805f9b34fb"); + ClientCharacteristicManager clientCharacteristicManager = new ClientCharacteristicManager(); + + public QtBluetoothLEServer(Context context) + { + qtContext = context; + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + + if (mBluetoothAdapter == null || qtContext == null) { + Log.w(TAG, "Missing Bluetooth adapter or Qt context. Peripheral role disabled."); + return; + } + + BluetoothManager manager = (BluetoothManager) qtContext.getSystemService(Context.BLUETOOTH_SERVICE); + if (manager == null) { + Log.w(TAG, "Bluetooth service not available."); + return; + } + + mGattServer = manager.openGattServer(qtContext, mGattServerListener); + mLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser(); + + if (!mBluetoothAdapter.isMultipleAdvertisementSupported()) + Log.w(TAG, "Device does not support Bluetooth Low Energy advertisement."); + else + Log.w(TAG, "Let's do BTLE Peripheral."); + } + + /* + * Call back handler for the Gatt Server. + */ + private BluetoothGattServerCallback mGattServerListener = new BluetoothGattServerCallback() + { + @Override + public void onConnectionStateChange(BluetoothDevice device, int status, int newState) { + Log.w(TAG, "Our gatt server connection state changed, new state: " + newState); + super.onConnectionStateChange(device, status, newState); + + int qtControllerState = 0; + switch (newState) { + case BluetoothProfile.STATE_DISCONNECTED: + qtControllerState = 0; // QLowEnergyController::UnconnectedState + clientCharacteristicManager.markDeviceConnectivity(device, false); + break; + case BluetoothProfile.STATE_CONNECTED: + clientCharacteristicManager.markDeviceConnectivity(device, true); + qtControllerState = 2; // QLowEnergyController::ConnectedState + break; + } + + int qtErrorCode; + switch (status) { + case BluetoothGatt.GATT_SUCCESS: + qtErrorCode = 0; break; + default: + Log.w(TAG, "Unhandled error code on peripheral connectionStateChanged: " + status + " " + newState); + qtErrorCode = status; + break; + } + + leServerConnectionStateChange(qtObject, qtErrorCode, qtControllerState); + } + + @Override + public void onServiceAdded(int status, BluetoothGattService service) { + super.onServiceAdded(status, service); + } + + @Override + public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) + { + byte[] dataArray; + try { + dataArray = Arrays.copyOfRange(characteristic.getValue(), offset, characteristic.getValue().length); + mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, dataArray); + } catch (Exception ex) { + Log.w(TAG, "onCharacteristicReadRequest: " + requestId + " " + offset + " " + characteristic.getValue().length); + ex.printStackTrace(); + mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_FAILURE, offset, null); + } + + super.onCharacteristicReadRequest(device, requestId, offset, characteristic); + } + + @Override + public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, + boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) + { + Log.w(TAG, "onCharacteristicWriteRequest"); + int resultStatus = BluetoothGatt.GATT_SUCCESS; + boolean sendNotificationOrIndication = false; + if (!preparedWrite) { // regular write + if (offset == 0) { + characteristic.setValue(value); + leServerCharacteristicChanged(qtObject, characteristic, value); + sendNotificationOrIndication = true; + } else { + // This should not really happen as per Bluetooth spec + Log.w(TAG, "onCharacteristicWriteRequest: !preparedWrite, offset " + offset + ", Not supported"); + resultStatus = BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED; + } + + + } else { + Log.w(TAG, "onCharacteristicWriteRequest: preparedWrite, offset " + offset + ", Not supported"); + resultStatus = BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED; + + // TODO we need to record all requests and execute them in one go once onExecuteWrite() is received + // we use a queue to remember the pending requests + // TODO we are ignoring the device identificator for now -> Bluetooth spec requires a queue per device + } + + + if (responseNeeded) + mGattServer.sendResponse(device, requestId, resultStatus, offset, value); + if (sendNotificationOrIndication) + sendNotificationsOrIndications(characteristic); + + super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value); + } + + @Override + public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) + { + byte[] dataArray = descriptor.getValue(); + try { + if (descriptor.getUuid().equals(CLIENT_CHARACTERISTIC_CONFIGURATION_UUID)) { + dataArray = clientCharacteristicManager.valueFor(descriptor.getCharacteristic(), device); + if (dataArray == null) + dataArray = descriptor.getValue(); + } + + dataArray = Arrays.copyOfRange(dataArray, offset, dataArray.length); + mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, dataArray); + } catch (Exception ex) { + Log.w(TAG, "onDescriptorReadRequest: " + requestId + " " + offset + " " + dataArray.length); + ex.printStackTrace(); + mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_FAILURE, offset, null); + } + + super.onDescriptorReadRequest(device, requestId, offset, descriptor); + } + + @Override + public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, + boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) + { + int resultStatus = BluetoothGatt.GATT_SUCCESS; + if (!preparedWrite) { // regular write + if (offset == 0) { + descriptor.setValue(value); + + if (descriptor.getUuid().equals(CLIENT_CHARACTERISTIC_CONFIGURATION_UUID)) { + clientCharacteristicManager.insertOrUpdate(descriptor.getCharacteristic(), + device, value); + } + + leServerDescriptorWritten(qtObject, descriptor, value); + } else { + // This should not really happen as per Bluetooth spec + Log.w(TAG, "onDescriptorWriteRequest: !preparedWrite, offset " + offset + ", Not supported"); + resultStatus = BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED; + } + + + } else { + Log.w(TAG, "onDescriptorWriteRequest: preparedWrite, offset " + offset + ", Not supported"); + resultStatus = BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED; + // TODO we need to record all requests and execute them in one go once onExecuteWrite() is received + // we use a queue to remember the pending requests + // TODO we are ignoring the device identificator for now -> Bluetooth spec requires a queue per device + } + + + if (responseNeeded) + mGattServer.sendResponse(device, requestId, resultStatus, offset, value); + + super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value); + } + + @Override + public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) + { + // TODO not yet implemented -> return proper GATT error for it + mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED, 0, null); + + super.onExecuteWrite(device, requestId, execute); + } + + @Override + public void onNotificationSent(BluetoothDevice device, int status) { + super.onNotificationSent(device, status); + Log.w(TAG, "onNotificationSent" + device + " " + status); + } + + // MTU change disabled since it requires API level 22. Right now we only enforce lvl 21 +// @Override +// public void onMtuChanged(BluetoothDevice device, int mtu) { +// super.onMtuChanged(device, mtu); +// } + }; + + public boolean connectServer() + { + if (mGattServer == null) + return false; + + return true; + } + + public void disconnectServer() + { + if (mGattServer == null) + return; + + mGattServer.close(); + } + + public boolean startAdvertising(AdvertiseData advertiseData, + AdvertiseData scanResponse, + AdvertiseSettings settings) + { + if (mLeAdvertiser == null) + return false; + + connectServer(); + + Log.w(TAG, "Starting to advertise."); + mLeAdvertiser.startAdvertising(settings, advertiseData, scanResponse, mAdvertiseListener); + + return true; + } + + public void stopAdvertising() + { + if (mLeAdvertiser == null) + return; + + mLeAdvertiser.stopAdvertising(mAdvertiseListener); + Log.w(TAG, "Advertisement stopped."); + } + + public void addService(BluetoothGattService service) + { + if (mGattServer == null) + return; + + mGattServer.addService(service); + } + + /* + Check the client characteristics configuration for the given characteristic + and sends notifications or indications as per required. + */ + private void sendNotificationsOrIndications(BluetoothGattCharacteristic characteristic) + { + final ListIterator<BluetoothDevice> iter = + clientCharacteristicManager.getToBeUpdatedDevices(characteristic).listIterator(); + + // TODO This quick loop over multiple devices should be synced with onNotificationSent(). + // The next notifyCharacteristicChanged() call must wait until onNotificationSent() + // was received. At this becomes an issue when the server accepts multiple remote + // devices at the same time. + while (iter.hasNext()) { + final BluetoothDevice device = iter.next(); + final byte[] clientCharacteristicConfig = clientCharacteristicManager.valueFor(characteristic, device); + if (clientCharacteristicConfig != null) { + if (Arrays.equals(clientCharacteristicConfig, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)) { + mGattServer.notifyCharacteristicChanged(device, characteristic, false); + } else if (Arrays.equals(clientCharacteristicConfig, BluetoothGattDescriptor.ENABLE_INDICATION_VALUE)) { + mGattServer.notifyCharacteristicChanged(device, characteristic, true); + } + } + } + } + + /* + Updates the local database value for the given characteristic with \a charUuid and + \a newValue. If notifications for this task are enabled an approproiate notification will + be send to the remote client. + + This function is called from the Qt thread. + */ + public boolean writeCharacteristic(BluetoothGattService service, UUID charUuid, byte[] newValue) + { + BluetoothGattCharacteristic foundChar = null; + List<BluetoothGattCharacteristic> charList = service.getCharacteristics(); + for (BluetoothGattCharacteristic iter: charList) { + if (iter.getUuid().equals(charUuid) && foundChar == null) { + foundChar = iter; + // don't break here since we want to check next condition below on next iteration + } else if (iter.getUuid().equals(charUuid)) { + Log.w(TAG, "Found second char with same UUID. Wrong char may have been selected."); + break; + } + } + + if (foundChar == null) { + Log.w(TAG, "writeCharacteristic: update for unknown characteristic failed"); + return false; + } + + foundChar.setValue(newValue); + sendNotificationsOrIndications(foundChar); + + return true; + } + + /* + Updates the local database value for the given \a descUuid to \a newValue. + + This function is called from the Qt thread. + */ + public boolean writeDescriptor(BluetoothGattService service, UUID charUuid, UUID descUuid, + byte[] newValue) + { + BluetoothGattDescriptor foundDesc = null; + BluetoothGattCharacteristic foundChar = null; + final List<BluetoothGattCharacteristic> charList = service.getCharacteristics(); + for (BluetoothGattCharacteristic iter: charList) { + if (!iter.getUuid().equals(charUuid)) + continue; + + if (foundChar == null) { + foundChar = iter; + } else { + Log.w(TAG, "Found second char with same UUID. Wrong char may have been selected."); + break; + } + } + + if (foundChar != null) + foundDesc = foundChar.getDescriptor(descUuid); + + if (foundChar == null || foundDesc == null) { + Log.w(TAG, "writeDescriptor: update for unknown char or desc failed (" + foundChar + ")"); + return false; + } + + // we even write CLIENT_CHARACTERISTIC_CONFIGURATION_UUID this way as we choose + // to interpret the server's call as a change of the default value. + foundDesc.setValue(newValue); + + return true; + } + + /* + * Call back handler for Advertisement requests. + */ + private AdvertiseCallback mAdvertiseListener = new AdvertiseCallback() + { + @Override + public void onStartSuccess(AdvertiseSettings settingsInEffect) { + super.onStartSuccess(settingsInEffect); + } + + @Override + public void onStartFailure(int errorCode) { + Log.e(TAG, "Advertising failure: " + errorCode); + super.onStartFailure(errorCode); + + // changing errorCode here implies changes to errorCode handling on Qt side + int qtErrorCode = 0; + switch (errorCode) { + case AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED: + return; // ignore -> noop + case AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE: + qtErrorCode = 1; + break; + case AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED: + qtErrorCode = 2; + break; + default: // default maps to internal error + case AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR: + qtErrorCode = 3; + break; + case AdvertiseCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS: + qtErrorCode = 4; + break; + } + + if (qtErrorCode > 0) + leServerAdvertisementError(qtObject, qtErrorCode); + } + }; + + public native void leServerConnectionStateChange(long qtObject, int errorCode, int newState); + public native void leServerAdvertisementError(long qtObject, int status); + public native void leServerCharacteristicChanged(long qtObject, + BluetoothGattCharacteristic characteristic, + byte[] newValue); + public native void leServerDescriptorWritten(long qtObject, + BluetoothGattDescriptor descriptor, + byte[] newValue); +} diff --git a/src/android/nfc/src/org/qtproject/qt5/android/nfc/QtNfc.java b/src/android/nfc/src/org/qtproject/qt5/android/nfc/QtNfc.java index 47dcf1bf..345b87d3 100644 --- a/src/android/nfc/src/org/qtproject/qt5/android/nfc/QtNfc.java +++ b/src/android/nfc/src/org/qtproject/qt5/android/nfc/QtNfc.java @@ -62,22 +62,25 @@ public class QtNfc static public NfcAdapter m_adapter = null; static public PendingIntent m_pendingIntent = null; static public IntentFilter[] m_filters; - static public Activity m_activity; + static public Context m_context = null; + static public Activity m_activity = null; static public void setContext(Context context) { - if (!(context instanceof Activity)) { - Log.w(TAG, "NFC only works with Android activities and not in Android services. " + - "NFC has been disabled."); + m_context = context; + if (context instanceof Activity) m_activity = (Activity) context; + m_adapter = NfcAdapter.getDefaultAdapter(context); + + if (m_activity == null) { + Log.w(TAG, "New NFC tags will only be recognized with Android activities and not with Android services."); return; } - m_activity = (Activity)context; - m_adapter = NfcAdapter.getDefaultAdapter(m_activity); if (m_adapter == null) { //Log.e(TAG, "No NFC available"); return; } + m_pendingIntent = PendingIntent.getActivity( m_activity, 0, @@ -86,7 +89,7 @@ public class QtNfc //Log.d(TAG, "Pending intent:" + m_pendingIntent); - IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED); + IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED); m_filters = new IntentFilter[]{ filter @@ -103,22 +106,26 @@ public class QtNfc static public boolean start() { - if (m_adapter == null) return false; + if (m_adapter == null || m_activity == null) return false; + m_activity.runOnUiThread(new Runnable() { public void run() { //Log.d(TAG, "Enabling NFC"); - IntentFilter[] filters = new IntentFilter[2]; + IntentFilter[] filters = new IntentFilter[3]; filters[0] = new IntentFilter(); - filters[0].addAction(NfcAdapter.ACTION_NDEF_DISCOVERED); + filters[0].addAction(NfcAdapter.ACTION_TAG_DISCOVERED); filters[0].addCategory(Intent.CATEGORY_DEFAULT); + filters[1] = new IntentFilter(); + filters[1].addAction(NfcAdapter.ACTION_NDEF_DISCOVERED); + filters[1].addCategory(Intent.CATEGORY_DEFAULT); try { - filters[0].addDataType("*/*"); + filters[1].addDataType("*/*"); } catch (MalformedMimeTypeException e) { throw new RuntimeException("Check your mime type."); } // some tags will report as tech, even if they are ndef formated/formatable. - filters[1] = new IntentFilter(); - filters[1].addAction(NfcAdapter.ACTION_TECH_DISCOVERED); + filters[2] = new IntentFilter(); + filters[2].addAction(NfcAdapter.ACTION_TECH_DISCOVERED); String[][] techList = new String[][]{ {"android.nfc.tech.Ndef"}, {"android.nfc.tech.NdefFormatable"} @@ -136,7 +143,8 @@ public class QtNfc static public boolean stop() { - if (m_adapter == null) return false; + if (m_adapter == null || m_activity == null) return false; + m_activity.runOnUiThread(new Runnable() { public void run() { //Log.d(TAG, "Disabling NFC"); @@ -153,11 +161,11 @@ public class QtNfc static public boolean isAvailable() { - m_adapter = NfcAdapter.getDefaultAdapter(m_activity); if (m_adapter == null) { //Log.e(TAG, "No NFC available (Adapter is null)"); return false; } + return m_adapter.isEnabled(); } @@ -165,6 +173,7 @@ public class QtNfc { Log.d(TAG, "getStartIntent"); if (m_activity == null) return null; + Intent intent = m_activity.getIntent(); if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction()) || NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction()) || |