Server-side setup:
Please refer “Setting up the Server” section from README.md file present at https://github.com/sondehealth-samples/score
Before going to through client-side integration we are considering that you have done with server-side setup.
Integration steps for Android :
Step-1 : Get Access token (Authentication Token)
The first step to integrate the sonde services into your mobile app is to get the access token which will authenticate your further requests made from app to Sonde services. To know how to get the access token from Sonde services at backend you can refer this link Respiratory Symptoms Risk API
Below code snipped you can refer to get access token from your backend.
private void getAccessToken(String apiUrl) { BackendApi backendApi = RetrofitClientInstance.getRetrofitInstance().create(BackendApi.class); Call<AccessTokenResponse> call = backendApi.getAuthToken(apiUrl); call.enqueue(new Callback<AccessTokenResponse>() { @Override public void onResponse(Call<AccessTokenResponse> call, Response<AccessTokenResponse> response) { Log.i(TAG, "AccessToken Response : " + response.toString()); AccessTokenResponse accessTokenResponse = response.body(); if (accessTokenResponse != null) { Log.i(TAG, "AccessToken is : " + accessTokenResponse.getAccessToken()); // use this accessToken for further apis call String accessToken = accessTokenResponse.getAccessToken(); } else { Log.e(TAG, "error: code " + response.code()); } } @Override public void onFailure(Call<AccessTokenResponse> call, Throwable t) { Log.e(TAG, "error: " + t); } }); }
Here apiUrl should be your backend api url to get access token
e.g. apiUrl=https://YOUR_BACKEND/authToken (Please note that this url should not be Sonde APIs end point)
onResponse
: Callback method where you will get access token. You should store this received token in secure place, as this token will be used for further request to Sonde. Please note that this access token is only valid for certain time (1 hour), so its your responsibility to re-fetch the token after expiry.
onFailure
: Callback method for failure case
Step-2 : Register User
In this step you have to register user to Sonde platform but this registration should be done through your backend. So you have to call your backend api to register user. You can refer below code snippet to register user.
private void registerUser(String apiUrl) { SignUpRequest signUpRequest = new SignUpRequest("1985", "MALE", "ENGLISH"); BackendApi backendApi = RetrofitClientInstance.getRetrofitInstance().create(BackendApi.class); Call<SignUpResponse> call = backendApi.signUpUser(apiUrl, signUpRequest); call.enqueue(new Callback<SignUpResponse>() { @Override public void onResponse(Call<SignUpResponse> call, Response<SignUpResponse> response) { SignUpResponse signUpResponse = response.body(); if (signUpResponse != null) { // use this userIdentifier to calculate score for wav file String userIdentifier = signUpResponse.getUserIdentifier(); } else { Log.e(TAG, "error: code " + response.code()); } } @Override public void onFailure(Call<SignUpResponse> call, Throwable t) { Log.e(TAG, "error: " + t); } }); }
Step-3 : Get list of measures
In this step you have to get list of all the accessible measures. Any measure from the list can be used to get score for an audio (wav) file.
Request -
URL : SONDE_API_BASE_URL/measures
Method : GET
Headers : Authorization - <Access token received in step 1>
Response : List of measures
Below snipped can be refer to get measures lists
private void getMeasures() { BackendApi backendApi = RetrofitClientInstance.getRetrofitInstance().create(BackendApi.class); Call<MeasureResponse> call = backendApi.getMeasures(accessToken); call.enqueue(new Callback<MeasureResponse>() { @Override public void onResponse(Call<MeasureResponse> call, Response<MeasureResponse> response) { MeasureResponse measureResponse = response.body(); if (measureResponse != null) { measureList = measureResponse.getMeasures(); } else { Log.e(TAG, "error: code " + response.code()); } } @Override public void onFailure(Call<MeasureResponse> call, Throwable t) { Log.e(TAG, "error: " + t); } }); }
Step-4 : Record audio file (wav file)
In step-4 you have to record the audio file (wav file). Please note the currently Sonde only accepts the wav file format, other audio format like MP3, 3GP are not supported.
You can record a audio file in following steps :
i). Request for microphone permission :
To record an audio, you must request for microphone permission on android 6 and above.
Add the permission in manifest
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
Below code snippet you can refer to request for microphone permission.
private void requestAudioPermissions() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { //When permission is not granted by user, show them message why this permission is needed. if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.RECORD_AUDIO)) { Toast.makeText(this, "Please grant permissions to record audio", Toast.LENGTH_LONG).show(); ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, PERMISSIONS_RECORD_AUDIO); } else { // Show user dialog to grant permission to record audio ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, PERMISSIONS_RECORD_AUDIO); } } //If permission is granted, then go ahead for recording audio else if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED) { // start Recording audio; } }
ii). Start recording of audio :
Below snipped you can refer to start recording of audio and writing it to a file.
// starts recording a file at internal memory private void startRecording() { mAudioRecord = new AudioRecord(AUDIO_SOURCE, SAMPLE_RATE_HZ, AUDIO_CHANNEL_CONFIG, AUDIO_FORMAT, AUDIO_BUFFER_SIZE_BYTES); mAudioRecord.startRecording(); mFilePath = getFilesDir().getAbsolutePath() + "/" + System.currentTimeMillis() + ".wav"; recording = true; new Thread(new Runnable() { @Override public void run() { writeAudioDataToFile(mFilePath); } }).start(); } private void writeAudioDataToFile(String filename) { String audioFilename = filename + ".pcm"; OutputStream outputStream = null; try { outputStream = new FileOutputStream(audioFilename); int bufferSizeBytes = AUDIO_BUFFER_SIZE_BYTES; short[] audioBuffer = new short[bufferSizeBytes / 2]; // assumes 16-bit encoding byte[] outputBuffer = new byte[bufferSizeBytes]; int totalBytesRead = 0; while (recording) { int numShortsRead = mAudioRecord.read(audioBuffer, 0, audioBuffer.length); for (int i = 0; i < numShortsRead; i++) { outputBuffer[i * 2] = (byte) (audioBuffer[i] & 0x00FF); outputBuffer[i * 2 + 1] = (byte) (audioBuffer[i] >> 8); audioBuffer[i] = 0; } int numBytesRead = numShortsRead * 2; totalBytesRead += numBytesRead; outputStream.write(outputBuffer, 0, numBytesRead); } OutputStream waveOutputStream = new FileOutputStream(mFilePath); InputStream dataInputStream = new FileInputStream(audioFilename); short numChannels = 1; short sampleSizeBytes = 2; //convert pcm to wav file WaveUtils.pcmToWave(waveOutputStream, dataInputStream, totalBytesRead, numChannels, SAMPLE_RATE_HZ, sampleSizeBytes); } catch (Exception e) { Log.e(TAG, "Error : " + e); }finally { try { if (outputStream != null) { outputStream.close(); } } catch (IOException e) { Log.e(TAG, "Error : " + e); } try { if (mAudioRecord != null) { mAudioRecord.stop(); mAudioRecord.release(); mAudioRecord = null; } } catch (IllegalStateException e) { Log.e(TAG, "Error : " + e); } // delete PCM file File file = new File(audioFilename); file.delete(); } }
Step-5 : Get S3 signed url to upload wav file
After recording audio/wav file, the next step is to get the S3 signed url to upload the wav file.
Request -
URL : SONDE_API_BASE_URL/storage/files
Method : POST
Headers : Authorization - <Access token received in step 1>
Response : S3 signed url and file location
You can refer below snippet to get S3 signed url and file location.
private void getS3SignedUrl( { BackendApi backendApi = RetrofitClientInstance.getRetrofitInstance().create(BackendApi.class); String countryCode = "IN"; //Use your country code Call<S3PathResponse> call = backendApi.getS3FilePath(accessToken, new S3FilePathRequest("wav", countryCode, userIdentifier)); call.enqueue(new Callback<S3PathResponse>() { @Override public void onResponse(Call<S3PathResponse> call, Response<S3PathResponse> response) { S3PathResponse s3PathResponse = response.body(); if (s3PathResponse != null) { String s3FilePath = s3PathResponse.getSignedURL(); //use the received signed url to upload wav file } else { Log.e(TAG, "error: code " + response.code()); } } @Override public void onFailure(Call<S3PathResponse> call, Throwable t) { Log.e(TAG, "error: " + t); } }); }
Step-6 : Upload wav file to S3
Once you got the S3 signed url, next step is to upload wav file to S3 bucket.
To know how to upload file to S3 bucket, you can refer below code snippet
private void uploadFileToS3(final S3PathResponse s3PathResponse, final File filePath) { BackendApi backendApi = RetrofitClientInstance.getRetrofitInstance().create(BackendApi.class); MediaType MEDIA_TYPE_OCTET_STREAM = MediaType.parse("application/octet-stream"); String uploadUrl = s3PathResponse.getSignedURL(); Call<ResponseBody> call = backendApi.uploadFileToS3(uploadUrl, RequestBody.create(MEDIA_TYPE_OCTET_STREAM, filePath)); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { if (response.isSuccessful()) { Log.i(TAG, " : File Uploaded successfully " + filePath.getName()); // request for measure score } else { Log.e(TAG, " : Failed to upload file " + filePath.getName() + " Error code : " + response.code()); } } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { Log.e(TAG, " : Failed to upload file " + filePath.getName() + " Error: " + t); } }); }
Step-7 : Request for score
After successfully uploading the wav file, the last step requires you to request for measure score.
To request for score you should use the below rest api :
Request -
URL : SONDE_API_BASE_URL/inference/scores
Method : POST
Headers : Authorization - <Access token received in step 1>
Response : Measure score
You can refer below code snippet to request for measure score.
private void requestForMeasureScore(String fileLocation, final String measureName, String userIdentifier) { Log.i(TAG, " : fileLocation : " + fileLocation); BackendApi backendApi = RetrofitClientInstance.getRetrofitInstance().create(BackendApi.class); Call<InferenceScoreResponse> call = backendApi.getInferenceScore(accessToken, new InferenceScoreRequest(fileLocation, measureName, userIdentifier)); call.enqueue(new Callback<InferenceScoreResponse>() { @Override public void onResponse(Call<InferenceScoreResponse> call, Response<InferenceScoreResponse> response) { InferenceScoreResponse scoreResponse = response.body(); if (scoreResponse != null) { //show calculated score for measure } else { Log.e(TAG, " : Failed to get score , Error: " + response.code()); Toast.makeText(MainActivity.this, "Could not calculate the score, please try again", Toast.LENGTH_LONG).show(); } } @Override public void onFailure(Call<InferenceScoreResponse> call, Throwable t) { Log.e(TAG, " : Failed to get score , Error: " + t); Toast.makeText(MainActivity.this, "Could not calculate the score, please try again", Toast.LENGTH_LONG).show(); } }); }
For more information, please contact Sonde at support@sondehealth.com.