From 981f8d487ca24f4fca4c2c28e54f725b19579976 Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Thu, 17 Sep 2015 14:09:48 +0200 Subject: [PATCH] Android Wear support added to UART profile --- .idea/gradle.xml | 2 + .idea/modules.xml | 5 +- app/app.iml | 20 +- app/build.gradle | 18 +- app/libs/nrf-logger-v2.0.jar | Bin 29046 -> 0 bytes app/sources/nrf-logger-v2.0-source.jar | Bin 20458 -> 0 bytes app/src/main/AndroidManifest.xml | 12 +- .../android/nrftoolbox/bpm/BPMManager.java | 2 +- .../android/nrftoolbox/csc/CSCActivity.java | 2 +- .../android/nrftoolbox/csc/CSCManager.java | 2 +- .../android/nrftoolbox/csc/CSCService.java | 2 +- .../dfu/settings/SettingsFragment.java | 8 +- .../nrftoolbox/gls/GlucoseManager.java | 2 +- .../android/nrftoolbox/hrs/HRSManager.java | 2 +- .../android/nrftoolbox/hts/HTSActivity.java | 2 +- .../nrftoolbox/profile/BleManager.java | 6 +- .../profile/BleManagerCallbacks.java | 2 +- .../nrftoolbox/profile/BleProfileService.java | 21 +- .../proximity/ProximityManager.java | 2 +- .../proximity/ProximityService.java | 5 +- .../android/nrftoolbox/rsc/RSCManager.java | 2 +- .../nrftoolbox/template/TemplateManager.java | 2 +- .../android/nrftoolbox/uart/UARTActivity.java | 81 ++- .../android/nrftoolbox/uart/UARTService.java | 112 +++- .../uart/database/DatabaseHelper.java | 45 +- .../UARTConfigurationSynchronizer.java | 129 +++++ .../wearable/MainWearableListenerService.java | 69 +++ .../ic_launcher.png | Bin .../ic_launcher.png | Bin .../ic_launcher.png | Bin .../ic_launcher.png | Bin app/src/main/res/values/strings.xml | 5 +- app/src/main/res/values/strings_dfu.xml | 167 +++--- app/src/main/res/values/strings_uart.xml | 2 +- app/src/main/res/values/styles.xml | 1 + app/src/main/res/xml/settings_dfu.xml | 4 + common/.gitignore | 1 + common/build.gradle | 46 ++ common/common.iml | 95 ++++ common/proguard-rules.pro | 17 + common/src/main/AndroidManifest.xml | 28 + .../android/nrftoolbox/error/GattError.java | 150 +++++ .../nrftoolbox/utility/DebugLogger.java | 2 +- .../nrftoolbox/wearable/common/Constants.java | 66 +++ common/src/main/res/values/strings.xml | 26 + gradle/wrapper/gradle-wrapper.properties | 4 +- settings.gradle | 2 +- wear/.gitignore | 1 + wear/build.gradle | 29 + wear/proguard-rules.pro | 17 + wear/src/main/AndroidManifest.xml | 74 +++ .../android/nrftoolbox/DeviceItemLayout.java | 121 ++++ .../android/nrftoolbox/DevicesAdapter.java | 219 ++++++++ .../android/nrftoolbox/ScannerActivity.java | 174 ++++++ .../android/nrftoolbox/ble/BleManager.java | 528 ++++++++++++++++++ .../nrftoolbox/ble/BleManagerCallbacks.java | 80 +++ .../android/nrftoolbox/ble/BleProfile.java | 124 ++++ .../android/nrftoolbox/ble/BleProfileApi.java | 63 +++ .../nrftoolbox/ble/BleProfileProvider.java | 36 ++ .../nrftoolbox/ble/BleProfileService.java | 302 ++++++++++ .../nrftoolbox/uart/UARTCommandsActivity.java | 282 ++++++++++ .../nrftoolbox/uart/UARTCommandsAdapter.java | 106 ++++ .../uart/UARTConfigurationItemLayout.java | 126 +++++ .../uart/UARTConfigurationsActivity.java | 238 ++++++++ .../uart/UARTConfigurationsAdapter.java | 88 +++ .../android/nrftoolbox/uart/UARTProfile.java | 151 +++++ .../nrftoolbox/uart/domain/Command.java | 129 +++++ .../uart/domain/UartConfiguration.java | 105 ++++ .../nrftoolbox/wearable/ActionReceiver.java | 81 +++ .../wearable/MainWearableListenerService.java | 87 +++ wear/src/main/res/color/item_background.xml | 28 + .../main/res/drawable-hdpi/ic_bluetooth.png | Bin 0 -> 333 bytes .../res/drawable-hdpi/ic_full_bluetooth.png | Bin 0 -> 597 bytes .../main/res/drawable-xhdpi/ic_bluetooth.png | Bin 0 -> 340 bytes .../res/drawable-xhdpi/ic_configurations.png | Bin 0 -> 93 bytes .../res/drawable-xhdpi/ic_full_bluetooth.png | Bin 0 -> 1556 bytes .../src/main/res/drawable-xhdpi/ic_uart_1.png | Bin 0 -> 520 bytes .../src/main/res/drawable-xhdpi/ic_uart_2.png | Bin 0 -> 1259 bytes .../src/main/res/drawable-xhdpi/ic_uart_3.png | Bin 0 -> 1386 bytes .../src/main/res/drawable-xhdpi/ic_uart_4.png | Bin 0 -> 789 bytes .../src/main/res/drawable-xhdpi/ic_uart_5.png | Bin 0 -> 1115 bytes .../src/main/res/drawable-xhdpi/ic_uart_6.png | Bin 0 -> 1510 bytes .../src/main/res/drawable-xhdpi/ic_uart_7.png | Bin 0 -> 914 bytes .../src/main/res/drawable-xhdpi/ic_uart_8.png | Bin 0 -> 1738 bytes .../src/main/res/drawable-xhdpi/ic_uart_9.png | Bin 0 -> 1521 bytes .../main/res/drawable-xhdpi/ic_uart_about.png | Bin 0 -> 2971 bytes .../main/res/drawable-xhdpi/ic_uart_down.png | Bin 0 -> 586 bytes .../res/drawable-xhdpi/ic_uart_forward.png | Bin 0 -> 1063 bytes .../main/res/drawable-xhdpi/ic_uart_left.png | Bin 0 -> 564 bytes .../main/res/drawable-xhdpi/ic_uart_pause.png | Bin 0 -> 434 bytes .../main/res/drawable-xhdpi/ic_uart_play.png | Bin 0 -> 848 bytes .../res/drawable-xhdpi/ic_uart_rewind.png | Bin 0 -> 1073 bytes .../main/res/drawable-xhdpi/ic_uart_right.png | Bin 0 -> 536 bytes .../res/drawable-xhdpi/ic_uart_settings.png | Bin 0 -> 2452 bytes .../main/res/drawable-xhdpi/ic_uart_stop.png | Bin 0 -> 357 bytes .../main/res/drawable-xhdpi/ic_uart_up.png | Bin 0 -> 614 bytes wear/src/main/res/drawable/ic_uart_action.xml | 45 ++ wear/src/main/res/layout/action_item.xml | 47 ++ .../main/res/layout/activity_grid_pager.xml | 41 ++ wear/src/main/res/layout/activity_list.xml | 42 ++ .../res/layout/activity_list_with_header.xml | 53 ++ .../main/res/layout/configuration_item.xml | 51 ++ wear/src/main/res/layout/device_item.xml | 62 ++ wear/src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3948 bytes wear/src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 4518 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 5056 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 7311 bytes wear/src/main/res/values/colors.xml | 32 ++ wear/src/main/res/values/dimens.xml | 27 + wear/src/main/res/values/strings.xml | 42 ++ wear/wear.iml | 102 ++++ 111 files changed, 4757 insertions(+), 147 deletions(-) delete mode 100644 app/libs/nrf-logger-v2.0.jar delete mode 100644 app/sources/nrf-logger-v2.0-source.jar create mode 100644 app/src/main/java/no/nordicsemi/android/nrftoolbox/uart/wearable/UARTConfigurationSynchronizer.java create mode 100644 app/src/main/java/no/nordicsemi/android/nrftoolbox/wearable/MainWearableListenerService.java rename app/src/main/res/{drawable-hdpi => mipmap-hdpi}/ic_launcher.png (100%) rename app/src/main/res/{drawable-mdpi => mipmap-mdpi}/ic_launcher.png (100%) rename app/src/main/res/{drawable-xhdpi => mipmap-xhdpi}/ic_launcher.png (100%) rename app/src/main/res/{drawable-xxhdpi => mipmap-xxhdpi}/ic_launcher.png (100%) create mode 100644 common/.gitignore create mode 100644 common/build.gradle create mode 100644 common/common.iml create mode 100644 common/proguard-rules.pro create mode 100644 common/src/main/AndroidManifest.xml create mode 100644 common/src/main/java/no/nordicsemi/android/nrftoolbox/error/GattError.java rename {app => common}/src/main/java/no/nordicsemi/android/nrftoolbox/utility/DebugLogger.java (97%) create mode 100644 common/src/main/java/no/nordicsemi/android/nrftoolbox/wearable/common/Constants.java create mode 100644 common/src/main/res/values/strings.xml create mode 100644 wear/.gitignore create mode 100644 wear/build.gradle create mode 100644 wear/proguard-rules.pro create mode 100644 wear/src/main/AndroidManifest.xml create mode 100644 wear/src/main/java/no/nordicsemi/android/nrftoolbox/DeviceItemLayout.java create mode 100644 wear/src/main/java/no/nordicsemi/android/nrftoolbox/DevicesAdapter.java create mode 100644 wear/src/main/java/no/nordicsemi/android/nrftoolbox/ScannerActivity.java create mode 100644 wear/src/main/java/no/nordicsemi/android/nrftoolbox/ble/BleManager.java create mode 100644 wear/src/main/java/no/nordicsemi/android/nrftoolbox/ble/BleManagerCallbacks.java create mode 100644 wear/src/main/java/no/nordicsemi/android/nrftoolbox/ble/BleProfile.java create mode 100644 wear/src/main/java/no/nordicsemi/android/nrftoolbox/ble/BleProfileApi.java create mode 100644 wear/src/main/java/no/nordicsemi/android/nrftoolbox/ble/BleProfileProvider.java create mode 100644 wear/src/main/java/no/nordicsemi/android/nrftoolbox/ble/BleProfileService.java create mode 100644 wear/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTCommandsActivity.java create mode 100644 wear/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTCommandsAdapter.java create mode 100644 wear/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTConfigurationItemLayout.java create mode 100644 wear/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTConfigurationsActivity.java create mode 100644 wear/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTConfigurationsAdapter.java create mode 100644 wear/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTProfile.java create mode 100644 wear/src/main/java/no/nordicsemi/android/nrftoolbox/uart/domain/Command.java create mode 100644 wear/src/main/java/no/nordicsemi/android/nrftoolbox/uart/domain/UartConfiguration.java create mode 100644 wear/src/main/java/no/nordicsemi/android/nrftoolbox/wearable/ActionReceiver.java create mode 100644 wear/src/main/java/no/nordicsemi/android/nrftoolbox/wearable/MainWearableListenerService.java create mode 100644 wear/src/main/res/color/item_background.xml create mode 100644 wear/src/main/res/drawable-hdpi/ic_bluetooth.png create mode 100644 wear/src/main/res/drawable-hdpi/ic_full_bluetooth.png create mode 100644 wear/src/main/res/drawable-xhdpi/ic_bluetooth.png create mode 100644 wear/src/main/res/drawable-xhdpi/ic_configurations.png create mode 100644 wear/src/main/res/drawable-xhdpi/ic_full_bluetooth.png create mode 100644 wear/src/main/res/drawable-xhdpi/ic_uart_1.png create mode 100644 wear/src/main/res/drawable-xhdpi/ic_uart_2.png create mode 100644 wear/src/main/res/drawable-xhdpi/ic_uart_3.png create mode 100644 wear/src/main/res/drawable-xhdpi/ic_uart_4.png create mode 100644 wear/src/main/res/drawable-xhdpi/ic_uart_5.png create mode 100644 wear/src/main/res/drawable-xhdpi/ic_uart_6.png create mode 100644 wear/src/main/res/drawable-xhdpi/ic_uart_7.png create mode 100644 wear/src/main/res/drawable-xhdpi/ic_uart_8.png create mode 100644 wear/src/main/res/drawable-xhdpi/ic_uart_9.png create mode 100644 wear/src/main/res/drawable-xhdpi/ic_uart_about.png create mode 100644 wear/src/main/res/drawable-xhdpi/ic_uart_down.png create mode 100644 wear/src/main/res/drawable-xhdpi/ic_uart_forward.png create mode 100644 wear/src/main/res/drawable-xhdpi/ic_uart_left.png create mode 100644 wear/src/main/res/drawable-xhdpi/ic_uart_pause.png create mode 100644 wear/src/main/res/drawable-xhdpi/ic_uart_play.png create mode 100644 wear/src/main/res/drawable-xhdpi/ic_uart_rewind.png create mode 100644 wear/src/main/res/drawable-xhdpi/ic_uart_right.png create mode 100644 wear/src/main/res/drawable-xhdpi/ic_uart_settings.png create mode 100644 wear/src/main/res/drawable-xhdpi/ic_uart_stop.png create mode 100644 wear/src/main/res/drawable-xhdpi/ic_uart_up.png create mode 100644 wear/src/main/res/drawable/ic_uart_action.xml create mode 100644 wear/src/main/res/layout/action_item.xml create mode 100644 wear/src/main/res/layout/activity_grid_pager.xml create mode 100644 wear/src/main/res/layout/activity_list.xml create mode 100644 wear/src/main/res/layout/activity_list_with_header.xml create mode 100644 wear/src/main/res/layout/configuration_item.xml create mode 100644 wear/src/main/res/layout/device_item.xml create mode 100644 wear/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 wear/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 wear/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 wear/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 wear/src/main/res/values/colors.xml create mode 100644 wear/src/main/res/values/dimens.xml create mode 100644 wear/src/main/res/values/strings.xml create mode 100644 wear/wear.iml diff --git a/.idea/gradle.xml b/.idea/gradle.xml index c75a99ee..c99df1f6 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -11,6 +11,8 @@ diff --git a/.idea/modules.xml b/.idea/modules.xml index 27665145..bfd895e8 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -3,9 +3,10 @@ + + - - + \ No newline at end of file diff --git a/app/app.iml b/app/app.iml index ea4ba425..517941ff 100644 --- a/app/app.iml +++ b/app/app.iml @@ -71,9 +71,12 @@ - - + + + + + @@ -92,15 +95,18 @@ - - + + + - - - + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 462efc49..5eb16d6f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,13 +2,14 @@ apply plugin: 'com.android.application' android { compileSdkVersion 23 - buildToolsVersion '23.0.0' + buildToolsVersion '23.0.1' + defaultConfig { applicationId "no.nordicsemi.android.nrftoolbox" minSdkVersion 18 targetSdkVersion 23 - versionCode 37 - versionName "1.15.0" + versionCode 39 + versionName "1.16.0" } buildTypes { release { @@ -20,14 +21,17 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:appcompat-v7:23.0.0' - compile 'com.android.support:design:23.0.0' + compile 'com.google.android.gms:play-services-wearable:7.8.0' + compile 'com.android.support:appcompat-v7:23.0.1' + compile 'com.android.support:design:23.0.1' compile 'no.nordicsemi.android.support.v18:scanner:0.1.1' + compile 'no.nordicsemi.android:log:2.0.0' compile('org.simpleframework:simple-xml:2.7.1') { exclude group: 'stax', module: 'stax-api' exclude group: 'xpp3', module: 'xpp3' } - compile project(':dfu') compile files('libs/achartengine-1.1.0.jar') - compile files('libs/nrf-logger-v2.0.jar') + compile project(':dfu') + compile project(':common') + wearApp project(':wear') } diff --git a/app/libs/nrf-logger-v2.0.jar b/app/libs/nrf-logger-v2.0.jar deleted file mode 100644 index 8d37a964baae3847c39b4bc0ef56de97b5ee9ae0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29046 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 ve^l>2 + + + + + + + + { /** Blood Pressure service UUID */ diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/csc/CSCActivity.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/csc/CSCActivity.java index 284b396c..d1f4fcaf 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/csc/CSCActivity.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/csc/CSCActivity.java @@ -36,9 +36,9 @@ import android.widget.TextView; import java.util.UUID; import no.nordicsemi.android.nrftoolbox.R; +import no.nordicsemi.android.nrftoolbox.profile.BleProfileService; import no.nordicsemi.android.nrftoolbox.csc.settings.SettingsActivity; import no.nordicsemi.android.nrftoolbox.csc.settings.SettingsFragment; -import no.nordicsemi.android.nrftoolbox.profile.BleProfileService; import no.nordicsemi.android.nrftoolbox.profile.BleProfileServiceReadyActivity; public class CSCActivity extends BleProfileServiceReadyActivity { diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/csc/CSCManager.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/csc/CSCManager.java index c9c10c26..f705fffc 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/csc/CSCManager.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/csc/CSCManager.java @@ -32,8 +32,8 @@ import java.util.Queue; import java.util.UUID; import no.nordicsemi.android.log.Logger; -import no.nordicsemi.android.nrftoolbox.parser.CSCMeasurementParser; import no.nordicsemi.android.nrftoolbox.profile.BleManager; +import no.nordicsemi.android.nrftoolbox.parser.CSCMeasurementParser; public class CSCManager extends BleManager { /** Cycling Speed and Cadence service UUID */ diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/csc/CSCService.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/csc/CSCService.java index 89017f35..66b57e73 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/csc/CSCService.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/csc/CSCService.java @@ -37,9 +37,9 @@ import android.support.v7.app.NotificationCompat; import no.nordicsemi.android.log.Logger; import no.nordicsemi.android.nrftoolbox.FeaturesActivity; import no.nordicsemi.android.nrftoolbox.R; -import no.nordicsemi.android.nrftoolbox.csc.settings.SettingsFragment; import no.nordicsemi.android.nrftoolbox.profile.BleManager; import no.nordicsemi.android.nrftoolbox.profile.BleProfileService; +import no.nordicsemi.android.nrftoolbox.csc.settings.SettingsFragment; public class CSCService extends BleProfileService implements CSCManagerCallbacks { private static final String TAG = "CSCService"; diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/dfu/settings/SettingsFragment.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/dfu/settings/SettingsFragment.java index c9aec844..2349da7c 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/dfu/settings/SettingsFragment.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/dfu/settings/SettingsFragment.java @@ -69,12 +69,16 @@ public class SettingsFragment extends PreferenceFragment implements DfuSettingsC final boolean disabled = !preferences.getBoolean(SETTINGS_PACKET_RECEIPT_NOTIFICATION_ENABLED, true); if (disabled) { new AlertDialog.Builder(getActivity()).setMessage(R.string.dfu_settings_dfu_number_of_packets_info).setTitle(R.string.dfu_settings_dfu_information) - .setNeutralButton(R.string.ok, null).show(); + .setPositiveButton(R.string.ok, null).show(); } } else if (SETTINGS_NUMBER_OF_PACKETS.equals(key)) { updateNumberOfPacketsSummary(); } else if (SETTINGS_MBR_SIZE.equals(key)) { updateMBRSize(); + } else if (SETTINGS_ASSUME_DFU_NODE.equals(key) && sharedPreferences.getBoolean(key, false)) { + new AlertDialog.Builder(getActivity()).setMessage(R.string.dfu_settings_dfu_assume_dfu_mode_info).setTitle(R.string.dfu_settings_dfu_information) + .setPositiveButton(R.string.ok, null) + .show(); } } @@ -93,7 +97,7 @@ public class SettingsFragment extends PreferenceFragment implements DfuSettingsC final int valueInt = Integer.parseInt(value); if (valueInt > 200) { new AlertDialog.Builder(getActivity()).setMessage(R.string.dfu_settings_dfu_number_of_packets_info).setTitle(R.string.dfu_settings_dfu_information) - .setNeutralButton(R.string.ok, null) + .setPositiveButton(R.string.ok, null) .show(); } } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/gls/GlucoseManager.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/gls/GlucoseManager.java index 9f3e6925..f9a9f933 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/gls/GlucoseManager.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/gls/GlucoseManager.java @@ -34,10 +34,10 @@ import java.util.Queue; import java.util.UUID; import no.nordicsemi.android.log.Logger; +import no.nordicsemi.android.nrftoolbox.profile.BleManager; import no.nordicsemi.android.nrftoolbox.parser.GlucoseMeasurementContextParser; import no.nordicsemi.android.nrftoolbox.parser.GlucoseMeasurementParser; import no.nordicsemi.android.nrftoolbox.parser.RecordAccessControlPointParser; -import no.nordicsemi.android.nrftoolbox.profile.BleManager; import no.nordicsemi.android.nrftoolbox.utility.DebugLogger; @SuppressWarnings("unused") diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/hrs/HRSManager.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/hrs/HRSManager.java index 25aafa5e..6c3d826d 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/hrs/HRSManager.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/hrs/HRSManager.java @@ -32,9 +32,9 @@ import java.util.UUID; import no.nordicsemi.android.log.Logger; import no.nordicsemi.android.nrftoolbox.R; +import no.nordicsemi.android.nrftoolbox.profile.BleManager; import no.nordicsemi.android.nrftoolbox.parser.BodySensorLocationParser; import no.nordicsemi.android.nrftoolbox.parser.HeartRateMeasurementParser; -import no.nordicsemi.android.nrftoolbox.profile.BleManager; /** * HRSManager class performs BluetoothGatt operations for connection, service discovery, enabling notification and reading characteristics. All operations required to connect to device with BLE HR diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/hts/HTSActivity.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/hts/HTSActivity.java index d3bcb43b..f1b3507e 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/hts/HTSActivity.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/hts/HTSActivity.java @@ -36,9 +36,9 @@ import java.text.DecimalFormat; import java.util.UUID; import no.nordicsemi.android.nrftoolbox.R; +import no.nordicsemi.android.nrftoolbox.profile.BleProfileService; import no.nordicsemi.android.nrftoolbox.hts.settings.SettingsActivity; import no.nordicsemi.android.nrftoolbox.hts.settings.SettingsFragment; -import no.nordicsemi.android.nrftoolbox.profile.BleProfileService; import no.nordicsemi.android.nrftoolbox.profile.BleProfileServiceReadyActivity; /** diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleManager.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleManager.java index f0aeae0e..340aaf5a 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleManager.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleManager.java @@ -37,9 +37,9 @@ import android.os.Handler; import java.util.Queue; import java.util.UUID; -import no.nordicsemi.android.error.GattError; import no.nordicsemi.android.log.ILogSession; import no.nordicsemi.android.log.Logger; +import no.nordicsemi.android.nrftoolbox.error.GattError; import no.nordicsemi.android.nrftoolbox.utility.DebugLogger; import no.nordicsemi.android.nrftoolbox.utility.ParserUtils; @@ -52,7 +52,7 @@ import no.nordicsemi.android.nrftoolbox.utility.ParserUtils; * leaving this to the developers. *

  • The manager tries to read the Battery Level characteristic. No matter the result of this operation (for example the Battery Level characteristic may not have the READ property) * it tries to enable Battery Level notifications, to get battery updates from the device.
  • - *
  • Afterwards, the manager initializes the device using given queue of commands. See {@link BleManagerGattCallback#initGatt(android.bluetooth.BluetoothGatt)} method for more details.
  • + *
  • Afterwards, the manager initializes the device using given queue of commands. See {@link BleManagerGattCallback#initGatt(BluetoothGatt)} method for more details.
  • *
  • When initialization complete, the {@link BleManagerCallbacks#onDeviceReady()} callback is called.
  • * The manager also is responsible for parsing the Battery Level values and calling {@link BleManagerCallbacks#onBatteryValueReceived(int)} method.

    *

    Events from all profiles are being logged into the nRF Logger application, @@ -830,7 +830,7 @@ public abstract class BleManager { final Queue requests = mInitQueue; // Get the first request from the queue - final Request request = requests.poll(); + final Request request = requests != null ? requests.poll() : null; // Are we done? if (request == null) { diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleManagerCallbacks.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleManagerCallbacks.java index c7c4b65e..107d85f3 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleManagerCallbacks.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleManagerCallbacks.java @@ -38,7 +38,7 @@ public interface BleManagerCallbacks { public void onDeviceDisconnecting(); /** - * Called when the device has disconnected (when the callback returned {@link BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)} with state DISCONNECTED. + * Called when the device has disconnected (when the callback returned {@link BluetoothGattCallback#onConnectionStateChange(BluetoothGatt, int, int)} with state DISCONNECTED. */ public void onDeviceDisconnected(); diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleProfileService.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleProfileService.java index c0253762..578ea16b 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleProfileService.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/profile/BleProfileService.java @@ -35,7 +35,6 @@ import android.widget.Toast; import no.nordicsemi.android.log.ILogSession; import no.nordicsemi.android.log.Logger; -import no.nordicsemi.android.nrftoolbox.R; public abstract class BleProfileService extends Service implements BleManagerCallbacks { @SuppressWarnings("unused") @@ -132,7 +131,7 @@ public abstract class BleProfileService extends Service implements BleManagerCal * * @return the log session */ - protected ILogSession getLogSession() { + public ILogSession getLogSession() { return mLogSession; } } @@ -278,6 +277,15 @@ public abstract class BleProfileService extends Service implements BleManagerCal LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast); } + /** + * This method should return false if the service needs to do some asynchronous work after if has disconnected from the device. + * In that case the {@link #stopService()} method must be called when done. + * @return true (default) to automatically stop the service when device is disconnected. False otherwise. + */ + protected boolean stopWhenDisconnected() { + return true; + } + @Override public void onDeviceDisconnected() { mConnected = false; @@ -288,6 +296,11 @@ public abstract class BleProfileService extends Service implements BleManagerCal broadcast.putExtra(EXTRA_CONNECTION_STATE, STATE_DISCONNECTED); LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast); + if (stopWhenDisconnected()) + stopService(); + } + + protected void stopService() { // user requested disconnection. We must stop the service Logger.v(mLogSession, "Stopping service..."); stopSelf(); @@ -335,7 +348,7 @@ public abstract class BleProfileService extends Service implements BleManagerCal @Override public void onBondingRequired() { - showToast(R.string.bonding); + showToast(no.nordicsemi.android.nrftoolbox.common.R.string.bonding); final Intent broadcast = new Intent(BROADCAST_BOND_STATE); broadcast.putExtra(EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDING); @@ -344,7 +357,7 @@ public abstract class BleProfileService extends Service implements BleManagerCal @Override public void onBonded() { - showToast(R.string.bonded); + showToast(no.nordicsemi.android.nrftoolbox.common.R.string.bonded); final Intent broadcast = new Intent(BROADCAST_BOND_STATE); broadcast.putExtra(EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDED); diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/proximity/ProximityManager.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/proximity/ProximityManager.java index 00f1b4c3..358607a4 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/proximity/ProximityManager.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/proximity/ProximityManager.java @@ -40,8 +40,8 @@ import java.util.Queue; import java.util.UUID; import no.nordicsemi.android.log.Logger; -import no.nordicsemi.android.nrftoolbox.parser.AlertLevelParser; import no.nordicsemi.android.nrftoolbox.profile.BleManager; +import no.nordicsemi.android.nrftoolbox.parser.AlertLevelParser; import no.nordicsemi.android.nrftoolbox.utility.DebugLogger; import no.nordicsemi.android.nrftoolbox.utility.ParserUtils; diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/proximity/ProximityService.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/proximity/ProximityService.java index a7a7e921..6504b130 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/proximity/ProximityService.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/proximity/ProximityService.java @@ -32,6 +32,7 @@ import android.media.AudioManager; import android.media.Ringtone; import android.media.RingtoneManager; import android.net.Uri; +import android.support.v4.app.NotificationManagerCompat; import android.support.v7.app.NotificationCompat; import no.nordicsemi.android.log.Logger; @@ -232,13 +233,13 @@ public class ProximityService extends BleProfileService implements ProximityMana builder.setContentIntent(pendingIntent); builder.setContentTitle(getString(R.string.app_name)).setContentText(getString(messageResId, getDeviceName())); builder.setSmallIcon(R.drawable.ic_stat_notify_proximity); - builder.setShowWhen(defaults != 0).setDefaults(defaults).setAutoCancel(true).setOngoing(true); + builder.setShowWhen(defaults != 0).setDefaults(defaults).setAutoCancel(true).setOngoing(defaults == 0); // an ongoing notification would not be shown on Android Wear builder.addAction(new NotificationCompat.Action(R.drawable.ic_action_bluetooth, getString(R.string.proximity_notification_action_disconnect), disconnectAction)); if (isConnected()) builder.addAction(new NotificationCompat.Action(R.drawable.ic_stat_notify_proximity, getString(isImmediateAlertOn ? R.string.proximity_action_silentme : R.string.proximity_action_findme), secondAction)); final Notification notification = builder.build(); - final NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + final NotificationManagerCompat nm = NotificationManagerCompat.from(this); nm.notify(NOTIFICATION_ID, notification); } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/rsc/RSCManager.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/rsc/RSCManager.java index 30e3c454..4c58586a 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/rsc/RSCManager.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/rsc/RSCManager.java @@ -32,8 +32,8 @@ import java.util.Queue; import java.util.UUID; import no.nordicsemi.android.log.Logger; -import no.nordicsemi.android.nrftoolbox.parser.RSCMeasurementParser; import no.nordicsemi.android.nrftoolbox.profile.BleManager; +import no.nordicsemi.android.nrftoolbox.parser.RSCMeasurementParser; public class RSCManager extends BleManager { private static final byte INSTANTANEOUS_STRIDE_LENGTH_PRESENT = 0x01; // 1 bit diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/TemplateManager.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/TemplateManager.java index 1c731c78..a8d4f76e 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/TemplateManager.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/template/TemplateManager.java @@ -31,8 +31,8 @@ import java.util.Queue; import java.util.UUID; import no.nordicsemi.android.log.Logger; -import no.nordicsemi.android.nrftoolbox.parser.TemplateParser; import no.nordicsemi.android.nrftoolbox.profile.BleManager; +import no.nordicsemi.android.nrftoolbox.parser.TemplateParser; /** * Modify to template manager to match your requirements. diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTActivity.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTActivity.java index db731f4e..2625c499 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTActivity.java +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/uart/UARTActivity.java @@ -37,6 +37,7 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; +import android.database.Cursor; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.TransitionDrawable; @@ -60,6 +61,8 @@ import android.widget.AdapterView; import android.widget.ListView; import android.widget.Toast; +import com.google.android.gms.common.api.GoogleApiClient; + import org.simpleframework.xml.Serializer; import org.simpleframework.xml.core.Persister; import org.simpleframework.xml.strategy.Strategy; @@ -84,28 +87,35 @@ import java.io.StringWriter; import java.util.UUID; import no.nordicsemi.android.nrftoolbox.R; -import no.nordicsemi.android.nrftoolbox.dfu.adapter.FileBrowserAppsAdapter; import no.nordicsemi.android.nrftoolbox.profile.BleProfileService; +import no.nordicsemi.android.nrftoolbox.dfu.adapter.FileBrowserAppsAdapter; import no.nordicsemi.android.nrftoolbox.profile.BleProfileServiceReadyActivity; import no.nordicsemi.android.nrftoolbox.uart.database.DatabaseHelper; import no.nordicsemi.android.nrftoolbox.uart.domain.Command; import no.nordicsemi.android.nrftoolbox.uart.domain.UartConfiguration; +import no.nordicsemi.android.nrftoolbox.uart.wearable.UARTConfigurationSynchronizer; import no.nordicsemi.android.nrftoolbox.utility.FileHelper; import no.nordicsemi.android.nrftoolbox.widget.ClosableSpinner; public class UARTActivity extends BleProfileServiceReadyActivity implements UARTInterface, - UARTNewConfigurationDialogFragment.NewConfigurationDialogListener, UARTConfigurationsAdapter.ActionListener, AdapterView.OnItemSelectedListener { + UARTNewConfigurationDialogFragment.NewConfigurationDialogListener, UARTConfigurationsAdapter.ActionListener, AdapterView.OnItemSelectedListener, + GoogleApiClient.ConnectionCallbacks { private final static String TAG = "UARTActivity"; private final static String PREFS_BUTTON_ENABLED = "prefs_uart_enabled_"; private final static String PREFS_BUTTON_COMMAND = "prefs_uart_command_"; private final static String PREFS_BUTTON_ICON = "prefs_uart_icon_"; + /** This preference keeps the ID of the selected configuration. */ private final static String PREFS_CONFIGURATION = "configuration_id"; + /** This preference is set to true when initial data synchronization for wearables has been completed. */ + private final static String PREFS_WEAR_SYNCED = "prefs_uart_synced"; private final static String SIS_EDIT_MODE = "sis_edit_mode"; private final static int SELECT_FILE_REQ = 2678; // random private final static int PERMISSION_REQ = 24; // random, 8-bit + UARTConfigurationSynchronizer mWearableSynchronizer; + /** The current configuration. */ private UartConfiguration mConfiguration; private DatabaseHelper mDatabaseHelper; @@ -162,7 +172,57 @@ public class UARTActivity extends BleProfileServiceReadyActivity= 0) + mWearableSynchronizer.onConfigurationDeleted(id); refreshConfigurations(); final Snackbar snackbar = Snackbar.make(mSlider, R.string.uart_configuration_deleted, Snackbar.LENGTH_INDEFINITE).setAction(R.string.uart_action_undo, new View.OnClickListener() { @Override public void onClick(final View v) { - mDatabaseHelper.restoreDeletedServerConfigurations(); + final long id = mDatabaseHelper.restoreDeletedServerConfiguration(name); + if (id >= 0) + mWearableSynchronizer.onConfigurationAddedOrEdited(id, removedConfiguration); refreshConfigurations(); } }); @@ -521,6 +586,7 @@ public class UARTActivity extends BleProfileServiceReadyActivity onConfigurationAddedOrEdited(final long id, final UartConfiguration configuration) { + if (mGoogleApiClient == null || !mGoogleApiClient.isConnected()) + return null; + + final PutDataMapRequest mapRequest = PutDataMapRequest.create(Constants.UART.CONFIGURATIONS + "/" + id); + final DataMap map = mapRequest.getDataMap(); + map.putString(Constants.UART.Configuration.NAME, configuration.getName()); + final ArrayList commands = new ArrayList<>(UartConfiguration.COMMANDS_COUNT); + for (Command command : configuration.getCommands()) { + if (command != null && command.isActive()) { + final DataMap item = new DataMap(); + item.putInt(Constants.UART.Configuration.Command.ICON_ID, command.getIconIndex()); + item.putString(Constants.UART.Configuration.Command.MESSAGE, command.getCommand()); + commands.add(item); + } + } + map.putDataMapArrayList(Constants.UART.Configuration.COMMANDS, commands); + final PutDataRequest request = mapRequest.asPutDataRequest(); + return Wearable.DataApi.putDataItem(mGoogleApiClient, request); + } + + /** + * Synchronizes the UART configurations between handheld and wearables. + * Call this when configuration has been deleted. + * @return pending result + */ + public PendingResult onConfigurationDeleted(final long id) { + if (mGoogleApiClient == null || !mGoogleApiClient.isConnected()) + return null; + return Wearable.DataApi.deleteDataItems(mGoogleApiClient, id2Uri(id)); + } + + /** + * Creates URI without nodeId. + * @param id the configuration id in the database + * @return Uri that may be used to delete the associated DataMap. + */ + private Uri id2Uri(final long id) { + return Uri.parse(WEAR_URI_PREFIX + Constants.UART.CONFIGURATIONS + "/" + id); + } +} diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/wearable/MainWearableListenerService.java b/app/src/main/java/no/nordicsemi/android/nrftoolbox/wearable/MainWearableListenerService.java new file mode 100644 index 00000000..a9d43335 --- /dev/null +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/wearable/MainWearableListenerService.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2015, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package no.nordicsemi.android.nrftoolbox.wearable; + +import android.content.Intent; + +import com.google.android.gms.wearable.MessageEvent; +import com.google.android.gms.wearable.WearableListenerService; + +import no.nordicsemi.android.nrftoolbox.wearable.common.Constants; +import no.nordicsemi.android.nrftoolbox.uart.UARTService; + +/** + * The main listener for messages from Wearable devices. There may be only one such service per application so it has to handle messages from all profiles. + */ +public class MainWearableListenerService extends WearableListenerService { + + @Override + public void onMessageReceived(final MessageEvent messageEvent) { + switch (messageEvent.getPath()) { + case Constants.ACTION_DISCONNECT: { + // A disconnect message was sent. The information which profile should be disconnected is in the data. + final String profile = new String(messageEvent.getData()); + + switch (profile) { + // Currently only UART profile has Wear support + case Constants.UART.PROFILE: { + final Intent disconnectIntent = new Intent(UARTService.ACTION_DISCONNECT); + disconnectIntent.putExtra(UARTService.EXTRA_SOURCE, UARTService.SOURCE_WEARABLE); + sendBroadcast(disconnectIntent); + break; + } + } + break; + } + case Constants.UART.COMMAND: { + final String command = new String(messageEvent.getData()); + + final Intent intent = new Intent(UARTService.ACTION_SEND); + intent.putExtra(UARTService.EXTRA_SOURCE, UARTService.SOURCE_WEARABLE); + intent.putExtra(Intent.EXTRA_TEXT, command); + sendBroadcast(intent); + } + default: + super.onMessageReceived(messageEvent); + break; + } + } +} diff --git a/app/src/main/res/drawable-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from app/src/main/res/drawable-hdpi/ic_launcher.png rename to app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/app/src/main/res/drawable-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from app/src/main/res/drawable-mdpi/ic_launcher.png rename to app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/app/src/main/res/drawable-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from app/src/main/res/drawable-xhdpi/ic_launcher.png rename to app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from app/src/main/res/drawable-xxhdpi/ic_launcher.png rename to app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cbee8212..55af6d9f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -59,10 +59,7 @@ BONDED DEVICES: AVAILABLE DEVICES: Since Android 6.0 Marshmallow system requires granting access to device\'s location in order to scan for Bluetooth Smart devices. Bluetooth beacons may be used to determine the phone\'s and user\'s location. - - Bonding with the device… - The device is now bonded. - + %1$tR:%1$tS.%1$tL Permission required diff --git a/app/src/main/res/values/strings_dfu.xml b/app/src/main/res/values/strings_dfu.xml index 7d355a8a..d619b440 100644 --- a/app/src/main/res/values/strings_dfu.xml +++ b/app/src/main/res/values/strings_dfu.xml @@ -21,103 +21,106 @@ ~ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --> - DFU - nRF Toolbox DFU Proxy - DFU Settings - - DEVICE FIRMWARE UPDATE - -186dp - - Example HEX files were copied to /sdcard/Nordic Semiconductor. - New example HEX files were copied to /sdcard/Nordic Semiconductor. - DFU script files were copied to /sdcard/Nordic Semiconductor. - - APPLICATION - File Name: - File Type: - File Size: - Status: - %d bytes - OK - OK (Init file selected) - File not loaded - Invalid file - Reading file failed - Please, select valid HEX file - Info - - SELECT FILE - - File Browser not found - File browser application has not been found on your device. Would you like to download one? - - Drive - File Manager - Total Commander - Search for others - - - market://details?id=com.google.android.apps.docs - market://details?id=com.rhmsoft.fm - market://details?id=com.ghisler.android.TotalCommander - market://search?q=file manager - - - DEVICE FIRMWARE UPDATE - UPLOAD - CANCEL - - DFU options - Packets receipt notification procedure - Number of packets - MBR size - MBR size (4096 on nRF51, 12288 on nRF52) + DFU + nRF Toolbox DFU Proxy + DFU Settings + + DEVICE FIRMWARE UPDATE + -186dp + + Example HEX files were copied to /sdcard/Nordic Semiconductor. + New example HEX files were copied to /sdcard/Nordic Semiconductor. + DFU script files were copied to /sdcard/Nordic Semiconductor. + + APPLICATION + File Name: + File Type: + File Size: + Status: + %d bytes + OK + OK (Init file selected) + File not loaded + Invalid file + Reading file failed + Please, select valid HEX file + Info + + SELECT FILE + + File Browser not found + File browser application has not been found on your device. Would you like to download one? + + Drive + File Manager + Total Commander + Search for others + + + market://details?id=com.google.android.apps.docs + market://details?id=com.rhmsoft.fm + market://details?id=com.ghisler.android.TotalCommander + market://search?q=file manager + + + DEVICE FIRMWARE UPDATE + UPLOAD + CANCEL + + DFU options + Packets receipt notification procedure + Number of packets + MBR size + MBR size (4096 on nRF51, 12288 on nRF52) Keep bond information - About DFU - DFU documentation on Nordic\'s Developer Zone - Information - During a DFU operation a lot of data packets are being sent to the target. The onCharacteristicWrite(...) + External MCU DFU + Check this switch to ON if you want to perform a DFU operation without jumping to the bootloader mode on a device + with a DFU implementation from SDK 6.1 or older. To achieve the same result on any newer implementation the DFU Version characteristic should return value other than 0x0100. + About DFU + DFU documentation on Nordic\'s Developer Zone + Information + During a DFU operation a lot of data packets are being sent to the target. The onCharacteristicWrite(...) callback in Android API is invoked when the data has been written to the outgoing queue, not when physically sent. Packet receipt notifications were introduced to prevent from overflowing the queue. Depending on the device model, disabling the notifications or setting the value to “high” (> ~300) may make the DFU process freeze at some point. - - Select file type - + + Select file type + Distribution packet (ZIP) Soft Device Bootloader Application - - Init packet - Do you want to select the Init packet file?\n + + Init packet + Do you want to select the Init packet file?\n The Init packet file (*.dat) should contain the device type and revision, application version, list of supported Soft Devices and the firmware CRC in binary format or, with old versions of the DFU bootloader, only the CRC (CRC-CCITT-16). With the new version of the bootloader the extended Init packet is required. - - unnamed device - 0% - %d%% - DEFAULT DFU - Application Uploading - Are you sure to cancel upload? - Application has been transferred successfully. - Uploading of the application has been canceled. - - Select file - A file browser application must be installed on the device before selecting the file. - \n\nThere are number of applications available on Google Play store, e.g. Total Commander or File Manager, that allow you to pick a file from internal memory of the device. To upload - a file from the Internet you may use f.e. Drive or Dropbox application. - \n\nYou will be asked to select an application if more than one is installed. A single application will be launched automatically. - \n\nSince Android KitKat you may use the preinstalled document picker application. Ensure Display advanced devices option is enabled in settings to use the Internal storage. - \n\nSample applications were copied to Nordic Semiconductor folder in the internal storage. - - Starting from nRF Toolbox v1.12 the new Distribution packet (ZIP) is the recommended method for distributing firmware upgrades. + + unnamed device + 0% + %d%% + DEFAULT DFU + Application Uploading + Are you sure to cancel upload? + Application has been transferred successfully. + Uploading of the application has been canceled. + + Select file + A file browser application must be installed on the device before selecting the file. + \n\nThere are number of applications available on Google Play store, e.g. Total Commander or File Manager, that allow you to pick a file from internal memory of the device. To upload + a file from the Internet you may use f.e. Drive or Dropbox application. + \n\nYou will be asked to select an application if more than one is installed. A single application will be launched automatically. + \n\nSince Android KitKat you may use the preinstalled document picker application. Ensure Display advanced devices option is enabled in settings to use the Internal storage. + \n\nSample applications were copied to Nordic Semiconductor folder in the internal storage. + + Starting from nRF Toolbox v1.12 the new Distribution packet (ZIP) is the recommended method for distributing firmware upgrades. You can create the ZIP file using the nrf utility tool, which is part of Master Control Panel 3.8.0+. For more detailed information, see the DFU documentation. \n\nBackward compatibility \nThe nRF Toolbox also supports all old file formats: HEX and BIN files, separate DAT files and ZIP files without a manifest file but with a fixed naming convention: - The Device Firmware Update (DFU) app allows you to update the firmware of your Bluetooth Smart device over-the-air (OTA). + The Device Firmware Update (DFU) app allows you to update the firmware of your Bluetooth Smart device over-the-air (OTA). It is compatible with Nordic Semiconductor nRF51822 or nRF51422 devices with S110 SoftDevice and DFU bootloader enabled. With SoftDevice s110 7.0.0+, the SoftDevice itself and/or a bootloader may also be updated. - \n\nFor more information about the DFU, see the About DFU section in Settings. + \n\nFor more information about the DFU, see the About DFU section in Settings. diff --git a/app/src/main/res/values/strings_uart.xml b/app/src/main/res/values/strings_uart.xml index d4fac1a6..843e4273 100644 --- a/app/src/main/res/values/strings_uart.xml +++ b/app/src/main/res/values/strings_uart.xml @@ -43,7 +43,7 @@ No data to display. Rename configuration - New GATT configuration + New configuration Configuration name Please, provide a unique configuration name: Name must not be empty. diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 075ae4f0..f5c30279 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -120,6 +120,7 @@ @color/actionBarColorDark @color/actionBarColorDark @style/Widget.Button + @style/Widget.AppCompat.Spinner