diff --git a/.gitignore b/.gitignore index 937f8525..475319fc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,100 @@ ShimmerAndroidInstrumentDriver/build/generated/mockable-android-19.jar ShimmerAndroidInstrumentDriver/build/generated/mockable-android-21.jar ShimmerAndroidInstrumentDriver/gradle.properties + +# Built application files +*.apk +*.aar +*.ap_ +*.aab + +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ +out/ +# Uncomment the following line in case you need and you don't have the release build type files in your app +# release/ + +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation/ + +# Android Studio captures folder +captures/ + +# IntelliJ +*.iml +.idea/ +misc.xml +deploymentTargetDropDown.xml +render.experimental.xml +.idea/workspace.xml +.idea/tasks.xml +.idea/gradle.xml +.idea/assetWizardSettings.xml +.idea/dictionaries +.idea/libraries +.idea/jarRepositories.xml +# Android Studio 3 in .gitignore file. +.idea/caches +.idea/modules.xml +# Comment next line if keeping position of elements in Navigation Editor is relevant for you +.idea/navEditor.xml + +# Keystore files +# Uncomment the following lines if you do not want to check your keystore files in. +#*.jks +#*.keystore + +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild +.cxx/ + +# Google Services (e.g. APIs or Firebase) +# google-services.json + +# Freeline +freeline.py +freeline/ +freeline_project_description.json + +# fastlane +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output +fastlane/readme.md + +# Version control +vcs.xml + +# lint +lint/intermediates/ +lint/generated/ +lint/outputs/ +lint/tmp/ +# lint/reports/ + +# Android Profiling +*.hprof gradle.properties +ShimmerAndroidInstrumentDriver/.idea/jarRepositories.xml +ShimmerAndroidInstrumentDriver/.idea/gradle.xml diff --git a/ShimmerAndroidInstrumentDriver/.gitignore b/ShimmerAndroidInstrumentDriver/.gitignore index ecc2fdb4..cecbba34 100644 --- a/ShimmerAndroidInstrumentDriver/.gitignore +++ b/ShimmerAndroidInstrumentDriver/.gitignore @@ -1,38 +1,93 @@ # Built application files -/**/build/ +*.apk +*.aar +*.ap_ +*.aab -# Crashlytics configuations -com_crashlytics_export_strings.xml +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ +out/ +# Uncomment the following line in case you need and you don't have the release build type files in your app +# release/ + +# Gradle files +.gradle/ +build/ # Local configuration file (sdk path, etc) local.properties -# Gradle generated files -.gradle/ +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log -# Signing files -.signing/ +# Android Studio Navigation editor temp files +.navigation/ -# User-specific configurations -.idea/libraries/ +# Android Studio captures folder +captures/ + +# IntelliJ +*.iml +.idea/ +misc.xml +deploymentTargetDropDown.xml +render.experimental.xml .idea/workspace.xml .idea/tasks.xml -.idea/.name -.idea/compiler.xml -.idea/copyright/profiles_settings.xml -.idea/encodings.xml -.idea/misc.xml +.idea/gradle.xml +.idea/assetWizardSettings.xml +.idea/dictionaries +.idea/libraries +.idea/jarRepositories.xml +# Android Studio 3 in .gitignore file. +.idea/caches .idea/modules.xml -.idea/scopes/scope_settings.xml -.idea/vcs.xml -*.iml +# Comment next line if keeping position of elements in Navigation Editor is relevant for you +.idea/navEditor.xml + +# Keystore files +# Uncomment the following lines if you do not want to check your keystore files in. +#*.jks +#*.keystore + +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild +.cxx/ + +# Google Services (e.g. APIs or Firebase) +# google-services.json + +# Freeline +freeline.py +freeline/ +freeline_project_description.json + +# fastlane +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output +fastlane/readme.md + +# Version control +vcs.xml + +# lint +lint/intermediates/ +lint/generated/ +lint/outputs/ +lint/tmp/ +# lint/reports/ -# OS-specific files -.DS_Store -.DS_Store? -._* -.Spotlight-V100 -.Trashes -ehthumbs.db -Thumbs.db -/ShimmerAndroidInstrumentDriver/gradle.properties +# Android Profiling +*.hprof \ No newline at end of file diff --git a/ShimmerAndroidInstrumentDriver/.idea/codeStyles/Project.xml b/ShimmerAndroidInstrumentDriver/.idea/codeStyles/Project.xml deleted file mode 100644 index 30aa626c..00000000 --- a/ShimmerAndroidInstrumentDriver/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/ShimmerAndroidInstrumentDriver/.idea/gradle.xml b/ShimmerAndroidInstrumentDriver/.idea/gradle.xml deleted file mode 100644 index 327e3464..00000000 --- a/ShimmerAndroidInstrumentDriver/.idea/gradle.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/ShimmerAndroidInstrumentDriver/.idea/runConfigurations.xml b/ShimmerAndroidInstrumentDriver/.idea/runConfigurations.xml deleted file mode 100644 index 7f68460d..00000000 --- a/ShimmerAndroidInstrumentDriver/.idea/runConfigurations.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/.gitignore b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/.gitignore index a307a61d..c2f04fbc 100644 --- a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/.gitignore +++ b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/.gitignore @@ -1,37 +1,90 @@ # Built application files -/*/build/ +*.apk +*.aar +*.ap_ +*.aab -# Crashlytics configuations -com_crashlytics_export_strings.xml +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ +out/ +# Uncomment the following line in case you need and you don't have the release build type files in your app +# release/ + +# Gradle files +.gradle/ +build/ # Local configuration file (sdk path, etc) local.properties -# Gradle generated files -.gradle/ +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log -# Signing files -.signing/ +# Android Studio Navigation editor temp files +.navigation/ -# User-specific configurations -.idea/libraries/ +# Android Studio captures folder +captures/ + +# IntelliJ +*.iml .idea/workspace.xml .idea/tasks.xml -.idea/.name -.idea/compiler.xml -.idea/copyright/profiles_settings.xml -.idea/encodings.xml -.idea/misc.xml +.idea/gradle.xml +.idea/assetWizardSettings.xml +.idea/dictionaries +.idea/libraries +.idea/jarRepositories.xml +# Android Studio 3 in .gitignore file. +.idea/caches .idea/modules.xml -.idea/scopes/scope_settings.xml -.idea/vcs.xml -*.iml +# Comment next line if keeping position of elements in Navigation Editor is relevant for you +.idea/navEditor.xml + +# Keystore files +# Uncomment the following lines if you do not want to check your keystore files in. +#*.jks +#*.keystore + +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild +.cxx/ + +# Google Services (e.g. APIs or Firebase) +# google-services.json + +# Freeline +freeline.py +freeline/ +freeline_project_description.json + +# fastlane +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output +fastlane/readme.md + +# Version control +vcs.xml + +# lint +lint/intermediates/ +lint/generated/ +lint/outputs/ +lint/tmp/ +# lint/reports/ -# OS-specific files -.DS_Store -.DS_Store? -._* -.Spotlight-V100 -.Trashes -ehthumbs.db -Thumbs.db \ No newline at end of file +# Android Profiling +*.hprof +/gradle.properties diff --git a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/build.gradle b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/build.gradle index b70f3425..f3dcbd5e 100644 --- a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/build.gradle +++ b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/build.gradle @@ -2,13 +2,10 @@ apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' -//apply plugin: 'com.jfrog.artifactory' apply plugin: 'maven-publish' -version = '3.0.73Beta' - android { - compileSdkVersion 25 + compileSdkVersion 33 lintOptions { abortOnError false @@ -18,7 +15,7 @@ android { // Enabling multidex support. multiDexEnabled true minSdkVersion 14 - targetSdkVersion 14 + targetSdkVersion 33 } buildTypes { @@ -75,12 +72,13 @@ publishing { def getArtificatId = { -> return "ShimmerAndroidInstrumentDriver" // Replace with library name ID } +/* publishing { publications { bar(MavenPublication) { groupId = 'com.shimmerresearch' // Replace with your package's group/organization name artifactId = 'shimmerandroidinstrumentdriver' // Replace with the name of your package - version = '3.1.0_alpha' // Replace with your package version + version = '3.0.83_beta' // Replace with your package version artifact("$buildDir/outputs/aar/${getArtificatId()}-release.aar") } } @@ -90,31 +88,20 @@ publishing { name = "GitHubPackages" url = uri("https://maven.pkg.github.com/ShimmerEngineering/ShimmerAndroidAPI") credentials { - /**Create github.properties in root project folder file with gpra.usr=GITHUB_USER_ID & gpra.key=PERSONAL_ACCESS_TOKEN**/ - username = githubProperties['gpra.usr'] ?: System.getenv("GPRA_USER") - password = githubProperties['gpra.key'] ?: System.getenv("GPRA_API_KEY") + //Create github.properties in root project folder file with gpra.usr=GITHUB_USER_ID & gpra.key=PERSONAL_ACCESS_TOKEN + username = project.findProperty('gpra.usr') + password = project.findProperty('gpra.key') } } } } - +*/ dependencies { compile 'com.google.guava:guava:20.0' compile 'java3d:vecmath:1.3.1' compile files('libs/ShimmerBiophysicalProcessingLibrary_Rev_0_11.jar') compile files('libs/AndroidBluetoothLibrary.jar') compile files('libs/androidplot-core-0.5.0-release.jar') - implementation (group: 'com.shimmerresearch', name: 'shimmerbluetoothmanager', version:'0.10.0_alpha'){ - // excluding org.json which is provided by Android - exclude group: 'io.netty' - exclude group: 'com.google.protobuf' - exclude group: 'org.apache.commons.math' - } - implementation (group: 'com.shimmerresearch', name: 'shimmerdriver', version:'0.10.0_alpha'){ - // excluding org.json which is provided by Android - exclude group: 'io.netty' - exclude group: 'com.google.protobuf' - exclude group: 'org.apache.commons.math' - } - compile 'com.android.support:appcompat-v7:25.3.1' + compile 'com.android.support:appcompat-v7:26.1.0' + compile 'com.clj.fastble:FastBleLib:2.3.4' } diff --git a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/Shimmer.java b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/Shimmer.java index 7d8c3e68..e883ffe0 100644 --- a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/Shimmer.java +++ b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/Shimmer.java @@ -132,15 +132,22 @@ package com.shimmerresearch.android; +import static android.content.Context.BLUETOOTH_SERVICE; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothSocket; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; +import com.shimmerresearch.androidinstrumentdriver.R; import com.shimmerresearch.bluetooth.BluetoothProgressReportPerCmd; import com.shimmerresearch.bluetooth.ShimmerBluetooth; import com.shimmerresearch.driver.CallbackObject; @@ -151,9 +158,14 @@ import com.shimmerresearch.driver.ShimmerMsg; import com.shimmerresearch.driver.shimmer2r3.ConfigByteLayoutShimmer3; import com.shimmerresearch.driver.shimmer4sdk.Shimmer4sdk; +import com.shimmerresearch.driverUtilities.ShimmerBattStatusDetails; import com.shimmerresearch.driverUtilities.ShimmerVerDetails; +import com.shimmerresearch.driverUtilities.UtilShimmer; +import com.shimmerresearch.exceptions.ShimmerException; import com.shimmerresearch.exgConfig.ExGConfigOptionDetails; +import org.apache.commons.lang3.ArrayUtils; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; @@ -221,6 +233,7 @@ public class Shimmer extends ShimmerBluetooth{ public static final int MSG_STATE_STREAMING = 4; public static final int MSG_STATE_STOP_STREAMING = 5; + transient private Context mContext; protected String mClassName="Shimmer"; private int mBluetoothLib=0; // 0 = default lib, 1 = arduino lib @@ -236,11 +249,37 @@ public class Shimmer extends ShimmerBluetooth{ setUseInfoMemConfigMethod(true); } + protected void unregisterDisconnectListener(){ + if(mContext!=null) { + try { + mContext.unregisterReceiver(mReceiver); + } catch (Exception ex){ + System.out.println(ex); + } + } + } + + protected void registerDisconnectListener(){ + if(mContext!=null) { + System.out.println("initialize process 0) register disconnect listener"); + BluetoothAdapter bluetoothAdapter = null; + if (android.os.Build.VERSION.SDK_INT >= 18) { + BluetoothManager bluetoothManager = (BluetoothManager) mContext.getSystemService(BLUETOOTH_SERVICE); + bluetoothAdapter = bluetoothManager.getAdapter(); + } else bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + + IntentFilter filter = new IntentFilter(bluetoothAdapter.ACTION_STATE_CHANGED); + filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); + filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); + mContext.registerReceiver(mReceiver, filter); + } + } + /** * This constructor is for applications that only require one Handler. * @param handler add handler to receive msgs from the shimmer class */ - public Shimmer(Handler handler) { + public Shimmer(Handler handler, Context context) { super(); mAdapter = BluetoothAdapter.getDefaultAdapter(); mBluetoothRadioState = BT_STATE.DISCONNECTED; @@ -248,20 +287,40 @@ public Shimmer(Handler handler) { // mContinousSync=continousSync; mSetupDeviceWhileConnecting=false; mUseProcessingThread = true; + mContext = context; } + // The BroadcastReceiver that listens for discovered devices and + // changes the title when discovery is finished + transient private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + // When discovery finds a device + if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) { + BluetoothDevice device = intent + .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + + String macAdd = device.getAddress(); + if (macAdd.equals(mMyBluetoothAddress)){ + connectionLost(); + } + } + } + }; /** * This constructor is for applications requiring more than one Handler so as to receive the msg * in multiple threads. * @param handlerList this is an ArrayList containing multiple Handlers */ - public Shimmer(ArrayList handlerList) { + public Shimmer(ArrayList handlerList, Context context) { super(); mAdapter = BluetoothAdapter.getDefaultAdapter(); mBluetoothRadioState = BT_STATE.DISCONNECTED; mHandlerList = handlerList; mSetupDeviceWhileConnecting = false; mUseProcessingThread = true; + mContext = context; } /** @@ -436,14 +495,16 @@ public Shimmer(Context context, Handler handler, String myName, double samplingR * @param magRange * @param orientation * @param pressureResolution + * @param context */ - public Shimmer(Handler handler, String userAssignedName, double samplingRate, int accelRange, int gsrRange, Integer[] sensorIdsToEnable, int gyroRange, int magRange, int orientation, int pressureResolution){ + public Shimmer(Handler handler, String userAssignedName, double samplingRate, int accelRange, int gsrRange, Integer[] sensorIdsToEnable, int gyroRange, int magRange, int orientation, int pressureResolution, Context context){ super(userAssignedName, samplingRate, sensorIdsToEnable, accelRange, gsrRange, gyroRange, magRange, pressureResolution); mAdapter = BluetoothAdapter.getDefaultAdapter(); mBluetoothRadioState = BT_STATE.DISCONNECTED; mHandlerList.add(handler); setupOrientation(orientation, samplingRate); mUseProcessingThread = true; + mContext = context; } /** Shimmer2R Constructor @@ -455,7 +516,7 @@ public Shimmer(Handler handler, String userAssignedName, double samplingRate, in * @param magGain * @param orientation */ - public Shimmer(Handler handler, String myName, double samplingRate, int accelRange, int gsrRange, int setEnabledSensors, int magGain, int orientation) { + public Shimmer(Handler handler, String myName, double samplingRate, int accelRange, int gsrRange, int setEnabledSensors, int magGain, int orientation, Context context) { super(myName,samplingRate, setEnabledSensors, accelRange, gsrRange, magGain); setupOrientation(orientation, samplingRate); mAdapter = BluetoothAdapter.getDefaultAdapter(); @@ -463,6 +524,7 @@ public Shimmer(Handler handler, String myName, double samplingRate, int accelRan mHandlerList.add(handler); setupOrientation(orientation, samplingRate); mUseProcessingThread = true; + mContext = context; } /** @@ -479,7 +541,7 @@ public Shimmer(Handler handler, String myName, double samplingRate, int accelRan * @param pressureResolution * @param enableCalibration */ - public Shimmer(Handler handler, String userAssignedName, double samplingRate, int accelRange, int gsrRange, Integer[] sensorIdsToEnable, int gyroRange, int magRange, int orientation, int pressureResolution, boolean enableCalibration){ + public Shimmer(Handler handler, String userAssignedName, double samplingRate, int accelRange, int gsrRange, Integer[] sensorIdsToEnable, int gyroRange, int magRange, int orientation, int pressureResolution, boolean enableCalibration, Context context){ super(userAssignedName, samplingRate, sensorIdsToEnable, accelRange, gsrRange, gyroRange, magRange, pressureResolution); mAdapter = BluetoothAdapter.getDefaultAdapter(); mBluetoothRadioState = BT_STATE.DISCONNECTED; @@ -487,6 +549,7 @@ public Shimmer(Handler handler, String userAssignedName, double samplingRate, in setupOrientation(orientation, samplingRate); setEnableCalibration(enableCalibration); mUseProcessingThread = true; + mContext = context; } /** @@ -501,7 +564,7 @@ public Shimmer(Handler handler, String userAssignedName, double samplingRate, in * @param orientation * @param enableCalibration */ - public Shimmer(Handler handler, String myName, double samplingRate, int accelRange, int gsrRange, int setEnabledSensors, int magGain, int orientation, boolean enableCalibration) { + public Shimmer(Handler handler, String myName, double samplingRate, int accelRange, int gsrRange, int setEnabledSensors, int magGain, int orientation, boolean enableCalibration, Context context) { super(myName,samplingRate, setEnabledSensors, accelRange, gsrRange, magGain); setupOrientation(orientation, samplingRate); mAdapter = BluetoothAdapter.getDefaultAdapter(); @@ -510,9 +573,9 @@ public Shimmer(Handler handler, String myName, double samplingRate, int accelRan setupOrientation(orientation, samplingRate); setEnableCalibration(enableCalibration); mUseProcessingThread = true; + mContext = context; } - /** * Set the current state of the chat connection * @param state An integer defining the current connection state @@ -535,11 +598,11 @@ public Shimmer(Handler handler, String myName, double samplingRate, int accelRan * @param bluetoothLibrary Supported libraries are 'default' and 'gerdavax' */ public synchronized void connect(final String address, String bluetoothLibrary) { + registerDisconnectListener(); mIamAlive = false; getListofInstructions().clear(); mFirstTime=true; - if (bluetoothLibrary=="default"){ mMyBluetoothAddress=address; mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); @@ -592,6 +655,7 @@ public synchronized void ready() { * @param socket The BluetoothSocket on which the connection was made */ public synchronized void connected(BluetoothSocket socket) { + System.out.println("initialize process 2) connected and start initialize"); // Cancel the thread that completed the connection if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;} // Cancel any thread currently running a connection @@ -599,9 +663,11 @@ public synchronized void connected(BluetoothSocket socket) { // Start the thread to manage the connection and perform transmissions mConnectedThread = new ConnectedThread(socket); mIOThread = new IOThread(); + mIOThread.setName("IO Thread " + socket.getRemoteDevice().getAddress()); mIOThread.start(); if (mUseProcessingThread){ mPThread = new ProcessingThread(); + mPThread.setName("P Thread " + socket.getRemoteDevice().getAddress()); mPThread.start(); } @@ -618,7 +684,7 @@ public synchronized void connected(BluetoothSocket socket) { /** * Stop all threads */ - public synchronized void stop() { + public void stop() { if (mTimerReadStatus!=null) { mTimerReadStatus.cancel(); mTimerReadStatus.purge(); @@ -651,15 +717,267 @@ public synchronized void stop() { mConnectThread = null; } if (mConnectedThread != null) { + mConnectedThread.cancel(); + mConnectedThread = null; + } + + } + + @Override + protected void clearSingleDataPacketFromBuffers(byte[] bufferTemp, int packetSize) { + byte[] fullBuffer = mByteArrayOutputStream.toByteArray(); + byte[] keepBuffer = new byte[fullBuffer.length-packetSize]; + System.arraycopy(fullBuffer,packetSize,keepBuffer,0,keepBuffer.length); + this.mByteArrayOutputStream.reset(); + try { + this.mByteArrayOutputStream.write(keepBuffer); + } catch (IOException e) { + throw new RuntimeException(e); + } + if (this.mEnablePCTimeStamps) { + for(int i = 0; i < packetSize; ++i) { + try { + this.mListofPCTimeStamps.remove(0); + } catch (Exception var5) { + this.consolePrintException(var5.getMessage(), var5.getStackTrace()); + } + } + } + + } + + @Override + protected void processPacket() { + setIamAlive(true); + byte[] allBytes = mByteArrayOutputStream.toByteArray(); + byte[] bufferTemp = new byte[getPacketSizeWithCrc()+2]; + System.arraycopy(allBytes,0,bufferTemp,0,bufferTemp.length); + //Data packet followed by another data packet + if(bufferTemp[0]==DATA_PACKET + && bufferTemp[getPacketSizeWithCrc()+1]==DATA_PACKET){ + + if (mBtCommsCrcModeCurrent != BT_CRC_MODE.OFF && !checkCrc(bufferTemp, getPacketSize() + 1)) { + discardFirstBufferByte(); + return; + } + + //Handle the data packet + processDataPacket(bufferTemp); + clearSingleDataPacketFromBuffers(bufferTemp, getPacketSizeWithCrc()+1); + } + + //Data packet followed by an ACK (suggesting an ACK in response to a SET BT command or else a BT response command) + else if(bufferTemp[0]==DATA_PACKET + && bufferTemp[getPacketSizeWithCrc()+1]==ACK_COMMAND_PROCESSED){ + + if(mByteArrayOutputStream.size()>getPacketSizeWithCrc()+2){ + allBytes = mByteArrayOutputStream.toByteArray(); + bufferTemp = new byte[getPacketSizeWithCrc()+3]; //check if the next byte is data packet + System.arraycopy(allBytes,0,bufferTemp,0,bufferTemp.length); + if(bufferTemp[getPacketSizeWithCrc()+2]==DATA_PACKET){ + //Firstly handle the data packet + processDataPacket(bufferTemp); + clearSingleDataPacketFromBuffers(bufferTemp, getPacketSizeWithCrc()+2); //clear an extra byte which is the ack + + //Then handle the ACK from the last SET command + if(isKnownSetCommand(mCurrentCommand)){ + stopTimerCheckForAckOrResp(); //cancel the ack timer + mWaitForAck=false; + + processAckFromSetCommand(mCurrentCommand); + + mTransactionCompleted = true; + setInstructionStackLock(false); + } + printLogDataForDebugging("Ack Received for Command: \t\t\t" + btCommandToString(mCurrentCommand)); + } + + //this is for LogAndStream support, command is transmitted and ack received + else if(isSupportedInStreamCmds() && bufferTemp[getPacketSizeWithCrc()+2]==INSTREAM_CMD_RESPONSE){ + printLogDataForDebugging("COMMAND TXed and ACK RECEIVED IN STREAM"); + printLogDataForDebugging("INS CMD RESP"); + + //Firstly handle the in-stream response + stopTimerCheckForAckOrResp(); //cancel the ack timer + mWaitForResponse=false; + mWaitForAck=false; + + processInstreamResponse(); + + // Need to remove here because it is an + // in-stream response while streaming so not + // handled elsewhere + if(getListofInstructions().size()>0){ + removeInstruction(0); + } + + mTransactionCompleted=true; + setInstructionStackLock(false); + + //Then process the Data packet + //processDataPacket(bufferTemp); + //clearBuffers(); + } + else { + printLogDataForDebugging("Unknown parsing error while streaming"); + discardFirstBufferByte(); //throw the first byte away + } + } + /* + if(mByteArrayOutputStream.size()>getPacketSizeWithCrc()+2){ + printLogDataForDebugging("Unknown packet error (check with JC):\tExpected: " + (getPacketSizeWithCrc()+2) + "bytes but buffer contains " + mByteArrayOutputStream.size() + "bytes"); + discardFirstBufferByte(); //throw the first byte away + } + */ + + } + //TODO: ACK in bufferTemp[0] not handled + //else if + else { + printLogDataForDebugging("Packet syncing problem:\tExpected: " + (getPacketSizeWithCrc()+2) + "bytes. Buffer contains " + mByteArrayOutputStream.size() + "bytes" + + "\nBuffer = " + UtilShimmer.bytesToHexStringWithSpacesFormatted(mByteArrayOutputStream.toByteArray())); + discardFirstBufferByte(); //throw the first byte away + } + } + + protected byte[] getDataFromArrayOutputStream(int extraBytesLength){ + if (mByteArrayOutputStream.size() >= getPacketSizeWithCrc() + extraBytesLength) { + byte[] allBytes = mByteArrayOutputStream.toByteArray(); + byte[] bufferTemp = new byte[getPacketSizeWithCrc() + extraBytesLength]; //check if the next byte is data packet + System.arraycopy(allBytes, 0, bufferTemp, 0, bufferTemp.length); + return bufferTemp; + } + return null; + } + + /** process responses to in-stream response */ + @Override + protected void processInstreamResponse() { + + if (mBluetoothRadioState.equals(BT_STATE.CONNECTED)){ + super.processInstreamResponse(); + } else if (mBluetoothRadioState.equals(BT_STATE.STREAMING)){ + //byte[] inStreamResponseCommandBuffer = readBytes(1, INSTREAM_CMD_RESPONSE); + byte[] bufferTemp = getDataFromArrayOutputStream(4); + if (bufferTemp != null) { + byte inStreamResponseCommand = bufferTemp[bufferTemp.length - 1]; + consolePrintLn("In-stream received = " + btCommandToString(inStreamResponseCommand)); + + if (inStreamResponseCommand == DIR_RESPONSE) { + //byte[] responseData = readBytes(1, inStreamResponseCommand); + bufferTemp = getDataFromArrayOutputStream(5); + if (bufferTemp != null) { + int directoryNameLength = bufferTemp[bufferTemp.length - 1]; + byte[] bufferDirectoryName = new byte[directoryNameLength]; + bufferTemp = getDataFromArrayOutputStream(5 + directoryNameLength); + System.arraycopy(bufferTemp, bufferTemp.length - bufferDirectoryName.length, bufferDirectoryName, 0, bufferDirectoryName.length); + if (bufferDirectoryName != null) { + String tempDirectory = new String(bufferDirectoryName); + mDirectoryName = tempDirectory; + printLogDataForDebugging("Directory Name = " + mDirectoryName); + } + processDataPacket(bufferTemp); + clearSingleDataPacketFromBuffers(bufferTemp, bufferTemp.length + mBtCommsCrcModeCurrent.getNumCrcBytes()); + } + } else if (inStreamResponseCommand == STATUS_RESPONSE) { + bufferTemp = getDataFromArrayOutputStream(5); + if (bufferTemp != null) { + byte[] responseData = new byte[1]; + System.arraycopy(bufferTemp, bufferTemp.length - responseData.length, responseData, 0, responseData.length); + if (responseData != null) { + parseStatusByte(responseData[0]); + + if (!isSupportedRtcStateInStatus()) { + if (!mIsSensing && !isInitialised()) { + writeRealTimeClock(); + } + } else { + //New case to make sure RTC is set if it hasn't been already + if (!isSDLogging() && (!isInitialised() || !mIsRtcSet)) { + writeRealTimeClock(); + } + } + eventLogAndStreamStatusChanged(mCurrentCommand); + processDataPacket(bufferTemp); + clearSingleDataPacketFromBuffers(bufferTemp, bufferTemp.length + mBtCommsCrcModeCurrent.getNumCrcBytes()); + } + } + } else if (inStreamResponseCommand == VBATT_RESPONSE) { + bufferTemp = getDataFromArrayOutputStream(7); + if (bufferTemp != null) { + byte[] responseData = new byte[3]; + System.arraycopy(bufferTemp, bufferTemp.length - responseData.length, responseData, 0, responseData.length); + if (responseData != null) { + ShimmerBattStatusDetails battStatusDetails = new ShimmerBattStatusDetails(((responseData[1] & 0xFF) << 8) + (responseData[0] & 0xFF), responseData[2]); + setBattStatusDetails(battStatusDetails); + printLogDataForDebugging("Battery Status:" + + "\tVoltage=" + battStatusDetails.getBattVoltageParsed() + + "\tCharging status=" + battStatusDetails.getChargingStatusParsed() + + "\tBatt %=" + battStatusDetails.getEstimatedChargePercentageParsed()); + } + processDataPacket(bufferTemp); + clearSingleDataPacketFromBuffers(bufferTemp, bufferTemp.length + mBtCommsCrcModeCurrent.getNumCrcBytes()); + } + + } else { + discardFirstBufferByte(); + } + } + } + } + + + @Override + protected void processWhileStreaming() { + byte[] byteBuffer = readBytes(availableBytes()); + if(byteBuffer!=null){ try { - wait(200); - } catch (InterruptedException e) { - e.printStackTrace(); + mByteArrayOutputStream.write(byteBuffer); + } catch (IOException e) { + throw new RuntimeException(e); } - mConnectedThread.cancel(); - mConnectedThread = null; + //Everytime a byte is received the timestamp is taken + if(mEnablePCTimeStamps) { + for (int index:byteBuffer) { + mListofPCTimeStamps.add(System.currentTimeMillis()); + } + } + } + else { + printLogDataForDebugging("readbyte null"); } + //If there is a full packet and the subsequent sequence number of following packet + if(mByteArrayOutputStream.size()>=getPacketSizeWithCrc()+2){ // +2 because there are two acks + processPacket(); + } + } + + /**this is to clear the buffer + * + */ + @Override + protected void clearSerialBuffer() { + startTimerCheckForSerialPortClear(); + byte[] buffer = new byte[0]; + while (availableBytes() != 0) { +// int available = availableBytes(); + if (bytesAvailableToBeRead()) { + //AA-283 : the clearing of the serial bytes , is too slow when streaming 1024Hz (e.g. exg test) + buffer = readBytes(availableBytes()); + + if (mSerialPortReadTimeout) { + break; + } + } + } + + if (buffer.length > 0) { + String msg = "Clearing Buffer:\t\t" + UtilShimmer.bytesToHexStringWithSpacesFormatted(buffer); + printLogDataForDebugging(msg); + } + + stopTimerCheckForSerialPortClear(); } /** @@ -668,6 +986,7 @@ public synchronized void stop() { * @see ConnectedThread write(byte[]) */ public void write(byte[] out) { + /* // Create temporary object ConnectedThread r; // Synchronize a copy of the ConnectedThread @@ -678,6 +997,8 @@ public void write(byte[] out) { } // Perform the write unsynchronized r.write(out); + */ + mConnectedThread.write(out); } /** @@ -746,6 +1067,7 @@ public ConnectThread(BluetoothDevice device) { } public void run() { + System.out.println("initialize process 1) start connecting thread"); setName("ConnectThread"); // Always cancel discovery because it will slow down a connection @@ -989,7 +1311,7 @@ public void cancel() { - + @Override protected void inquiryDone() { //TODO: Delete this... // Message msg = mHandler.obtainMessage(MESSAGE_TOAST); @@ -1001,6 +1323,7 @@ protected void inquiryDone() { isReadyForStreaming(); } + @Override protected void isReadyForStreaming(){ //TODO: Delete this... // Message msg = mHandler.obtainMessage(MESSAGE_TOAST); @@ -1030,7 +1353,12 @@ protected void isReadyForStreaming(){ //mHandler.obtainMessage(ShimmerBluetooth.MSG_IDENTIFIER_STATE_CHANGE, -1, -1, new ObjectCluster(mShimmerUserAssignedName,getBluetoothAddress(),mBluetoothRadioState)).sendToTarget(); Log.d(mClassName,"Shimmer " + mMyBluetoothAddress +" Initialization completed and is ready for Streaming"); if(mAutoStartStreaming){ - startStreaming(); + try { + startStreaming(); + } catch (ShimmerException e) { + connectionLost(); + e.printStackTrace(); + } } } @@ -1345,6 +1673,7 @@ else if((mBluetoothRadioState==BT_STATE.DISCONNECTED) mIsConnected = false; mIsStreaming = false; mIsInitialised = false; + unregisterDisconnectListener(); } // Give the new state to the Handler so the UI Activity can update @@ -1478,16 +1807,14 @@ public Set getSensorIdsSet() { // } // } public void setRadio(BluetoothSocket socket){ - + System.out.println("initialize process set radio"); + registerDisconnectListener(); if (socket.isConnected()){ setBluetoothRadioState(BT_STATE.CONNECTING); mMyBluetoothAddress = socket.getRemoteDevice().getAddress(); connected(socket); } - - - -} + } private void sendMsgToHandlerList(int obtainMessage) { for(Handler handler : mHandlerList) { diff --git a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/VerisenseDeviceAndroid.java b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/VerisenseDeviceAndroid.java new file mode 100644 index 00000000..69b5f64a --- /dev/null +++ b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/VerisenseDeviceAndroid.java @@ -0,0 +1,56 @@ +package com.shimmerresearch.android; + +import android.bluetooth.BluetoothAdapter; +import android.os.Handler; + +import com.shimmerresearch.bluetooth.ShimmerBluetooth; +import com.shimmerresearch.driver.CallbackObject; +import com.shimmerresearch.driver.ObjectCluster; +import com.shimmerresearch.driver.ShimmerMsg; +import com.shimmerresearch.verisense.VerisenseDevice; +import com.shimmerresearch.verisense.communication.SyncProgressDetails; + +import java.util.ArrayList; +import java.util.List; + +public class VerisenseDeviceAndroid extends VerisenseDevice { + transient List mHandlerList = new ArrayList(); + + public VerisenseDeviceAndroid(Handler handler) { + super(); + mHandlerList.add(0, handler); + } + + private void sendMsgToHandlerListTarget(int what, int arg1, int arg2, Object object) { + for(Handler handler : mHandlerList) { + if (handler!=null) { + handler.obtainMessage(what, arg1, arg2, object).sendToTarget(); + } + } + } + + @Override + protected void dataHandler(ObjectCluster ojc){ + sendMsgToHandlerListTarget(ShimmerBluetooth.MSG_IDENTIFIER_DATA_PACKET, ojc); + } + + private void sendMsgToHandlerListTarget(int what, Object object) { + for(Handler handler : mHandlerList) { + if (handler!=null) { + handler.obtainMessage(what, object).sendToTarget(); + } + } + } + + @Override + public void sendCallBackMsg(int i, Object ojc){ + super.sendCallBackMsg(i, ojc); + if(i == ShimmerBluetooth.MSG_IDENTIFIER_STATE_CHANGE){ + sendMsgToHandlerListTarget(i, -1, -1, + new ObjectCluster(mShimmerUserAssignedName, getMacId(), ((CallbackObject)ojc).mState)); + } + else{ + sendMsgToHandlerListTarget(i, -1, -1, ojc); + } + } +} diff --git a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/DeviceConfigFragment.java b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/DeviceConfigFragment.java index 7716d2d9..f0984723 100644 --- a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/DeviceConfigFragment.java +++ b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/DeviceConfigFragment.java @@ -4,7 +4,6 @@ import android.app.Fragment; import android.content.Context; import android.os.Bundle; -import android.support.annotation.Nullable; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; @@ -16,6 +15,8 @@ import android.widget.LinearLayout; import android.widget.Toast; +import androidx.annotation.Nullable; + import com.shimmerresearch.android.Shimmer; import com.shimmerresearch.android.manager.ShimmerBluetoothManagerAndroid; import com.shimmerresearch.androidinstrumentdriver.R; diff --git a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/DeviceSensorConfigFragment.java b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/DeviceSensorConfigFragment.java index c75be7c1..35eb97ad 100644 --- a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/DeviceSensorConfigFragment.java +++ b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/DeviceSensorConfigFragment.java @@ -4,7 +4,6 @@ import android.app.Fragment; import android.content.Context; import android.os.Bundle; -import android.support.annotation.Nullable; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; @@ -16,6 +15,8 @@ import android.widget.LinearLayout; import android.widget.Toast; +import androidx.annotation.Nullable; + import com.shimmerresearch.android.Shimmer; import com.shimmerresearch.android.manager.ShimmerBluetoothManagerAndroid; import com.shimmerresearch.androidinstrumentdriver.R; diff --git a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/FileListActivity.java b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/FileListActivity.java index 9981904c..1b386080 100644 --- a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/FileListActivity.java +++ b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/FileListActivity.java @@ -4,8 +4,7 @@ import android.content.Intent; import android.os.Bundle; import android.os.Environment; -import android.support.annotation.Nullable; -import android.support.v4.content.FileProvider; + import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; @@ -14,6 +13,8 @@ import android.widget.Toast; +import androidx.core.content.FileProvider; + import com.shimmerresearch.androidinstrumentdriver.R; import org.apache.commons.lang3.StringUtils; @@ -21,6 +22,8 @@ import java.io.File; import java.util.ArrayList; +import javax.annotation.Nullable; + /** * Opens a list of files from a selected folder. Files in the list can be clicked to open an app selector. * A file provider that is defined in the Android manifest is needed in order to provide read access to files which are clicked. diff --git a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/PlotFragment.java b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/PlotFragment.java index ac3a965d..e6d291cb 100644 --- a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/PlotFragment.java +++ b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/PlotFragment.java @@ -8,7 +8,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; -import android.support.annotation.Nullable; import android.util.Log; import android.view.Display; import android.view.LayoutInflater; @@ -40,6 +39,8 @@ import java.util.HashMap; import java.util.List; +import javax.annotation.Nullable; + import pl.flex_it.androidplot.XYSeriesShimmer; /** diff --git a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/SensorsEnabledFragment.java b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/SensorsEnabledFragment.java index 87789a54..06ef3e3b 100644 --- a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/SensorsEnabledFragment.java +++ b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/SensorsEnabledFragment.java @@ -24,6 +24,7 @@ import com.shimmerresearch.driverUtilities.SensorDetails; import com.shimmerresearch.driverUtilities.SensorGroupingDetails; import com.shimmerresearch.driverUtilities.ShimmerVerObject; +import com.shimmerresearch.verisense.VerisenseDevice; import java.util.ArrayList; import java.util.List; @@ -157,7 +158,7 @@ public void onClick(View v) { AssembleShimmerConfig.generateSingleShimmerConfig(shimmerDeviceClone, Configuration.COMMUNICATION_TYPE.BLUETOOTH); - if (shimmerDevice instanceof Shimmer) { + if (shimmerDevice instanceof Shimmer || shimmerDevice instanceof VerisenseDevice) { bluetoothManager.configureShimmer(shimmerDeviceClone); mCallback.onSensorsSelected(); diff --git a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/ShimmerBluetoothDialog.java b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/ShimmerBluetoothDialog.java index 90413522..5564d42e 100644 --- a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/ShimmerBluetoothDialog.java +++ b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/ShimmerBluetoothDialog.java @@ -16,6 +16,7 @@ */ +import android.Manifest; import android.app.Activity; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; @@ -23,6 +24,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; import android.util.Log; @@ -38,6 +40,8 @@ import android.widget.Toast; +import androidx.core.app.ActivityCompat; + import com.shimmerresearch.androidinstrumentdriver.R; import java.util.Set; @@ -47,7 +51,7 @@ * devices detected in the area after discovery. When a device is chosen * by the user, the MAC address of the device is sent back to the parent * Activity in the result Intent. - * + * */ public class ShimmerBluetoothDialog extends Activity { // Debugging @@ -57,6 +61,7 @@ public class ShimmerBluetoothDialog extends Activity { // Return Intent extra public static String EXTRA_DEVICE_ADDRESS = "device_address"; + public static String EXTRA_DEVICE_NAME = "device_name"; // Member fields @@ -65,10 +70,11 @@ public class ShimmerBluetoothDialog extends Activity { private ArrayAdapter mNewDevicesArrayAdapter; //private String[] deviceAddresses={"","","","","","",""}; private Button scanButton; + @Override protected void onCreate(Bundle savedInstanceState) { //Set Material Design if the device's OS is Android Lollipop or higher - if(Build.VERSION.SDK_INT >= 21) { + if (Build.VERSION.SDK_INT >= 21) { setTheme(android.R.style.Theme_Material_Light_Dialog); } else { setTheme(android.R.style.Theme_Holo_Light_Dialog); @@ -84,7 +90,7 @@ protected void onCreate(Bundle savedInstanceState) { // Initialize the button to perform device discovery scanButton = (Button) findViewById(R.id.button_scan); - + scanButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { doDiscovery(); @@ -111,7 +117,8 @@ public void onClick(View v) { // Register for broadcasts when a device is discovered IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); this.registerReceiver(mReceiver, filter); - + filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_STARTED); + this.registerReceiver(mReceiver, filter); // Register for broadcasts when discovery has finished filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); this.registerReceiver(mReceiver, filter); @@ -164,6 +171,19 @@ private void doDiscovery() { findViewById(R.id.layoutNewDevices).setVisibility(View.VISIBLE); // If we're already discovering, stop it + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) { + // TODO: Consider calling + // ActivityCompat#requestPermissions + // here to request the missing permissions, and then overriding + // public void onRequestPermissionsResult(int requestCode, String[] permissions, + // int[] grantResults) + // to handle the case where the user grants the permission. See the documentation + // for ActivityCompat#requestPermissions for more details. + return; + } + } + if (mBtAdapter.isDiscovering()) { mBtAdapter.cancelDiscovery(); } @@ -181,10 +201,11 @@ public void onItemClick(AdapterView av, View v, int arg2, long arg3) { // Get the device MAC address, which is the last 17 chars in the View String info = ((TextView) v).getText().toString(); String address = info.substring(info.length() - 17); - + String devicename = info.split("\n")[0]; // Create the result Intent and include the MAC address Intent intent = new Intent(); intent.putExtra(EXTRA_DEVICE_ADDRESS, address); + intent.putExtra(EXTRA_DEVICE_NAME, devicename); Toast.makeText(getApplicationContext(),"Device Selected " + "-> "+ address, Toast.LENGTH_SHORT).show(); setResult(Activity.RESULT_OK, intent); // Set result and finish this Activity diff --git a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/ShimmerDialogConfigurations.java b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/ShimmerDialogConfigurations.java index aa61f796..8fd3a567 100644 --- a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/ShimmerDialogConfigurations.java +++ b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/ShimmerDialogConfigurations.java @@ -6,7 +6,6 @@ import android.content.DialogInterface; import android.hardware.Sensor; import android.os.Build; -import android.support.v4.content.ContextCompat; import android.text.InputType; import android.util.Log; import android.view.Gravity; @@ -29,6 +28,7 @@ import com.shimmerresearch.android.manager.ShimmerBluetoothManagerAndroid; import com.shimmerresearch.android.shimmerService.ShimmerService; import com.shimmerresearch.androidinstrumentdriver.R; +import com.shimmerresearch.androidradiodriver.Shimmer3BLEAndroid; import com.shimmerresearch.bluetooth.ShimmerBluetooth; import com.shimmerresearch.driver.Configuration; import com.shimmerresearch.driver.ShimmerDevice; @@ -224,6 +224,8 @@ public void onClick(DialogInterface dialog, int id) { if (shimmerDeviceClone instanceof Shimmer) { bluetoothManager.configureShimmer(shimmerDeviceClone); + }else if(shimmerDeviceClone instanceof Shimmer3BLEAndroid) { + bluetoothManager.configureShimmer(shimmerDeviceClone); } } }) @@ -329,6 +331,8 @@ public void onClick(DialogInterface dialog, int which) { ((Shimmer)shimmerDevice).writeConfigBytes(shimmerDeviceClone.getShimmerConfigBytes()); } else if (shimmerDevice instanceof Shimmer4Android){ ((Shimmer4Android)shimmerDevice).writeConfigBytes(shimmerDeviceClone.getShimmerConfigBytes()); + }else if(shimmerDeviceClone instanceof Shimmer3BLEAndroid) { + ((Shimmer3BLEAndroid)shimmerDevice).writeBytes(shimmerDeviceClone.getShimmerConfigBytes()); } } }); @@ -363,6 +367,8 @@ public void onClick(DialogInterface dialog, int which) { ((Shimmer)shimmerDevice).writeConfigBytes(shimmerDeviceClone.getShimmerConfigBytes()); } else if (shimmerDevice instanceof Shimmer4Android){ ((Shimmer4Android)shimmerDevice).writeConfigBytes(shimmerDeviceClone.getShimmerConfigBytes()); + }else if(shimmerDeviceClone instanceof Shimmer3BLEAndroid) { + ((Shimmer3BLEAndroid)shimmerDevice).writeBytes(shimmerDeviceClone.getShimmerConfigBytes()); } } }); @@ -415,6 +421,8 @@ public void onClick(DialogInterface dialog, int which) { //((Shimmer)shimmerDevice).configureShimmer(shimmerDeviceClone); } else if (shimmerDevice instanceof Shimmer4Android){ bluetoothManager.configureShimmer(shimmerDeviceClone); + }else if(shimmerDevice instanceof Shimmer3BLEAndroid) { + bluetoothManager.configureShimmer(shimmerDeviceClone); } } }); @@ -532,6 +540,8 @@ public void writeConfigToShimmer(ShimmerDevice clone, ShimmerBluetoothManagerAnd bluetoothManager.configureShimmer(clone); } else if (clone instanceof Shimmer4Android){ bluetoothManager.configureShimmer(clone); + } else if (clone instanceof Shimmer3BLEAndroid){ + bluetoothManager.configureShimmer(clone); } } diff --git a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/SignalsToPlotFragment.java b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/SignalsToPlotFragment.java index 7a946ef5..60532332 100644 --- a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/SignalsToPlotFragment.java +++ b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/SignalsToPlotFragment.java @@ -3,7 +3,6 @@ import android.content.Context; import android.os.Bundle; -import android.support.annotation.Nullable; import android.app.Fragment; import android.app.ListFragment; import android.util.Log; @@ -12,6 +11,8 @@ import android.widget.ArrayAdapter; import android.widget.ListView; +import androidx.annotation.Nullable; + import com.androidplot.xy.XYPlot; import com.shimmerresearch.android.manager.ShimmerBluetoothManagerAndroid; import com.shimmerresearch.android.shimmerService.ShimmerService; diff --git a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/supportfragments/ConnectedShimmersListFragment.java b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/supportfragments/ConnectedShimmersListFragment.java index 86430c6d..8d80d647 100644 --- a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/supportfragments/ConnectedShimmersListFragment.java +++ b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/supportfragments/ConnectedShimmersListFragment.java @@ -4,8 +4,8 @@ import android.app.Activity; import android.content.Context; import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v4.app.ListFragment; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.ListFragment; import android.util.Log; import android.view.View; import android.widget.AbsListView; @@ -26,24 +26,22 @@ */ public class ConnectedShimmersListFragment extends ListFragment { - OnShimmerDeviceSelectedListener mCallBack; - String selectedDeviceAddress, selectedDeviceName; final static String LOG_TAG = "SHIMMER"; + OnShimmerDeviceSelectedListener mCallBack; + String selectedDeviceAddress; ListView savedListView = null; ArrayAdapter savedListAdapter = null; - int selectedItemPos = -1; List shimmerDeviceList; Context context; int selectedDevicePos = -1; - public ConnectedShimmersListFragment() { // Required empty public constructor } //Container Activity must implement this interface public interface OnShimmerDeviceSelectedListener { - public void onShimmerDeviceSelected(String macAddress, String deviceName); + public void onShimmerDeviceSelected(String macAddress, String deviceName, Boolean selected); } @Override @@ -66,72 +64,71 @@ public static ConnectedShimmersListFragment newInstance() { public void buildShimmersConnectedListView(final List deviceList, final Context context) { if(isVisible()){ - shimmerDeviceList = deviceList; - this.context = context; - if(deviceList == null) { - //String[] displayList = {"Service not yet initialised"}; - String[] displayList = {"No devices connected"}; - ArrayAdapter listAdapter = new ArrayAdapter(context, android.R.layout.simple_list_item_1, displayList); - setListAdapter(listAdapter); - } - else { - final String[] nameList = new String[deviceList.size()]; - final String[] macList = new String[deviceList.size()]; - final String[] displayList = new String[deviceList.size()]; - - for (int i = 0; i < nameList.length; i++) { - nameList[i] = deviceList.get(i).getShimmerUserAssignedName(); - macList[i] = deviceList.get(i).getMacId(); - displayList[i] = nameList[i] + "\n" + macList[i]; + shimmerDeviceList = deviceList; + this.context = context; + if(deviceList == null) { + //String[] displayList = {"Service not yet initialised"}; + String[] displayList = {"No devices connected"}; + ArrayAdapter listAdapter = new ArrayAdapter(context, android.R.layout.simple_list_item_1, displayList); + setListAdapter(listAdapter); } + else { + final String[] nameList = new String[deviceList.size()]; + final String[] macList = new String[deviceList.size()]; + final String[] displayList = new String[deviceList.size()]; + + for (int i = 0; i < nameList.length; i++) { + nameList[i] = deviceList.get(i).getShimmerUserAssignedName(); + macList[i] = deviceList.get(i).getMacId(); + displayList[i] = nameList[i] + "\n" + macList[i]; + } - ArrayAdapter listAdapter = new ArrayAdapter(context, R.layout.simple_list_item_multiple_choice_force_black_text, displayList); - - //Set the list of devices to be displayed in the Fragment - setListAdapter(listAdapter); + ArrayAdapter listAdapter = new ArrayAdapter(context, R.layout.simple_list_item_multiple_choice_force_black_text, displayList); - final ListView listView = getListView(); - listView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE); - listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - selectedItemPos = position; + //Set the list of devices to be displayed in the Fragment + setListAdapter(listAdapter); - selectedDeviceAddress = macList[position]; - selectedDeviceName = nameList[position]; - selectedDevicePos = position; + final ListView listView = getListView(); + listView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE); + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + if(selectedDeviceAddress != null && selectedDeviceAddress == macList[position]){ + selectedDeviceAddress = null; + selectedDevicePos = -1; + } + else{ + selectedDeviceAddress = macList[position]; + selectedDevicePos = position; + } - try { - mCallBack.onShimmerDeviceSelected(macList[position], nameList[position]); - } catch (ClassCastException cce) { + try { + mCallBack.onShimmerDeviceSelected(macList[position], nameList[position], selectedDevicePos == -1 ? false : true); + } catch (ClassCastException cce) { + } } - } - }); + }); - //Save the listView so that it can be restored in onCreateView when returning to the Fragment. - savedListView = listView; - savedListAdapter = listAdapter; + //Save the listView so that it can be restored in onCreateView when returning to the Fragment. + savedListView = listView; + savedListAdapter = listAdapter; - //Ensure that the selected item's checkbox is checked - if (selectedDeviceAddress != null) { + //Ensure that the selected item's checkbox is checked for (int i = 0; i < listView.getAdapter().getCount(); i++) { - View view = getViewByPosition(i, listView); CheckedTextView checkedTextView = (CheckedTextView) view.findViewById(android.R.id.text1); if (checkedTextView != null) { String text = checkedTextView.getText().toString(); - if (text.contains(selectedDeviceAddress)) { - listView.setItemChecked(i, true); - } else { + if (selectedDeviceAddress == null || !text.contains(selectedDeviceAddress)) { listView.setItemChecked(i, false); } + else { + listView.setItemChecked(i, true); + } } - } } - - } } } @@ -157,6 +154,11 @@ public View getViewByPosition(int pos, ListView listView) { } } + public void removeSelectedDevice(){ + selectedDeviceAddress = null; + selectedDevicePos = -1; + } + public int getNumShimmersConnected() { if(shimmerDeviceList != null) { return shimmerDeviceList.size(); diff --git a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/supportfragments/DataSyncFragment.java b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/supportfragments/DataSyncFragment.java new file mode 100644 index 00000000..be172081 --- /dev/null +++ b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/supportfragments/DataSyncFragment.java @@ -0,0 +1,55 @@ +package com.shimmerresearch.android.guiUtilities.supportfragments; + +import android.content.Context; +import android.os.Bundle; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; +import android.widget.EditText; +import com.shimmerresearch.androidinstrumentdriver.R; + + +public class DataSyncFragment extends Fragment { + + Context context; + public EditText editTextParticipantName; + public EditText editTextTrialName; + public TextView TextViewPayloadIndex; + public TextView TextViewSpeed; + public TextView TextViewDirectory; + public Button ButtonDataSync; + + public DataSyncFragment() { + // Required empty public constructor + } + + public static DataSyncFragment newInstance() { + DataSyncFragment fragment = new DataSyncFragment(); + Bundle args = new Bundle(); + fragment.setArguments(args); + return fragment; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + context = getActivity(); + // Inflate the layout for this fragment + return inflater.inflate(R.layout.data_sync, container, false); + } + + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + ButtonDataSync = (Button) getView().findViewById(R.id.dataSyncButton); + editTextParticipantName = (EditText) getView().findViewById(R.id.participantName); + editTextTrialName = (EditText) getView().findViewById(R.id.trialName); + TextViewPayloadIndex = (TextView) getView().findViewById(R.id.payloadIndex); + TextViewSpeed = (TextView) getView().findViewById(R.id.speed); + TextViewDirectory = (TextView) getView().findViewById(R.id.directory); + super.onActivityCreated(savedInstanceState); + } +} \ No newline at end of file diff --git a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/supportfragments/DeviceConfigFragment.java b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/supportfragments/DeviceConfigFragment.java index 191d47be..d5bcafcb 100644 --- a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/supportfragments/DeviceConfigFragment.java +++ b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/supportfragments/DeviceConfigFragment.java @@ -3,8 +3,9 @@ import android.content.Context; import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; + +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -22,8 +23,10 @@ import com.shimmerresearch.driverUtilities.AssembleShimmerConfig; import com.shimmerresearch.driverUtilities.ConfigOptionDetailsSensor; import com.shimmerresearch.driverUtilities.SensorDetails; +import com.shimmerresearch.verisense.VerisenseDevice; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -48,10 +51,14 @@ public void buildDeviceConfigList(final ShimmerDevice shimmerDevice, final Conte final Map configOptionsMap = shimmerDevice.getConfigOptionsMap(); shimmerDeviceClone = shimmerDevice.deepClone(); Map sensorMap = shimmerDevice.getSensorMap(); - List listOfKeys = new ArrayList(); + final List listOfKeys = new ArrayList(); for (SensorDetails sd:sensorMap.values()) { if (sd.mSensorDetailsRef.mListOfConfigOptionKeysAssociated!=null && sd.isEnabled()) { - listOfKeys.addAll(sd.mSensorDetailsRef.mListOfConfigOptionKeysAssociated); + for(String configOptionKey:sd.mSensorDetailsRef.mListOfConfigOptionKeysAssociated){ + if(!listOfKeys.contains(configOptionKey)){ + listOfKeys.add(configOptionKey); + } + } } } @@ -135,7 +142,7 @@ public boolean onChildClick(ExpandableListView parent, View v, int groupPosition if(expandListView.getFooterViewsCount() == 0) { LinearLayout buttonLayout = new LinearLayout(context); buttonLayout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); - buttonLayout.setOrientation(LinearLayout.HORIZONTAL); + buttonLayout.setOrientation(LinearLayout.VERTICAL); Button writeConfigButton = new Button(context); Button resetListButton = new Button(context); writeConfigButton.setOnClickListener(new View.OnClickListener() { @@ -146,9 +153,9 @@ public void onClick(View v) { cloneList.add(0, shimmerDeviceClone); AssembleShimmerConfig.generateMultipleShimmerConfig(cloneList, Configuration.COMMUNICATION_TYPE.BLUETOOTH); - if(shimmerDeviceClone instanceof Shimmer) { + //if(shimmerDeviceClone instanceof Shimmer) { bluetoothManager.configureShimmer(shimmerDeviceClone); - } + //} } }); @@ -163,6 +170,39 @@ public void onClick(View v) { }); writeConfigButton.setText("Write config to Shimmer"); resetListButton.setText("Reset settings"); + + if(shimmerDevice instanceof VerisenseDevice){ + Button setDefaultConfigButton = new Button(context); + setDefaultConfigButton.setText("Set default config"); + setDefaultConfigButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + final Map defaultConfigMap = new HashMap(); + defaultConfigMap.put("Accel_Range", 3); + defaultConfigMap.put("Gyro_Range", 3); + defaultConfigMap.put("Accel_Rate", 2); + defaultConfigMap.put("Mode", 1); + defaultConfigMap.put("Accel_Gyro_Rate", 6); + defaultConfigMap.put("LP Mode", 1); + defaultConfigMap.put("Range", 3); + defaultConfigMap.put("PPG Rate", 3); + + shimmerDeviceClone = shimmerDevice.deepClone(); + for(String key : listOfKeys) { + if(defaultConfigMap.containsKey(key)){ + final ConfigOptionDetailsSensor cods = configOptionsMap.get(key); + if(cods != null){ + shimmerDeviceClone.setConfigValueUsingConfigLabel(key, cods.mConfigValues[defaultConfigMap.get(key)]); + } + } + } + expandListAdapter.updateCloneDevice(shimmerDeviceClone); + expandListAdapter.notifyDataSetChanged(); + Toast.makeText(context, "Settings have been set to default", Toast.LENGTH_SHORT).show(); + } + }); + buttonLayout.addView(setDefaultConfigButton); + } buttonLayout.addView(resetListButton); buttonLayout.addView(writeConfigButton); expandListView.addFooterView(buttonLayout); diff --git a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/supportfragments/PlotFragment.java b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/supportfragments/PlotFragment.java index 60cdf71b..b26f12e2 100644 --- a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/supportfragments/PlotFragment.java +++ b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/supportfragments/PlotFragment.java @@ -6,9 +6,11 @@ import android.graphics.Point; import android.os.Bundle; import android.os.Handler; +import android.os.Looper; import android.os.Message; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; + +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; import android.util.Log; import android.view.Display; import android.view.LayoutInflater; @@ -39,6 +41,8 @@ import java.text.DecimalFormat; import java.util.HashMap; import java.util.List; +import java.util.Timer; +import java.util.TimerTask; import pl.flex_it.androidplot.XYSeriesShimmer; @@ -50,6 +54,8 @@ public class PlotFragment extends Fragment { static String deviceState = ""; static TextView textViewDeviceName; static TextView textViewDeviceState; + static TextView textViewPRR; + static String selectedDeviceAddress; private static String LOG_TAG = "PlotFragment"; Button signalsToPlotButton; @@ -91,7 +97,7 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState) { initPlot(); textViewDeviceName = (TextView) getView().findViewById(R.id.textViewDeviceName); textViewDeviceState = (TextView) getView().findViewById(R.id.textViewDeviceState); - + textViewPRR = (TextView) getView().findViewById(R.id.textViewPRR); super.onActivityCreated(savedInstanceState); } @@ -176,8 +182,25 @@ public void setShimmerService(ShimmerService service) { } shimmerService.mPlotManager.updateDynamicPlot(dynamicPlot); } - - + static Timer timer = null; + static class PRRTask extends TimerTask { + @Override + public void run() { + if (shimmerService.getShimmer(mBluetoothAddress)!=null) { + double value = shimmerService.getShimmer(mBluetoothAddress).getPacketReceptionRateOverall(); + final String formattedValue = String.format("%.2f", value); + + Handler mainHandler = new Handler(Looper.getMainLooper()); + mainHandler.post(new Runnable() { + @Override + public void run() { + // Update UI elements here + textViewPRR.setText(formattedValue); + } + }); + } + } + } private static Handler graphHandler = new Handler() { @@ -198,11 +221,18 @@ public void handleMessage(Message msg) { } switch (state) { case CONNECTED: + if (timer!=null){ + timer.cancel(); + timer = new Timer(); + } Log.d(LOG_TAG,"Message Fully Initialized Received from Shimmer driver"); shimmerService.enableGraphingHandler(true); deviceState = "Connected"; - textViewDeviceName.setText(mBluetoothAddress); - textViewDeviceState.setText(deviceState); + if(selectedDeviceAddress.equals(mBluetoothAddress)){ + textViewDeviceName.setText(mBluetoothAddress); + textViewDeviceState.setText(deviceState); + } + break; case SDLOGGING: Log.d(LOG_TAG,"Message Fully Initialized Received from Shimmer driver"); @@ -214,10 +244,23 @@ public void handleMessage(Message msg) { case CONNECTING: Log.d(LOG_TAG,"Driver is attempting to establish connection with Shimmer device"); deviceState = "Connecting"; - textViewDeviceName.setText(mBluetoothAddress); - textViewDeviceState.setText(deviceState); + if(selectedDeviceAddress.equals(mBluetoothAddress)){ + textViewDeviceName.setText(mBluetoothAddress); + textViewDeviceState.setText(deviceState); + } break; case STREAMING: + + if (timer!=null){ + timer.cancel(); + timer = new Timer(); + } else { + timer = new Timer(); + } + // Schedule a task to be executed after a delay of 2 seconds + timer.schedule(new PRRTask(), 0 , 2000); + + deviceState="Streaming"; textViewDeviceName.setText(mBluetoothAddress); textViewDeviceState.setText(deviceState); @@ -279,12 +322,27 @@ else if(shimmerService.isEXGUsingTestSignal16Configuration(mBluetoothAddress)) textViewDeviceState.setText(deviceState); //TODO: set the enable logging regarding the user selection break; + case STREAMING_LOGGED_DATA: + deviceState="Data Sync"; + textViewDeviceName.setText(mBluetoothAddress); + textViewDeviceState.setText(deviceState); + break; case DISCONNECTED: Log.d(LOG_TAG,"Shimmer No State"); mBluetoothAddress=null; deviceState = "Disconnected"; textViewDeviceName.setText("Unknown"); textViewDeviceState.setText(deviceState); + if (timer!=null) { + timer.cancel(); + timer = null; + } + break; + case CONFIGURING: + break; + case CONNECTION_LOST: + break; + case CONNECTION_FAILED: break; } @@ -324,4 +382,11 @@ public void clearPlot() { mPlotDataMap.clear(); dynamicPlot.clear(); } + + public void setSelectedDeviceAddress(String address){ + selectedDeviceAddress = address; + textViewDeviceName.setText(address); + textViewDeviceState.setText("Connected"); + } + } diff --git a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/supportfragments/SensorsEnabledFragment.java b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/supportfragments/SensorsEnabledFragment.java index 49757cd8..c6739ffc 100644 --- a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/supportfragments/SensorsEnabledFragment.java +++ b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/supportfragments/SensorsEnabledFragment.java @@ -3,7 +3,7 @@ import android.app.Activity; import android.content.Context; import android.os.Bundle; -import android.support.v4.app.ListFragment; +import androidx.fragment.app.ListFragment; import android.util.Log; import android.view.View; import android.widget.AbsListView; @@ -18,12 +18,14 @@ import com.shimmerresearch.android.Shimmer4Android; import com.shimmerresearch.android.manager.ShimmerBluetoothManagerAndroid; import com.shimmerresearch.android.shimmerService.ShimmerService; +import com.shimmerresearch.androidradiodriver.Shimmer3BLEAndroid; import com.shimmerresearch.driver.Configuration; import com.shimmerresearch.driver.ShimmerDevice; import com.shimmerresearch.driverUtilities.AssembleShimmerConfig; import com.shimmerresearch.driverUtilities.SensorDetails; import com.shimmerresearch.driverUtilities.SensorGroupingDetails; import com.shimmerresearch.driverUtilities.ShimmerVerObject; +import com.shimmerresearch.verisense.VerisenseDevice; import java.util.ArrayList; import java.util.List; @@ -164,7 +166,7 @@ public void onClick(View v) { //TODO: Change this when AssembleShimmerConfig has been updated: AssembleShimmerConfig.generateMultipleShimmerConfig(cloneList, Configuration.COMMUNICATION_TYPE.BLUETOOTH); - if (shimmerDevice instanceof Shimmer) { + if (shimmerDevice instanceof Shimmer || shimmerDevice instanceof VerisenseDevice || shimmerDevice instanceof Shimmer3BLEAndroid) { //((Shimmer)device).writeConfigBytes(shimmerDeviceClone.getShimmerInfoMemBytes()); /*try { Thread.sleep(1000); diff --git a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/supportfragments/SignalsToPlotFragment.java b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/supportfragments/SignalsToPlotFragment.java index 77f12060..5df6cc19 100644 --- a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/supportfragments/SignalsToPlotFragment.java +++ b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/guiUtilities/supportfragments/SignalsToPlotFragment.java @@ -3,10 +3,12 @@ import android.content.Context; import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.support.v4.app.ListFragment; + +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.ListFragment; import android.util.Log; +import android.util.SparseBooleanArray; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; @@ -141,6 +143,23 @@ public void buildSignalsToPlotList(Context context, final ShimmerService service listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); updateCheckboxes(); + shimmerService.mPlotManager.removeAllSignals(); + + SparseBooleanArray pos = listView.getCheckedItemPositions(); + for (int i = 0; i < listView.getCount(); i++) { + if (pos.get(i)) { + try { + if(dynamicPlot == null) { + Log.e(LOG_TAG, "dynamicPlot is null!"); + } + shimmerService.mPlotManager.addSignal(mList.get(i), dynamicPlot); + } catch (Exception e) { + Log.e(LOG_TAG, "Error! Could not add signal: " + e); + e.printStackTrace(); + } + } + } + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { @@ -196,9 +215,8 @@ public static String joinStrings(String[] a){ public void setDeviceNotStreamingView() { String[] notStreamingMsg = new String[]{"Device not streaming", "Signals to plot can only be displayed when device is streaming"}; - ArrayAdapter adapter = new ArrayAdapter(context, android.R.layout.simple_list_item_1, notStreamingMsg); + ArrayAdapter adapter = new ArrayAdapter(getActivity(), android.R.layout.simple_list_item_1, notStreamingMsg); setListAdapter(adapter); } - } diff --git a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/manager/ShimmerBluetoothManagerAndroid.java b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/manager/ShimmerBluetoothManagerAndroid.java index 71131238..35630142 100644 --- a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/manager/ShimmerBluetoothManagerAndroid.java +++ b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/manager/ShimmerBluetoothManagerAndroid.java @@ -4,6 +4,11 @@ import android.app.ProgressDialog; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; +import android.bluetooth.le.BluetoothLeScanner; +import android.bluetooth.le.ScanCallback; +import android.bluetooth.le.ScanFilter; +import android.bluetooth.le.ScanResult; +import android.bluetooth.le.ScanSettings; import android.content.Context; import android.os.Handler; import android.util.Log; @@ -11,6 +16,10 @@ import com.shimmerresearch.android.Shimmer; import com.shimmerresearch.android.Shimmer4Android; +import com.shimmerresearch.android.VerisenseDeviceAndroid; +import com.shimmerresearch.android.protocol.VerisenseProtocolByteCommunicationAndroid; +import com.shimmerresearch.androidradiodriver.VerisenseBleAndroidRadioByteCommunication; +import com.shimmerresearch.androidradiodriver.Shimmer3BLEAndroid; import com.shimmerresearch.androidradiodriver.ShimmerRadioInitializerAndroid; import com.shimmerresearch.androidradiodriver.ShimmerSerialPortAndroid; import com.shimmerresearch.bluetooth.ShimmerBluetooth; @@ -35,8 +44,10 @@ import com.shimmerresearch.exceptions.ConnectionExceptionListener; import com.shimmerresearch.exceptions.ShimmerException; import com.shimmerresearch.managers.bluetoothManager.ShimmerBluetoothManager; +import com.shimmerresearch.verisense.communication.VerisenseProtocolByteCommunication; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; @@ -53,11 +64,19 @@ public class ShimmerBluetoothManagerAndroid extends ShimmerBluetoothManager { private static final String TAG = ShimmerBluetoothManagerAndroid.class.getSimpleName(); private static final String DEFAULT_SHIMMER_NAME = "ShimmerDevice"; + static final String VERISENSE_NAME_NO_PAIRING_REQUIRED = "Verisense-00"; + + BluetoothAdapter mBluetoothAdapter; Context mContext; protected Handler mHandler; private boolean AllowAutoPairing = true; + public enum BT_TYPE{ + BT_CLASSIC, + BLE + } + public ShimmerBluetoothManagerAndroid(Context context, Handler handler) throws Exception { super(); ShimmerRadioInitializer.useLegacyDelayBeforeBtRead(true); @@ -92,6 +111,13 @@ public void enablePairingOnConnect(boolean enable){ AllowAutoPairing = enable; } + public boolean checkIfDeviceRequiresPairing(String deviceName){ + if(deviceName.contains(VERISENSE_NAME_NO_PAIRING_REQUIRED)){ + return false; + } + return true; + } + /** * See also {@link #connectShimmerThroughBTAddress(String)}. * @param bluetoothAddress in the form of XX:XX:XX:XX:XX:XX @@ -99,10 +125,10 @@ public void enablePairingOnConnect(boolean enable){ * @exception IllegalArgumentException if bluetoothAddress is invalid, note this will only occur when {@link #enablePairingOnConnect(boolean)} is enabled * @exception DeviceNotPairedException if the device is not paired */ - public void connectShimmerThroughBTAddress(final String bluetoothAddress,Context context) { + public void connectShimmerThroughBTAddress(final String bluetoothAddress, final String deviceName, Context context) { if(isDevicePaired(bluetoothAddress) || AllowAutoPairing) { - if (!isDevicePaired(bluetoothAddress)){ + if (!isDevicePaired(bluetoothAddress) && checkIfDeviceRequiresPairing(deviceName)){ if (context!=null) { //Toast.makeText(mContext, "Attempting to pair device, please wait...", Toast.LENGTH_LONG).show(); final ProgressDialog progress = new ProgressDialog(context); @@ -116,7 +142,9 @@ public void connectShimmerThroughBTAddress(final String bluetoothAddress,Context } } addDiscoveredDevice(bluetoothAddress); - super.connectShimmerThroughBTAddress(bluetoothAddress); + //super.connectShimmerThroughBTAddress(bluetoothAddress); + BluetoothDeviceDetails bdd = new BluetoothDeviceDetails("",bluetoothAddress,deviceName); + super.connectShimmerThroughBTAddress(bdd); super.setConnectionExceptionListener(new ConnectionExceptionListener() { @Override public void onConnectionStart(String connectionHandle) { @@ -157,8 +185,126 @@ public void onConnectStartException(String connectionHandle) { * @exception DeviceNotPairedException if the device is not paired */ @Override + public void connectVerisenseDevice(BluetoothDeviceDetails bdd) { + VerisenseBleAndroidRadioByteCommunication radio1 = new VerisenseBleAndroidRadioByteCommunication(bdd.mShimmerMacId); + VerisenseProtocolByteCommunicationAndroid protocol1 = new VerisenseProtocolByteCommunicationAndroid(radio1); + final VerisenseDeviceAndroid verisenseDevice = new VerisenseDeviceAndroid(mHandler); + verisenseDevice.setMacIdFromUart(bdd.mShimmerMacId); + verisenseDevice.setProtocol(Configuration.COMMUNICATION_TYPE.BLUETOOTH, protocol1); + initializeNewShimmerCommon(verisenseDevice); + Thread thread = new Thread(){ + public void run(){ + + try { + verisenseDevice.connect(); + } catch (ShimmerException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + }; + thread.start(); + } + + public void connectShimmerThroughBTAddress(final String bluetoothAddress, BT_TYPE btType) { + if(btType.equals(BT_TYPE.BT_CLASSIC)){ + connectShimmerThroughBTAddress(bluetoothAddress); + }else{ + connectShimmer3BLEThroughBTAddress(bluetoothAddress,"",null); + } + } + @Override public void connectShimmerThroughBTAddress(final String bluetoothAddress) { - connectShimmerThroughBTAddress(bluetoothAddress,null); + + //scanLeDevice(bluetoothAddress); + //doDiscovery(); + connectShimmerThroughBTAddress(bluetoothAddress,"",null); + } + + public void connectShimmer3BLEThroughBTAddress(final String bluetoothAddress, final String deviceName, Context context){ + final Shimmer3BLEAndroid shimmer3BLE = new Shimmer3BLEAndroid(bluetoothAddress, mHandler); + shimmer3BLE.setMacIdFromUart(bluetoothAddress); + initializeNewShimmerCommon(shimmer3BLE); + Thread thread = new Thread(){ + public void run(){ + shimmer3BLE.connect(bluetoothAddress, "default"); + } + }; + thread.start(); + } + + //BT Classic Scan + private void doDiscovery() { + + // If we're already discovering, stop it + if (mBluetoothAdapter.isDiscovering()) { + mBluetoothAdapter.cancelDiscovery(); + } + + // Request discover from BluetoothAdapter + mBluetoothAdapter.startDiscovery(); + } + + private BluetoothLeScanner bluetoothLeScanner; + private boolean scanning; + private Handler handler = new Handler(); + // Stops scanning after 10 seconds. + private static final long SCAN_PERIOD = 10000; + List listScanBleDevice = new ArrayList(); + + //BLE Scan + private void scanLeDevice(String deviceMacAddress) { + getScannedBleDevices(); + bluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner(); + if (!scanning) { + // Stops scanning after a predefined scan period. + handler.postDelayed(new Runnable() { + @Override + public void run() { + scanning = false; + bluetoothLeScanner.stopScan(leScanCallback); + } + }, SCAN_PERIOD); + + scanning = true; + scanForAllBleDevices(); + //scanForSpecificBleDevices(deviceMacAddress); + + } else { + scanning = false; + bluetoothLeScanner.stopScan(leScanCallback); + } + } + private void scanForAllBleDevices() { + bluetoothLeScanner.startScan(leScanCallback); + } + private void scanForSpecificBleDevices(String deviceMacAddress) { + List scanFilters = new ArrayList<>(); + ScanFilter filter = new ScanFilter.Builder().setDeviceAddress(deviceMacAddress).build(); + scanFilters.add(filter); + ScanSettings scanSettings = new ScanSettings.Builder().build(); + bluetoothLeScanner.startScan(scanFilters, scanSettings, leScanCallback); + } + + // Device scan callback. + private ScanCallback leScanCallback = + new ScanCallback() { + @Override + public void onScanResult(int callbackType, ScanResult result) { + super.onScanResult(callbackType, result); + BluetoothDevice bledevice = result.getDevice(); + if(!listScanBleDevice.contains(result.getDevice())){ + listScanBleDevice.add(result.getDevice()); + } + } + }; + + private void getScannedBleDevices(){ + for(BluetoothDevice dev : listScanBleDevice) + { + System.out.println(dev.getAddress()); + } + } /** @@ -245,7 +391,7 @@ protected ShimmerDevice createNewShimmer3(ShimmerRadioInitializer shimmerRadioIn mProgressDialog.dismiss(); } ShimmerSerialPortAndroid serialPort = (ShimmerSerialPortAndroid) shimmerRadioInitializer.getSerialCommPort(); - Shimmer shimmer = new Shimmer(mHandler); + Shimmer shimmer = new Shimmer(mHandler, mContext); shimmer.setDelayForBtRespone(true); mMapOfBtConnectedShimmers.put(bluetoothAddress, shimmer); try { @@ -276,6 +422,18 @@ protected ShimmerDevice initializeShimmer3(AbstractSerialPortHal abstractSerialP return shimmerDevice; } + @Override + public void configureShimmer(final ShimmerDevice shimmerClone) { + Thread thread = new Thread(){ + public void run(){ + configureShimmers(Arrays.asList(shimmerClone)); + } + }; + + thread.start(); + + } + @Override protected Shimmer4sdk createNewShimmer4(String comport, String bluetoothAddress) { if(mProgressDialog!=null){ @@ -317,7 +475,10 @@ private void connectExistingShimmer4(Shimmer4Android shimmer4, String btAddress) } } - + @Override + protected BluetoothDeviceDetails getBluetoothDeviceDetails(String connectionHandle) { + return (BluetoothDeviceDetails)this.mMapOfParsedBtComPorts.get(connectionHandle); + } private void addDiscoveredDevice(String bluetoothAddress){ BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(bluetoothAddress); diff --git a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/protocol/VerisenseProtocolByteCommunicationAndroid.java b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/protocol/VerisenseProtocolByteCommunicationAndroid.java new file mode 100644 index 00000000..c0dafd97 --- /dev/null +++ b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/protocol/VerisenseProtocolByteCommunicationAndroid.java @@ -0,0 +1,142 @@ +package com.shimmerresearch.android.protocol; + +import android.content.Context; +import android.net.Uri; +import android.os.ParcelFileDescriptor; + +import androidx.documentfile.provider.DocumentFile; + +import com.shimmerresearch.exceptions.ShimmerException; +import com.shimmerresearch.tools.FileUtils; +import com.shimmerresearch.verisense.communication.AbstractByteCommunication; +import com.shimmerresearch.verisense.communication.VerisenseMessage; +import com.shimmerresearch.verisense.communication.VerisenseProtocolByteCommunication; + +import java.io.BufferedWriter; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class VerisenseProtocolByteCommunicationAndroid extends VerisenseProtocolByteCommunication { + + protected Context mContext; + protected Uri mTreeURI; + + public VerisenseProtocolByteCommunicationAndroid(AbstractByteCommunication byteComm) { + super(byteComm); + } + + public void enableWriteToBinFile(Context context, Uri treeUri){ + mContext = context; + mTreeURI = treeUri; + } + + @Override + protected void createBinFile(VerisenseMessage verisenseMessage, boolean crcError) { + System.out.println(); + try { + + String pIndex = String.format("%05d", verisenseMessage.payloadIndex); + if (crcError) { + dataFileName = String.format("%s_%s_%s.bin", new SimpleDateFormat("yyMMdd_HHmmss").format(new Date()), pIndex, BadCRC); + } else { + dataFileName = String.format("%s_%s.bin", new SimpleDateFormat("yyMMdd_HHmmss").format(new Date()), pIndex); + } + + // AdvanceLog(LogObject, "BinFileCreated", dataFilePath, ASMName); + } catch (Exception ex) { + // AdvanceLog(LogObject, "BinFileCreatedException", ex, ASMName); + } + + } + + @Override + public void readLoggedData() throws ShimmerException { + if (mContext==null || mTreeURI ==null){ + throw new ShimmerException("Context and Uri needs to be set"); + } + super.readLoggedData(); + } + + protected void WritePayloadToBinFile(VerisenseMessage verisenseMessage) { + + if (PreviouslyWrittenPayloadIndex != verisenseMessage.payloadIndex) { + try { + DocumentFile pickedDir = DocumentFile.fromTreeUri(mContext, mTreeURI); + DocumentFile[] arrDF = pickedDir.listFiles(); + for (DocumentFile file:arrDF) { + System.out.println(file.getName()); + } + //trial name + DocumentFile dfT = pickedDir.findFile(getTrialName()); + if (dfT==null){ + dfT = pickedDir.createDirectory(getTrialName()); + } + + //participant name + DocumentFile dfP = dfT.findFile(getParticipantID()); + if(dfP==null) { + dfP = dfT.createDirectory(getParticipantID()); + } + + //uuid + DocumentFile dfUUID = dfP.findFile(mByteCommunication.getUuid()); + if(dfUUID==null) { + dfUUID = dfP.createDirectory(mByteCommunication.getUuid()); + } + + //BinaryFiles + DocumentFile dfBF = dfUUID.findFile("BinaryFiles"); + if(dfBF==null) { + dfBF = dfUUID.createDirectory("BinaryFiles"); + } + + DocumentFile newFile = dfBF.findFile(dataFileName); + if (newFile == null) { + newFile = dfBF.createFile("application/bin", dataFileName); + dataFilePath = new FileUtils(mContext).getPath(newFile.getUri(), FileUtils.UriType.FILE); + } + if (newFile != null) { + ParcelFileDescriptor pfd = mContext.getContentResolver().openFileDescriptor(newFile.getUri(), "wa"); // "w" for write, "a" for append + FileOutputStream fos = new FileOutputStream(pfd.getFileDescriptor()); + fos.write(verisenseMessage.payloadBytes); + fos.flush(); + fos.close(); + } + + /* + // System.Console.WriteLine("Write Payload To Bin File!"); + File f = new File(dataFilePath); + if (!f.exists()) { + f.createNewFile(); + } + Files.write(Paths.get(dataFilePath), verisenseMessage.payloadBytes, StandardOpenOption.APPEND); + */ + if (verisenseMessage.mCRCErrorPayload) { + //SaveBinFileToDB(); + } else { + // only assume non crc error payload index is valid + PreviouslyWrittenPayloadIndex = verisenseMessage.payloadIndex; + } + // DataBufferToBeSaved = null; + // RealmService.UpdateSensorDataSyncDate(Asm_uuid.ToString()); + // UpdateSensorDataSyncDate(); + + + } catch (Exception ex) { + // AdvanceLog(LogObject, "FileAppendException", ex, ASMName); + // throw ex; + System.out.println(ex.toString()); + } + } else { + // AdvanceLog(LogObject, "WritePayloadToBinFile", "Same Payload Index = " + + // PayloadIndex.ToString(), ASMName); + } + + } + + +} diff --git a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/shimmerService/ShimmerService.java b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/shimmerService/ShimmerService.java index 3992a6df..0984e512 100644 --- a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/shimmerService/ShimmerService.java +++ b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/android/shimmerService/ShimmerService.java @@ -41,9 +41,11 @@ import android.app.Service; import android.bluetooth.BluetoothAdapter; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.media.MediaScannerConnection; +import android.net.Uri; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -63,10 +65,14 @@ import com.shimmerresearch.driver.FormatCluster; import com.shimmerresearch.driver.ObjectCluster; import com.shimmerresearch.driver.ShimmerDevice; +import com.shimmerresearch.driverUtilities.BluetoothDeviceDetails; import com.shimmerresearch.driverUtilities.ChannelDetails.CHANNEL_TYPE; +import com.shimmerresearch.exceptions.ShimmerException; import com.shimmerresearch.tools.Logging; import com.shimmerresearch.tools.PlotManagerAndroid; +import com.shimmerresearch.verisense.VerisenseDevice; +import java.net.URI; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; @@ -90,6 +96,10 @@ public class ShimmerService extends Service { protected Handler mHandlerGraph=null; private boolean mGraphing=false; public String mLogFileName="Default"; + + public Uri mFileURI= null; + public ContentResolver mResolver = null; + public Context mContext = null; Filter mFilter; Filter mLPFilterECG; Filter mHPFilterECG; @@ -245,7 +255,29 @@ public boolean isECGtoHREnabled(){ } public void connectShimmer(final String bluetoothAddress,Context context){ - btManager.connectShimmerThroughBTAddress(bluetoothAddress,context); + btManager.connectShimmerThroughBTAddress(bluetoothAddress,"",context); + } + + public void connectShimmer(final String bluetoothAddress,final String deviceName, Context context){ + btManager.connectShimmerThroughBTAddress(bluetoothAddress,deviceName,context); + } + + public void connectShimmer(final String bluetoothAddress,final String deviceName, ShimmerBluetoothManagerAndroid.BT_TYPE preferredBtType, Context context){ + boolean isVerisense = false; + if (deviceName!=null){ + if (deviceName.contains(VerisenseDevice.VERISENSE_PREFIX)) { + isVerisense = true; + } + } + + if (isVerisense){ + btManager.connectVerisenseDevice(new BluetoothDeviceDetails("",bluetoothAddress,deviceName)); + } else { + btManager.connectShimmerThroughBTAddress(bluetoothAddress, preferredBtType); //Connect to the selected device + } + + + } public void connectShimmer(final String bluetoothAddress){ @@ -427,9 +459,17 @@ public void handleMsgDataPacket(Message msg) { char[] bA=objectCluster.getMacAddress().toCharArray(); Logging shimmerLog; if (mLogFileName.equals("Default")){ - shimmerLog=new Logging(fromMilisecToDate(System.currentTimeMillis()) + " Device" + bA[12] + bA[13] + bA[15] + bA[16],"\t", mLogFolderName, mLoggingFileType); + if(mFileURI==null) { + shimmerLog = new Logging(fromMilisecToDate(System.currentTimeMillis()) + " Device" + bA[12] + bA[13] + bA[15] + bA[16], "\t", mLogFolderName, mLoggingFileType); + } else { + shimmerLog = new Logging(mFileURI,mContext,fromMilisecToDate(System.currentTimeMillis()) + " Device" + bA[12] + bA[13] + bA[15] + bA[16], "\t", mLogFolderName, mLoggingFileType); + } } else { - shimmerLog=new Logging(fromMilisecToDate(System.currentTimeMillis()) + mLogFileName,"\t", mLogFolderName, mLoggingFileType); + if(mFileURI==null) { + shimmerLog = new Logging(fromMilisecToDate(System.currentTimeMillis()) + mLogFileName, "\t", mLogFolderName, mLoggingFileType); + }else { + shimmerLog = new Logging(mFileURI,mContext,fromMilisecToDate(System.currentTimeMillis()) + mLogFileName, "\t", mLogFolderName, mLoggingFileType); + } } mLogShimmer.remove(objectCluster.getMacAddress()); if (mLogShimmer.get(objectCluster.getMacAddress())==null){ @@ -513,6 +553,7 @@ public void handleMsgStateChange(Message msg) { sendBroadcast(intent); break; case DISCONNECTED: + btManager.removeShimmerDeviceBtConnected(macAddress); intent.putExtra("ShimmerBluetoothAddress", macAddress ); intent.putExtra("ShimmerDeviceName", shimmerName ); intent.putExtra("ShimmerState",BT_STATE.DISCONNECTED); @@ -915,8 +956,8 @@ public void startStreaming(String bluetoothAddress) { } */ - public void startStreaming(String bluetoothAddress) { - btManager.startStreaming(bluetoothAddress); + public void startStreaming(String bluetoothAddress) throws Exception{ + btManager.startStreaming(bluetoothAddress); } /** @@ -1008,8 +1049,8 @@ public void stopStreaming(String bluetoothAddress) { } */ - public void stopStreaming(String bluetoothAddress) { - btManager.stopStreaming(bluetoothAddress); + public void stopStreaming(String bluetoothAddress) throws Exception{ + btManager.stopStreaming(bluetoothAddress); } /** @@ -1130,7 +1171,12 @@ public void setLoggingName(String name){ public void closeAndRemoveFile(String bluetoothAddress){ if (mLogShimmer.get(bluetoothAddress)!=null){ mLogShimmer.get(bluetoothAddress).closeFile(); - MediaScannerConnection.scanFile(this, new String[] { mLogShimmer.get(bluetoothAddress).getAbsoluteName() }, null, null); + + try { + MediaScannerConnection.scanFile(this, new String[] { mLogShimmer.get(bluetoothAddress).getAbsoluteName() }, null, null); + } catch (Exception e) { + System.out.println(e); + } mLogShimmer.remove(bluetoothAddress); } diff --git a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/androidradiodriver/Shimmer3BLEAndroid.java b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/androidradiodriver/Shimmer3BLEAndroid.java new file mode 100644 index 00000000..2785d85d --- /dev/null +++ b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/androidradiodriver/Shimmer3BLEAndroid.java @@ -0,0 +1,602 @@ +package com.shimmerresearch.androidradiodriver; + +import static com.shimmerresearch.android.Shimmer.MESSAGE_TOAST; + +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattService; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; + +import com.clj.fastble.BleManager; +import com.clj.fastble.callback.BleGattCallback; +import com.clj.fastble.callback.BleMtuChangedCallback; +import com.clj.fastble.callback.BleNotifyCallback; +import com.clj.fastble.callback.BleWriteCallback; +import com.clj.fastble.data.BleDevice; +import com.clj.fastble.exception.BleException; +import com.shimmerresearch.android.Shimmer; +import com.shimmerresearch.bluetooth.BluetoothProgressReportPerCmd; +import com.shimmerresearch.bluetooth.ShimmerBluetooth; +import com.shimmerresearch.driver.BasicProcessWithCallBack; +import com.shimmerresearch.driver.CallbackObject; +import com.shimmerresearch.driver.Configuration; +import com.shimmerresearch.driver.FormatCluster; +import com.shimmerresearch.driver.ObjectCluster; +import com.shimmerresearch.driver.ShimmerDevice; +import com.shimmerresearch.driver.ShimmerDeviceCallbackAdapter; +import com.shimmerresearch.driver.ShimmerMsg; +import com.shimmerresearch.driver.ThreadSafeByteFifoBuffer; +import com.shimmerresearch.driver.shimmer2r3.ConfigByteLayoutShimmer3; +import com.shimmerresearch.driverUtilities.ChannelDetails; +import com.shimmerresearch.driverUtilities.SensorDetails; +import com.shimmerresearch.driverUtilities.UtilShimmer; +import com.shimmerresearch.exceptions.ShimmerException; +import com.shimmerresearch.sensors.kionix.SensorKionixAccel; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Collection; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import bolts.TaskCompletionSource; + +public class Shimmer3BLEAndroid extends ShimmerBluetooth implements Serializable { + transient BleDevice mBleDevice; + String TxID = "49535343-8841-43f4-a8d4-ecbe34729bb3"; + String RxID = "49535343-1e4d-4bd9-ba61-23c647249616"; + String ServiceID = "49535343-fe7d-4ae5-8fa9-9fafd205e455"; + UUID sid = UUID.fromString(ServiceID); + UUID txid = UUID.fromString(TxID); + UUID rxid = UUID.fromString(RxID); + String mMac; + String uuid; + transient ThreadSafeByteFifoBuffer mBuffer; + protected transient ShimmerDeviceCallbackAdapter mDeviceCallbackAdapter = new ShimmerDeviceCallbackAdapter(this); + transient public final Handler mHandler; + public static final String TOAST = "toast"; + + //"DA:A6:19:F0:4A:D7" + //"E7:45:2C:6D:6F:14" + + /** + * Initialize a ble radio + * + * @param mac mac address of the Shimmer3 BLE device e.g. d0:2b:46:3d:a2:bb + */ + public Shimmer3BLEAndroid(String mac) { + mMac = mac; + mHandler = null; + } + + public Shimmer3BLEAndroid(String mac, Handler handler){ + mMac = mac; + mHandler = handler; + } + transient TaskCompletionSource mTaskConnect = new TaskCompletionSource<>(); + transient TaskCompletionSource mTaskMTU = new TaskCompletionSource<>(); + + @Override + public void sendCallBackMsg(int msgid,Object obj){ + if (mHandler != null) { + mHandler.obtainMessage(msgid, obj).sendToTarget(); + } + } + /** + * Connect to the Shimmer3 BLE device + */ + @Override + public void connect(String s, String s1) { + mTaskConnect = new TaskCompletionSource<>(); + BleManager.getInstance().connect(mMac, new BleGattCallback() { + @Override + public void onStartConnect() { + System.out.println("Connecting"); + } + + @Override + public void onConnectFail(BleDevice bleDevice, BleException exception) { + + } + + @Override + public void onConnectSuccess(BleDevice bleDevice, BluetoothGatt gatt, int status) { + mBuffer = new ThreadSafeByteFifoBuffer(1000000); + mTaskMTU = new TaskCompletionSource<>(); + BleManager.getInstance().setMtu(bleDevice, 251, new BleMtuChangedCallback() { + @Override + public void onSetMTUFailure(BleException exception) { + System.out.println("MTU Failure"); + + } + + @Override + public void onMtuChanged(int mtu) { + System.out.println("MTU Changed: " + mtu); + mTaskMTU.setResult("MTU Changed: " + mtu); + } + }); + + try { + boolean result = mTaskMTU.getTask().waitForCompletion(3, TimeUnit.SECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + gatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH); + } + + mBleDevice = bleDevice; + startServiceS(bleDevice); + System.out.println(bleDevice.getMac() + " Connected"); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + gatt.setPreferredPhy(BluetoothDevice.PHY_LE_2M_MASK, BluetoothDevice.PHY_LE_2M_MASK, BluetoothDevice.PHY_OPTION_NO_PREFERRED); + } + mTaskConnect.setResult("Connected"); + //mHandler.obtainMessage(ShimmerBluetooth.MSG_IDENTIFIER_STATE_CHANGE, -1, -1, + //new ObjectCluster("", bleDevice.getMac(), BT_STATE.CONNECTED)).sendToTarget(); + sendCallBackMsg(ShimmerBluetooth.MSG_IDENTIFIER_STATE_CHANGE, new ObjectCluster("", bleDevice.getMac(), BT_STATE.CONNECTED)); + Bundle bundle = new Bundle(); + bundle.putString(TOAST, "Device connection established"); + sendMsgToHandlerList(MESSAGE_TOAST, bundle); + + mIOThread = new IOThread(); + mIOThread.start(); + if (mUseProcessingThread){ + mPThread = new ProcessingThread(); + mPThread.start(); + } + + initialize(); + } + + @Override + public void onDisConnected(boolean isActiveDisConnected, BleDevice bleDevice, BluetoothGatt gatt, int status) { + //mHandler.obtainMessage(ShimmerBluetooth.MSG_IDENTIFIER_STATE_CHANGE, -1, -1, + //new ObjectCluster("", bleDevice.getMac(), BT_STATE.DISCONNECTED)).sendToTarget(); + sendCallBackMsg(ShimmerBluetooth.MSG_IDENTIFIER_STATE_CHANGE, new ObjectCluster("", bleDevice.getMac(), BT_STATE.DISCONNECTED)); + + Bundle bundle = new Bundle(); + bundle.putString(TOAST, "Device connection was lost"); + sendMsgToHandlerList(MESSAGE_TOAST, bundle); + System.out.println(); + } + }); + + + try { + boolean result = mTaskConnect.getTask().waitForCompletion(10, TimeUnit.SECONDS); + Thread.sleep(200); + if (!result) { + System.out.println("Connect fail"); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + public void startServiceS(BleDevice bleDevice) { + List services = BleManager.getInstance().getBluetoothGattServices(bleDevice); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + + + for (BluetoothGattService service : services) { + System.out.println(service.getUuid()); + if (service.getUuid().compareTo(sid) == 0) { + for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) { + if (characteristic.getUuid().compareTo(txid) == 0) { + //newConnectedBLEDevice(bleDevice,characteristic); + CharSequence text = "TXID!"; + } else if (characteristic.getUuid().compareTo(rxid) == 0) { + newConnectedBLEDevice(bleDevice, characteristic); + CharSequence text = "RxID!"; + } + } + } + } + + + } + } + + /** + * Start notify for characteristics changed + * + * @param bleDevice BLE device + * @param characteristic + */ + public void newConnectedBLEDevice(final BleDevice bleDevice, final BluetoothGattCharacteristic characteristic) { + int count = 1; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + BleManager.getInstance().notify( + bleDevice, + characteristic.getService().getUuid().toString(), + characteristic.getUuid().toString(), + new BleNotifyCallback() { + + + @Override + public void onNotifySuccess() { + + } + + @Override + public void onNotifyFailure(BleException exception) { + + } + + @Override + public void onCharacteristicChanged(byte[] data) { + try { + mBuffer.write(data); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + }); + } + } + + @Override + protected boolean bytesAvailableToBeRead() { + if (mBuffer.size()>0) { + return true; + } + return false; + } + + @Override + protected int availableBytes() { + return mBuffer.size(); + } + + @Override + public void writeBytes(byte[] bytes) { + BleManager.getInstance().write(mBleDevice, sid.toString(), txid.toString(), bytes, false, new BleWriteCallback() { + @Override + public void onWriteSuccess(int current, int total, byte[] justWrite) { + System.out.println("Write Success " + UtilShimmer.bytesToHexStringWithSpacesFormatted(justWrite)); + } + + @Override + public void onWriteFailure(BleException exception) { + System.out.println("Write Fail"); + } + }); + } + + @Override + protected void stop() { + try { + disconnect(); + } catch (ShimmerException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + @Override + protected void sendProgressReport(BluetoothProgressReportPerCmd bluetoothProgressReportPerCmd) { + mDeviceCallbackAdapter.sendProgressReport(bluetoothProgressReportPerCmd); + } + + @Override + protected void isReadyForStreaming() { + mDeviceCallbackAdapter.isReadyForStreaming(); + restartTimersIfNull(); + } + + @Override + protected void isNowStreaming() { + mDeviceCallbackAdapter.isNowStreaming(); + } + + @Override + protected void hasStopStreaming() { + mDeviceCallbackAdapter.hasStopStreaming(); + } + + @Override + protected void sendStatusMsgPacketLossDetected() { + + } + + @Override + protected void inquiryDone() { + mDeviceCallbackAdapter.inquiryDone(); + isReadyForStreaming(); + } + + @Override + protected void sendStatusMSGtoUI(String s) { + + } + + @Override + protected void printLogDataForDebugging(String s) { + consolePrintLn(s); + } + + @Override + protected void connectionLost() { + try { + disconnect(); + } catch (ShimmerException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + setBluetoothRadioState(BT_STATE.CONNECTION_LOST); + } + + @Override + public boolean setBluetoothRadioState(BT_STATE state) { + boolean isChanged = super.setBluetoothRadioState(state); + mDeviceCallbackAdapter.setBluetoothRadioState(state, isChanged); + return isChanged; + } + + @Override + protected void startOperation(BT_STATE bt_state) { + this.startOperation(bt_state, 1); + consolePrintLn(bt_state + " START"); + } + + @Override + protected void finishOperation(BT_STATE bt_state) { + mDeviceCallbackAdapter.finishOperation(bt_state); + } + + @Override + protected void startOperation(BT_STATE bt_state, int i) { + mDeviceCallbackAdapter.startOperation(bt_state, i); + } + + @Override + protected void eventLogAndStreamStatusChanged(byte currentCommand) { + if (currentCommand == STOP_LOGGING_ONLY_COMMAND) { + //TODO need to query the Bluetooth connection here! + if (mIsStreaming) { + setBluetoothRadioState(BT_STATE.STREAMING); + } else if (isConnected()) { + setBluetoothRadioState(BT_STATE.CONNECTED); + } else { + setBluetoothRadioState(BT_STATE.DISCONNECTED); + } + } else { + if (mIsStreaming && isSDLogging()) { + setBluetoothRadioState(BT_STATE.STREAMING_AND_SDLOGGING); + } else if (mIsStreaming) { + setBluetoothRadioState(BT_STATE.STREAMING); + } else if (isSDLogging()) { + setBluetoothRadioState(BT_STATE.SDLOGGING); + } else { + // if(!isStreaming() && !isSDLogging() && isConnected()){ + if (!mIsStreaming && !isSDLogging() && isConnected() && mBluetoothRadioState != BT_STATE.CONNECTED) { + setBluetoothRadioState(BT_STATE.CONNECTED); + } + // if(getBTState() == BT_STATE.INITIALISED){ + // + // } + // else if(getBTState() != BT_STATE.CONNECTED){ + // setState(BT_STATE.CONNECTED); + // } + + CallbackObject callBackObject = new CallbackObject(NOTIFICATION_SHIMMER_STATE_CHANGE, mBluetoothRadioState, getMacId(), getComPort()); + sendCallBackMsg(MSG_IDENTIFIER_STATE_CHANGE, callBackObject); + } + } + } + + @Override + protected void batteryStatusChanged() { + mDeviceCallbackAdapter.batteryStatusChanged(); + } + + @Override + protected byte[] readBytes(int numberofBytes) { + try { + return mBuffer.read(numberofBytes); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return null; + } + + @Override + protected byte readByte() { + try { + return mBuffer.read(); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return -1; + } + + @Override + protected void dockedStateChange() { + mDeviceCallbackAdapter.dockedStateChange(); + } + + @Override + public ShimmerDevice deepClone() { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(this); + + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ObjectInputStream ois = new ObjectInputStream(bais); + Object readdd = ois.readObject(); + return (ShimmerDevice) readdd; + } catch (IOException e) { + e.printStackTrace(); + return null; + } catch (ClassNotFoundException e) { + e.printStackTrace(); + return null; + } + } + + @Override + protected void interpretDataPacketFormat(Object o, Configuration.COMMUNICATION_TYPE communication_type) { + + } + + @Override + public void createConfigBytesLayout() { + mConfigByteLayout = new ConfigByteLayoutShimmer3(getFirmwareIdentifier(), getFirmwareVersionMajor(), getFirmwareVersionMinor(), getFirmwareVersionInternal()); + } + + @Override + protected void dataHandler(ObjectCluster objectCluster) { + mDeviceCallbackAdapter.dataHandler(objectCluster); + } + + @Override + protected void processMsgFromCallback(ShimmerMsg shimmerMsg) { + System.out.println(shimmerMsg.mIdentifier); + } + + @Override + public void disconnect() throws ShimmerException { + // super.disconnect(); + stopAllTimers(); + BleManager.getInstance().disconnect(mBleDevice); + closeConnection(); + setBluetoothRadioState(BT_STATE.DISCONNECTED); + Bundle bundle = new Bundle(); + bundle.putString(TOAST, "Device connection was lost"); + sendMsgToHandlerList(MESSAGE_TOAST, bundle); + } + private void sendMsgToHandlerList(int obtainMessage, Bundle bundle) { + if (mHandler != null) { + Message msg = mHandler.obtainMessage(obtainMessage); + msg.setData(bundle); + mHandler.sendMessage(msg); + } + + } + private void closeConnection() { + try { + if (mIOThread != null) { + mIOThread.stop = true; + + // Closing serial port before before thread is finished stopping throws an error so waiting here + while (mIOThread != null && mIOThread.isAlive()) ; + + mIOThread = null; + + if (mUseProcessingThread) { + mPThread.stop = true; + mPThread = null; + } + } + mIsStreaming = false; + mIsInitialised = false; + + setBluetoothRadioState(BT_STATE.DISCONNECTED); + } catch (Exception ex) { + consolePrintException(ex.getMessage(), ex.getStackTrace()); + setBluetoothRadioState(BT_STATE.DISCONNECTED); + } + } + + //Need to override here because ShimmerDevice class uses a different map + @Override + public String getSensorLabel(int sensorKey) { + //TODO 2017-08-03 MN: super does this but in a different way, don't know is either is better + super.getSensorLabel(sensorKey); + SensorDetails sensor = mSensorMap.get(sensorKey); + if (sensor != null) { + return sensor.mSensorDetailsRef.mGuiFriendlyLabel; + } + return null; + } + + public class SensorDataReceived extends BasicProcessWithCallBack { + + @Override + protected void processMsgFromCallback(ShimmerMsg shimmerMSG) { + // TODO Auto-generated method stub + System.out.println(shimmerMSG.mIdentifier); + + + // TODO Auto-generated method stub + + // TODO Auto-generated method stub + int ind = shimmerMSG.mIdentifier; + + Object object = (Object) shimmerMSG.mB; + + if (ind == Shimmer.MSG_IDENTIFIER_STATE_CHANGE) { + CallbackObject callbackObject = (CallbackObject) object; + + if (callbackObject.mState == BT_STATE.CONNECTING) { + } else if (callbackObject.mState == BT_STATE.CONNECTED) { + } else if (callbackObject.mState == BT_STATE.DISCONNECTED + // || callbackObject.mState == BT_STATE.NONE + || callbackObject.mState == BT_STATE.CONNECTION_LOST) { + + } + } else if (ind == Shimmer.MSG_IDENTIFIER_NOTIFICATION_MESSAGE) { + CallbackObject callbackObject = (CallbackObject) object; + int msg = callbackObject.mIndicator; + if (msg == Shimmer.NOTIFICATION_SHIMMER_FULLY_INITIALIZED) { + } + if (msg == Shimmer.NOTIFICATION_SHIMMER_STOP_STREAMING) { + + } else if (msg == Shimmer.NOTIFICATION_SHIMMER_START_STREAMING) { + + } else { + } + } else if (ind == Shimmer.MSG_IDENTIFIER_DATA_PACKET) { + + double accelX = 0; + double accelY = 0; + double accelZ = 0; + FormatCluster formatx; + FormatCluster formaty; + FormatCluster formatz; + + int INVALID_RESULT = -1; + + ObjectCluster objc = (ObjectCluster) shimmerMSG.mB; + + Collection adcFormats = objc.getCollectionOfFormatClusters(SensorKionixAccel.ObjectClusterSensorName.ACCEL_LN_X); + formatx = ((FormatCluster) ObjectCluster.returnFormatCluster(adcFormats, ChannelDetails.CHANNEL_TYPE.CAL.toString())); // retrieve the calibrated data + + adcFormats = objc.getCollectionOfFormatClusters(SensorKionixAccel.ObjectClusterSensorName.ACCEL_LN_Y); + formaty = ((FormatCluster) ObjectCluster.returnFormatCluster(adcFormats, ChannelDetails.CHANNEL_TYPE.CAL.toString())); // retrieve the calibrated data + + adcFormats = objc.getCollectionOfFormatClusters(SensorKionixAccel.ObjectClusterSensorName.ACCEL_LN_Z); + formatz = ((FormatCluster) ObjectCluster.returnFormatCluster(adcFormats, ChannelDetails.CHANNEL_TYPE.CAL.toString())); // retrieve the calibrated data + + if (formatx != null) { + System.out.println("X:" + formatx.mData + " Y:" + formaty.mData + " Z:" + formatz.mData); + + } else { + System.out.println("ERROR! FormatCluster is Null!"); + } + + } else if (ind == Shimmer.MSG_IDENTIFIER_PACKET_RECEPTION_RATE_OVERALL) { + + } + + + } + + } +} diff --git a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/androidradiodriver/Shimmer3BleAndroidRadioByteCommunication.java b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/androidradiodriver/Shimmer3BleAndroidRadioByteCommunication.java new file mode 100644 index 00000000..7e5c5772 --- /dev/null +++ b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/androidradiodriver/Shimmer3BleAndroidRadioByteCommunication.java @@ -0,0 +1,23 @@ +package com.shimmerresearch.androidradiodriver; + +import java.util.UUID; + +public class Shimmer3BleAndroidRadioByteCommunication extends VerisenseBleAndroidRadioByteCommunication{ + + + + /** + * Initialize a ble radio + * + * @param mac mac address of the verisense device e.g. d0:2b:46:3d:a2:bb + */ + public Shimmer3BleAndroidRadioByteCommunication(String mac) { + super(mac); + TxID = "49535343-8841-43f4-a8d4-ecbe34729bb3"; + RxID = "49535343-1e4d-4bd9-ba61-23c647249616"; + ServiceID = "49535343-fe7d-4ae5-8fa9-9fafd205e455"; + sid = UUID.fromString(ServiceID); + txid = UUID.fromString(TxID); + rxid = UUID.fromString(RxID); + } +} diff --git a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/androidradiodriver/Shimmer3RAndroidRadioByteCommunication.java b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/androidradiodriver/Shimmer3RAndroidRadioByteCommunication.java new file mode 100644 index 00000000..a48a7e44 --- /dev/null +++ b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/androidradiodriver/Shimmer3RAndroidRadioByteCommunication.java @@ -0,0 +1,23 @@ +package com.shimmerresearch.androidradiodriver; + +import java.util.UUID; + +public class Shimmer3RAndroidRadioByteCommunication extends VerisenseBleAndroidRadioByteCommunication{ + + + + /** + * Initialize a ble radio + * + * @param mac mac address of the verisense device e.g. d0:2b:46:3d:a2:bb + */ + public Shimmer3RAndroidRadioByteCommunication(String mac) { + super(mac); + TxID = "65333333-A115-11E2-9E9A-0800200CA102"; + RxID = "65333333-A115-11E2-9E9A-0800200CA101"; + ServiceID = "65333333-A115-11E2-9E9A-0800200CA100"; + sid = UUID.fromString(ServiceID); + txid = UUID.fromString(TxID); + rxid = UUID.fromString(RxID); + } +} diff --git a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/androidradiodriver/ShimmerSerialPortAndroid.java b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/androidradiodriver/ShimmerSerialPortAndroid.java index 13f4a883..0721081b 100644 --- a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/androidradiodriver/ShimmerSerialPortAndroid.java +++ b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/androidradiodriver/ShimmerSerialPortAndroid.java @@ -35,16 +35,23 @@ public class ShimmerSerialPortAndroid extends AbstractSerialPortHal { transient private BluetoothSocket mBluetoothSocket; transient private DataInputStream mInStream; transient private OutputStream mOutStream; + private transient SerialPortListener mShimmerSerialEventCallback; + private boolean mUseListenerThread; public ShimmerSerialPortAndroid(String bluetoothAddress){ mBluetoothAddress = bluetoothAddress; mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); } + public ShimmerSerialPortAndroid(String bluetoothAddress, boolean useListenerThread){ + mBluetoothAddress = bluetoothAddress; + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + mUseListenerThread = useListenerThread; + } @Override public void connect(){ - + System.out.println("initialize process shimmer serial port android"); if (mState == ShimmerBluetooth.BT_STATE.DISCONNECTED) { createBluetoothSocket(); @@ -95,6 +102,9 @@ private boolean getIOStreams(){ InputStream tmpIn = mBluetoothSocket.getInputStream(); mInStream = new DataInputStream(tmpIn); mOutStream = mBluetoothSocket.getOutputStream(); + if(mUseListenerThread) { + startListening(); + } } catch (IOException e) { catchException(e, ErrorCodesSerialPort.SHIMMERUART_COMM_ERR_PORT_EXCEPTON_OPENING); return false; @@ -257,7 +267,7 @@ public boolean isDisonnected() { @Override public void registerSerialPortRxEventCallback(SerialPortListener serialPortListener) { - + mShimmerSerialEventCallback = serialPortListener; } private void catchException(Exception e, int errorCode) { @@ -267,7 +277,39 @@ private void catchException(Exception e, int errorCode) { } + public void startListening() { + Thread thread = new Thread(new Runnable() { + @Override + public void run() { + try { + while (true) { + // Read data from the input stream + + int availableBytes = mInStream.available(); + if (availableBytes < 1) { + // End of stream + + } else { + // Notify listeners with the read data + mShimmerSerialEventCallback.serialPortRxEvent(availableBytes); + } + + + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + mInStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + }); + thread.start(); + } public BluetoothSocket getBluetoothSocket(){ return mBluetoothSocket; diff --git a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/androidradiodriver/VerisenseBleAndroidRadioByteCommunication.java b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/androidradiodriver/VerisenseBleAndroidRadioByteCommunication.java new file mode 100644 index 00000000..33a89603 --- /dev/null +++ b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/androidradiodriver/VerisenseBleAndroidRadioByteCommunication.java @@ -0,0 +1,254 @@ +package com.shimmerresearch.androidradiodriver; + +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattService; +import android.content.Context; +import android.os.Build; +import android.widget.Toast; + +import com.clj.fastble.callback.BleMtuChangedCallback; +import com.shimmerresearch.driverUtilities.UtilShimmer; +import com.shimmerresearch.verisense.communication.AbstractByteCommunication; + +import java.util.Arrays; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import com.clj.fastble.BleManager; +import com.clj.fastble.callback.BleGattCallback; +import com.clj.fastble.callback.BleNotifyCallback; +import com.clj.fastble.callback.BleWriteCallback; +import com.clj.fastble.data.BleDevice; +import com.clj.fastble.exception.BleException; + +import org.apache.commons.codec.binary.Hex; + +import bolts.TaskCompletionSource; + +/** + * Each instance of this class represents a ble radio that is used to communicate with a verisense device + */ +public class VerisenseBleAndroidRadioByteCommunication extends AbstractByteCommunication { + BleDevice mBleDevice; + protected String TxID = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"; + protected String RxID = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"; + protected String ServiceID = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"; + protected UUID sid; + protected UUID txid; + protected UUID rxid; + String mMac; + String uuid; + + //"DA:A6:19:F0:4A:D7" + //"E7:45:2C:6D:6F:14" + /** + * Initialize a ble radio + * @param mac mac address of the verisense device e.g. d0:2b:46:3d:a2:bb + */ + public VerisenseBleAndroidRadioByteCommunication(String mac) { + mMac = mac; + sid = UUID.fromString(ServiceID); + txid = UUID.fromString(TxID); + rxid = UUID.fromString(RxID); + + } + TaskCompletionSource mTaskConnect = new TaskCompletionSource<>(); + TaskCompletionSource mTaskMTU = new TaskCompletionSource<>(); + + /** + * Connect to the verisense device + */ + @Override + public void connect() { + mTaskConnect = new TaskCompletionSource<>(); + BleManager.getInstance().connect(mMac, new BleGattCallback() { + @Override + public void onStartConnect() { + System.out.println("Connecting"); + } + + @Override + public void onConnectFail(BleDevice bleDevice, BleException exception) { + if (mByteCommunicationListener != null) { + mByteCommunicationListener.eventDisconnected(); + } + } + + @Override + public void onConnectSuccess(BleDevice bleDevice, BluetoothGatt gatt, int status) { + mTaskMTU= new TaskCompletionSource<>(); + BleManager.getInstance().setMtu(bleDevice, 251, new BleMtuChangedCallback() { + @Override + public void onSetMTUFailure(BleException exception) { + System.out.println("MTU Failure"); + + } + + @Override + public void onMtuChanged(int mtu) { + System.out.println("MTU Changed: " + mtu); + mTaskMTU.setResult("MTU Changed: " + mtu); + } + }); + + try { + boolean result = mTaskMTU.getTask().waitForCompletion(3, TimeUnit.SECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + gatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH); + } + + if (mByteCommunicationListener != null) { + mByteCommunicationListener.eventConnected(); + } + mBleDevice = bleDevice; + startServiceS(bleDevice); + System.out.println(bleDevice.getMac() + " Connected"); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + gatt.setPreferredPhy(BluetoothDevice.PHY_LE_2M_MASK, BluetoothDevice.PHY_LE_2M_MASK, BluetoothDevice.PHY_OPTION_NO_PREFERRED); + } + mTaskConnect.setResult("Connected"); + } + + @Override + public void onDisConnected(boolean isActiveDisConnected, BleDevice bleDevice, BluetoothGatt gatt, int status) { + System.out.println(); + } + }); + + + try { + boolean result = mTaskConnect.getTask().waitForCompletion(5, TimeUnit.SECONDS); + Thread.sleep(200); + if (!result) { + System.out.println("Connect fail"); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + /** + * Get services and start notify for characteristics changed + * @param bleDevice BLE device + */ + public void startServiceS(BleDevice bleDevice){ + List services = BleManager.getInstance().getBluetoothGattServices(bleDevice); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + + + for (BluetoothGattService service:services) { + System.out.println(service.getUuid()); + if (service.getUuid().compareTo(sid)==0){ + for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) { + if (characteristic.getUuid().compareTo(txid)==0){ + //newConnectedBLEDevice(bleDevice,characteristic); + CharSequence text = "TXID!"; + } + else if (characteristic.getUuid().compareTo(rxid)==0){ + newConnectedBLEDevice(bleDevice,characteristic); + CharSequence text = "RxID!"; + } + } + } + } + + + } + } + + /** + * Start notify for characteristics changed + * @param bleDevice BLE device + * @param characteristic + */ + public void newConnectedBLEDevice(final BleDevice bleDevice, final BluetoothGattCharacteristic characteristic) { + + + int count = 1; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + BleManager.getInstance().notify( + bleDevice, + characteristic.getService().getUuid().toString(), + characteristic.getUuid().toString(), + new BleNotifyCallback() { + + + @Override + public void onNotifySuccess() { + + } + + @Override + public void onNotifyFailure(BleException exception) { + + } + + @Override + public void onCharacteristicChanged(byte[] data) { + if (mByteCommunicationListener != null) { + try{ + mByteCommunicationListener.eventNewBytesReceived(data); + }catch (Exception e){ + e.printStackTrace(); + } + } + } + }); + } + } + + /** + * Disconnect from the verisense device + */ + @Override + public void disconnect() { + BleManager.getInstance().disconnect(mBleDevice); + } + + /** + * Write bytes to the verisense device + * @param bytes byte array to be written + */ + @Override + public void writeBytes(byte[] bytes) { + BleManager.getInstance().write(mBleDevice, sid.toString(), txid.toString(), bytes, false, new BleWriteCallback() { + @Override + public void onWriteSuccess(int current, int total, byte[] justWrite) { + System.out.println("Write Success " + UtilShimmer.bytesToHexStringWithSpacesFormatted(justWrite)); + } + + @Override + public void onWriteFailure(BleException exception) { + System.out.println("Write Fail"); + } + }); + } + + @Override + public void stop() { + + } + + /** + * Convert mac address to uuid + * @param MacID e.g. d0:2b:46:3d:a2:bb + * @return uuid e.g. 00000000-0000-0000-0000-d02b463da2bb + */ + public String convertMacIDtoUUID(String MacID) { + String uuid = "00000000-0000-0000-0000-"; + return uuid + MacID.replace(":", ""); + } + + /** + * @return uuid e.g. 00000000-0000-0000-0000-d02b463da2bb + */ + public String getUuid() { + return convertMacIDtoUUID(this.mMac); + } +} diff --git a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/tools/FileUtils.java b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/tools/FileUtils.java new file mode 100644 index 00000000..92035b8f --- /dev/null +++ b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/tools/FileUtils.java @@ -0,0 +1,421 @@ +package com.shimmerresearch.tools; + +import android.annotation.SuppressLint; +import android.content.ContentUris; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build; +import android.os.Environment; +import android.provider.DocumentsContract; +import android.provider.MediaStore; +import android.provider.OpenableColumns; +import android.text.TextUtils; +import android.util.Log; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.util.UUID; + +public class FileUtils { + public static String FALLBACK_COPY_FOLDER = "upload_part"; + + private static String TAG = "FileUtils"; + + private static Uri contentUri = null; + + Context context; + + public FileUtils(Context context) { + this.context = context; + } + public enum UriType { + FILE, FOLDER; // These are the constants of the enum + } + @SuppressLint("NewApi") + public String getPath(final Uri uri, UriType uritype) { + // check here to KITKAT or new version + final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; + String selection = null; + String[] selectionArgs = null; + // DocumentProvider + + if (isKitKat) { + // ExternalStorageProvider + + if (isExternalStorageDocument(uri)) { + String docId = ""; + if(uritype.equals(UriType.FOLDER)) { + docId = DocumentsContract.getTreeDocumentId(uri); + } else if(uritype.equals(UriType.FILE)) { + docId = DocumentsContract.getDocumentId(uri); + } + + final String[] split = docId.split(":"); + final String type = split[0]; + + String fullPath = getPathFromExtSD(split); + + if (fullPath == null || !fileExists(fullPath)) { + Log.d(TAG, "Copy files as a fallback"); + fullPath = copyFileToInternalStorage(uri, FALLBACK_COPY_FOLDER); + } + + if (fullPath != "") { + return fullPath; + } else { + return null; + } + } + + + // DownloadsProvider + + if (isDownloadsDocument(uri)) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + final String id; + Cursor cursor = null; + try { + cursor = context.getContentResolver().query(uri, new String[] { + MediaStore.MediaColumns.DISPLAY_NAME + }, null, null, null); + if (cursor != null && cursor.moveToFirst()) { + String fileName = cursor.getString(0); + String path = Environment.getExternalStorageDirectory().toString() + "/Download/" + fileName; + if (!TextUtils.isEmpty(path)) { + return path; + } + } + } finally { + if (cursor != null) + cursor.close(); + } + id = DocumentsContract.getDocumentId(uri); + + if (!TextUtils.isEmpty(id)) { + if (id.startsWith("raw:")) { + return id.replaceFirst("raw:", ""); + } + String[] contentUriPrefixesToTry = new String[] { + "content://downloads/public_downloads", + "content://downloads/my_downloads" + }; + + for (String contentUriPrefix: contentUriPrefixesToTry) { + try { + final Uri contentUri = ContentUris.withAppendedId(Uri.parse(contentUriPrefix), Long.valueOf(id)); + + return getDataColumn(context, contentUri, null, null); + } catch (NumberFormatException e) { + //In Android 8 and Android P the id is not a number + return uri.getPath().replaceFirst("^/document/raw:", "").replaceFirst("^raw:", ""); + } + } + } + } else { + final String id = DocumentsContract.getDocumentId(uri); + + if (id.startsWith("raw:")) { + return id.replaceFirst("raw:", ""); + } + try { + contentUri = ContentUris.withAppendedId( + Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + + if (contentUri != null) + return getDataColumn(context, contentUri, null, null); + } + } + + + // MediaProvider + if (isMediaDocument(uri)) { + final String docId = DocumentsContract.getDocumentId(uri); + final String[] split = docId.split(":"); + final String type = split[0]; + + Log.d(TAG, "MEDIA DOCUMENT TYPE: " + type); + + Uri contentUri = null; + + if ("image".equals(type)) { + contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; + } else if ("video".equals(type)) { + contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; + } else if ("audio".equals(type)) { + contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; + } else if ("document".equals(type)) { + contentUri = MediaStore.Files.getContentUri(MediaStore.getVolumeName(uri)); + } + + selection = "_id=?"; + selectionArgs = new String[] { + split[1] + }; + + + return getDataColumn(context, contentUri, selection, selectionArgs); + } + + if (isGoogleDriveUri(uri)) { + return getDriveFilePath(uri); + } + + if (isWhatsAppFile(uri)) { + return getFilePathForWhatsApp(uri); + } + + if ("content".equalsIgnoreCase(uri.getScheme())) { + if (isGooglePhotosUri(uri)) { + return uri.getLastPathSegment(); + } + + if (isGoogleDriveUri(uri)) { + return getDriveFilePath(uri); + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + // return getFilePathFromURI(context,uri); + return copyFileToInternalStorage(uri, FALLBACK_COPY_FOLDER); + // return getRealPathFromURI(context,uri); + } else { + return getDataColumn(context, uri, null, null); + } + + } + + if ("file".equalsIgnoreCase(uri.getScheme())) { + return uri.getPath(); + } + } else { + if (isWhatsAppFile(uri)) { + return getFilePathForWhatsApp(uri); + } + + if ("content".equalsIgnoreCase(uri.getScheme())) { + String[] projection = { + MediaStore.Images.Media.DATA + }; + Cursor cursor = null; + + try { + cursor = context.getContentResolver() + .query(uri, projection, selection, selectionArgs, null); + int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); + + if (cursor.moveToFirst()) { + return cursor.getString(column_index); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + return copyFileToInternalStorage(uri, FALLBACK_COPY_FOLDER); + } + + private static boolean fileExists(String filePath) { + File file = new File(filePath); + + return file.exists(); + } + + private static String getPathFromExtSD(String[] pathData) { + final String type = pathData[0]; + final String relativePath = File.separator + pathData[1]; + String fullPath = ""; + + + Log.d(TAG, "MEDIA EXTSD TYPE: " + type); + Log.d(TAG, "Relative path: " + relativePath); + // on my Sony devices (4.4.4 & 5.1.1), `type` is a dynamic string + // something like "71F8-2C0A", some kind of unique id per storage + // don't know any API that can get the root path of that storage based on its id. + // + // so no "primary" type, but let the check here for other devices + if ("primary".equalsIgnoreCase(type)) { + fullPath = Environment.getExternalStorageDirectory() + relativePath; + if (fileExists(fullPath)) { + return fullPath; + } + } + + if ("home".equalsIgnoreCase(type)) { + fullPath = "/storage/emulated/0/Documents" + relativePath; + if (fileExists(fullPath)) { + return fullPath; + } + } + + // Environment.isExternalStorageRemovable() is `true` for external and internal storage + // so we cannot relay on it. + // + // instead, for each possible path, check if file exists + // we'll start with secondary storage as this could be our (physically) removable sd card + fullPath = System.getenv("SECONDARY_STORAGE") + relativePath; + if (fileExists(fullPath)) { + return fullPath; + } + + fullPath = System.getenv("EXTERNAL_STORAGE") + relativePath; + if (fileExists(fullPath)) { + return fullPath; + } + + return null; + } + + private String getDriveFilePath(Uri uri) { + Uri returnUri = uri; + Cursor returnCursor = context.getContentResolver().query(returnUri, null, null, null, null); + /* + * Get the column indexes of the data in the Cursor, + * * move to the first row in the Cursor, get the data, + * * and display it. + * */ + int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); + int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE); + returnCursor.moveToFirst(); + String name = (returnCursor.getString(nameIndex)); + String size = (Long.toString(returnCursor.getLong(sizeIndex))); + File file = new File(context.getCacheDir(), name); + try { + InputStream inputStream = context.getContentResolver().openInputStream(uri); + FileOutputStream outputStream = new FileOutputStream(file); + int read = 0; + int maxBufferSize = 1 * 1024 * 1024; + int bytesAvailable = inputStream.available(); + + //int bufferSize = 1024; + int bufferSize = Math.min(bytesAvailable, maxBufferSize); + + final byte[] buffers = new byte[bufferSize]; + while ((read = inputStream.read(buffers)) != -1) { + outputStream.write(buffers, 0, read); + } + Log.e(TAG, "Size " + file.length()); + inputStream.close(); + outputStream.close(); + Log.e(TAG, "Path " + file.getPath()); + Log.e(TAG, "Size " + file.length()); + } catch (Exception e) { + Log.e(TAG, e.getMessage()); + } + + return file.getPath(); + } + + /*** + * Used for Android Q+ + * @param uri + * @param newDirName if you want to create a directory, you can set this variable + * @return + */ + private String copyFileToInternalStorage(Uri uri, String newDirName) { + Uri returnUri = uri; + + Cursor returnCursor = context.getContentResolver().query(returnUri, new String[] { + OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE + }, null, null, null); + + + /* + * Get the column indexes of the data in the Cursor, + * * move to the first row in the Cursor, get the data, + * * and display it. + * */ + int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); + int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE); + returnCursor.moveToFirst(); + String name = (returnCursor.getString(nameIndex)); + String size = (Long.toString(returnCursor.getLong(sizeIndex))); + + File output; + if (!newDirName.equals("")) { + String random_collision_avoidance = UUID.randomUUID().toString(); + + File dir = new File(context.getFilesDir() + File.separator + newDirName + File.separator + random_collision_avoidance); + if (!dir.exists()) { + dir.mkdirs(); + } + output = new File(context.getFilesDir() + File.separator + newDirName + File.separator + random_collision_avoidance + File.separator + name); + } else { + output = new File(context.getFilesDir() + File.separator + name); + } + + try { + InputStream inputStream = context.getContentResolver().openInputStream(uri); + FileOutputStream outputStream = new FileOutputStream(output); + int read = 0; + int bufferSize = 1024; + final byte[] buffers = new byte[bufferSize]; + + while ((read = inputStream.read(buffers)) != -1) { + outputStream.write(buffers, 0, read); + } + + inputStream.close(); + outputStream.close(); + } catch (Exception e) { + Log.e(TAG, e.getMessage()); + } + + return output.getPath(); + } + + private String getFilePathForWhatsApp(Uri uri) { + return copyFileToInternalStorage(uri, "whatsapp"); + } + + private String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { + Cursor cursor = null; + final String column = "_data"; + final String[] projection = { + column + }; + + try { + cursor = context.getContentResolver().query(uri, projection, + selection, selectionArgs, null); + + if (cursor != null && cursor.moveToFirst()) { + final int index = cursor.getColumnIndexOrThrow(column); + return cursor.getString(index); + } + } finally { + if (cursor != null) + cursor.close(); + } + + return null; + } + + private static boolean isExternalStorageDocument(Uri uri) { + return "com.android.externalstorage.documents".equals(uri.getAuthority()); + } + + private static boolean isDownloadsDocument(Uri uri) { + return "com.android.providers.downloads.documents".equals(uri.getAuthority()); + } + + private boolean isMediaDocument(Uri uri) { + return "com.android.providers.media.documents".equals(uri.getAuthority()); + } + + private boolean isGooglePhotosUri(Uri uri) { + return "com.google.android.apps.photos.content".equals(uri.getAuthority()); + } + + public boolean isWhatsAppFile(Uri uri) { + return "com.whatsapp.provider.media".equals(uri.getAuthority()); + } + + private boolean isGoogleDriveUri(Uri uri) { + return "com.google.android.apps.docs.storage".equals(uri.getAuthority()) || "com.google.android.apps.docs.storage.legacy".equals(uri.getAuthority()); + } +} \ No newline at end of file diff --git a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/tools/Logging.java b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/tools/Logging.java index c608326e..9a4825bb 100644 --- a/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/tools/Logging.java +++ b/ShimmerAndroidInstrumentDriver/ShimmerAndroidInstrumentDriver/src/main/java/com/shimmerresearch/tools/Logging.java @@ -43,12 +43,18 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; import java.util.Collection; import java.util.Iterator; +import android.content.Context; +import android.net.Uri; import android.os.Environment; import android.util.Log; +import androidx.documentfile.provider.DocumentFile; + import com.google.common.collect.Multimap; import com.shimmerresearch.android.shimmerService.ShimmerService; import com.shimmerresearch.driver.FormatCluster; @@ -64,7 +70,8 @@ public class Logging { BufferedWriter writer=null; File outputFile; String mDelimiter=","; //default is comma - + + @Deprecated /** * @param myName is the file name which will be used */ @@ -74,7 +81,8 @@ public Logging(String myName){ Log.d("AbsolutePath", root.getAbsolutePath()); outputFile = new File(root, mFileName+".dat"); } - + + @Deprecated public Logging(String myName, String delimiter){ mFileName=myName; mDelimiter=delimiter; @@ -82,7 +90,8 @@ public Logging(String myName, String delimiter){ Log.d("AbsolutePath", root.getAbsolutePath()); outputFile = new File(root, mFileName+".dat"); } - + + @Deprecated /** * Constructor with default file output type (.dat) * @param myName @@ -99,6 +108,7 @@ public Logging(String myName,String delimiter, String folderName){ outputFile = new File(root, mFileName+ "." + ShimmerService.FILE_TYPE.DAT.getName()); } + @Deprecated /** * Constructor to select output file type * @param myName @@ -109,13 +119,44 @@ public Logging(String myName,String delimiter, String folderName){ public Logging(String myName, String delimiter, String folderName, ShimmerService.FILE_TYPE fileType) { mFileName=myName; mDelimiter=delimiter; - File root = new File(Environment.getExternalStorageDirectory() + "/"+folderName); + File root; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { + root = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS+ "/"+folderName); + } else { + root = new File(Environment.getExternalStorageDirectory() + "/"+folderName); //android 13 no longer allows this + } + if(!root.exists()) { - if(root.mkdir()); //directory is created; + if(root.mkdir()){ + System.out.println();//directory is created; + } else { + + } } outputFile = new File(root, mFileName + "." + fileType.getName()); } - + DocumentFile mNewFile = null; + Context mContext = null; + public Logging(Uri treeURI, Context context, String myName, String delimiter, String folderName, ShimmerService.FILE_TYPE fileType) { + mFileName=myName; + mDelimiter=delimiter; + + File root; + + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { + + DocumentFile pickedDir = DocumentFile.fromTreeUri(context, treeURI); + if (fileType.equals(ShimmerService.FILE_TYPE.CSV)) { + mNewFile = pickedDir.createFile("text/comma-separated-values", myName); + } else if (fileType.equals(ShimmerService.FILE_TYPE.DAT)) { + mNewFile = pickedDir.createFile("application/dat", myName+"."+ShimmerService.FILE_TYPE.DAT.toString().toLowerCase()); + } + mContext = context; + } + + + } + /** * This function takes an object cluster and logs all the data within it. User should note that the function will write over prior files with the same name. @@ -126,8 +167,12 @@ public void logData(ObjectCluster objectCluster){ try { if (mFirstWrite==true) { - writer = new BufferedWriter(new FileWriter(outputFile,true)); - + if (mNewFile != null){ + OutputStream outputStream = mContext.getContentResolver().openOutputStream(mNewFile.getUri()); + writer = new BufferedWriter(new OutputStreamWriter(outputStream)); + } else { + writer = new BufferedWriter(new FileWriter(outputFile, true)); + } //First retrieve all the unique keys from the objectClusterLog Multimap m = objectClusterLog.getPropertyCluster(); @@ -154,7 +199,7 @@ public void logData(ObjectCluster objectCluster){ } // write header to a file - writer = new BufferedWriter(new FileWriter(outputFile,false)); + //writer = new BufferedWriter(new FileWriter(outputFile,false)); for (int k=0;k + + + + + +