Waiting for a callback method to be called in Android - java

I am writing an android application which interacts with a sensor using bluetooth and obtains temperature values. I am doing this by calling connectGatt() which is asynchronous and calls a callback once the connection is established. The problem I am facing is that my code has to wait until the connection gets established.
This is the implementation of the method which is being used in the code below.
public boolean connect(final String address)
{
Log.v(LOG_TAG,"IN CONNECT METHOD"+Thread.currentThread().getName());
if(btadapter == null || address == null)
{
Log.v(LOG_TAG,"Unable to get Bluetooth Adapter or Address is not valid");
return false;
}
if(address != null && address.equals(btaddress) && btgatt != null)
{
Log.v(LOG_TAG,"Trying to connect to a bt gatt profile directly");
boolean result = btgatt.connect();
if(result)
return true;
return false;
}
btdevice = btadapter.getRemoteDevice(address);
if(btdevice == null)
{
Log.v(LOG_TAG,"Could not find device.");
return false;
}
btgatt = btdevice.connectGatt(this,false,btgattcallback);
Log.v(LOG_TAG,btgatt.toString());
btaddress = address;
Log.v(LOG_TAG,"Connecting to the device");
btConnectionState = STATE_CONNECTING;
return true;
}
Currently I could solve this problem by writing the following code in a handler thread as I don't want to block the UI thread while waiting for the callback. But I am not convinced with the approach.
if(mLocation != null)
{
connect(btaddress); // Method encapsulates calls to connectGatt method.
while (btConnectionState != STATE_CONNECTED) {
continue;
}
while (!services_discovered) {
continue;
}
//Other Code
I feel that there could be better ways of solving this but couldn't find any on the web. I saw a couple of answers using CountDownLatch and Semaphores but I didn't understand them clearly.
Can anyone help me with understanding how to handle situations like these?
Thank you.

Related

Unable to Read from BLE Device, Write is working (Android)

I am trying to read values from BLE device. Steps I followed:
I am able to discover the BLE device and connect to it.
I am able to find the required characteristic by parsing through the
services and get the GattCharacteristic.
I am able to write a value to the BLE device characteristic and that's
verified.
The properties for that characteristic that I am trying to read the
value from are: READ, WRITE and INDICATE/NOTIFY.
The functions I am using for read and write are as follows:
a) Read function:
public void readCharacteristic(BluetoothGattCharacteristic characteristic)
{
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
boolean status;
Log.w(TAG, "SCBABLe: Writing BLuetooth");
status=mBluetoothGatt.readCharacteristic(characteristic);
if(status) {
Log.w(TAG, "Read Successfully");
}
else
{
Log.w(TAG, "Read Unsuccessfully");
}
}
b) Write function
public void writeCharacteristic(BluetoothGattCharacteristic characteristic)
{
if (mBluetoothAdapter == null || mBluetoothGatt == null)
{
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
boolean status;
Log.w(TAG, "SCBABLe: Writing BLuetooth");
status=mBluetoothGatt.writeCharacteristic(characteristic);
if(status) {
Log.w(TAG, "Write Successfully");
}
else
{
Log.w(TAG, "Write Unuccessfully");
}
}
I am calling the above two from a different activity, after I get the required characteristic(by matching the UUIDs).
//Current Activity Name: DeviceControlActivity
//BluetoothLeService is the other activity where Read and write are defined
private static final DeviceControlActivity holder1 = new DeviceControlActivity();
public BluetoothGattCharacteristic reqChar;
public BluetoothLeService reqService;
private BluetoothLeService mBluetoothLeService;
private void displayGattServices(List<BluetoothGattService> gattServices)
{
if (gattServices == null) return;
String uuid = null;
for (BluetoothGattService gattService : gattServices)
{
uuid = gattService.getUuid().toString();
// Loops through available Characteristics.
for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics)
{
uuid = gattCharacteristic.getUuid().toString();
//SampleGattAttributes.UNKNOWN_CHARACTERISTIC is the hardcoded uuid against which i am checking
if((uuid.equals((SampleGattAttributes.UNKNOWN_CHARACTERISTIC))))
{
holder1.reqChar = gattCharacteristic;
holder1.reqService = mBluetoothLeService;
//Call for write
byte [] byte1= {0x01, 0x10};
holder1.reqChar.setValue(byte1);
holder1.reqService.writeCharacteristic(holder1.reqChar);
//Call for read
holder1.reqService.readCharacteristic(holder1.reqChar);
Result: Read function is returning false and write function is returning true so the value is getting written successfully for the required characteristic.(verified it)
Please, could anyone help and tell why the read is not getting executed? Why is it still returning false when it has Read as property and proper value defined?
The problem is in these few lines.
holder1.reqService.writeCharacteristic(holder1.reqChar);
//Call for read
holder1.reqService.readCharacteristic(holder1.reqChar);
After calling writeCharacteristic, if you do any extra read/write/etc. , the call will not be executed and return false. You have to wait for BluetoothGattCallback.onCharacteristicWrite before doing further operation.
In the Android BLE implementation, the gatt operation calls need to be queued so that only one operation (read, write, etc.) is in effect at a time. So for example, after gatt.readCharacteristic(characteristicX) is called, you need to wait for the gatt callbackBluetoothGattCallback.onCharacteristicRead() to indicate the read is finished. If you initiate a second gatt.readCharacteristic() operation before the previous one completes, the second one will fail (by returning false) This goes for all of the gatt.XXX() operations.
Its a little work, but I think the best solution is to create a command queue for all the gatt operations and run them one at a time. You can use the command pattern to accomplish this.

IabHelper (class) Dispose (method) unhandled exception

I'm trying to implement in-app purchase in my latest android project.
To do so, I'm following this guide.
Everything went smooth until I used the dispose method in order to close any communication with the play store.
What I get is the following error:
Error:(101, 45) error: unreported exception IabAsyncInProgressException; must be caught or declared to be thrown
On the following code segment:
#Override
public void onDestroy() {
super.onDestroy();
//Always unbind the with the store connection, otherwise performance degradation of the device may follow.
if (mHelper != null) mHelper.dispose();
mHelper = null;
}
After digging in the IabHelper class (Java) I found the dispose method.
Here the code of the method:
/**
* Dispose of object, releasing resources. It's very important to call this
* method when you are done with this object. It will release any resources
* used by it such as service connections. Naturally, once the object is
* disposed of, it can't be used again.
*/
public void dispose() throws IabAsyncInProgressException {
synchronized (mAsyncInProgressLock) {
if (mAsyncInProgress) {
throw new IabAsyncInProgressException("Can't dispose because an async operation " +
"(" + mAsyncOperation + ") is in progress.");
}
}
logDebug("Disposing.");
mSetupDone = false;
if (mServiceConn != null) {
logDebug("Unbinding from service.");
if (mContext != null) mContext.unbindService(mServiceConn);
}
mDisposed = true;
mContext = null;
mServiceConn = null;
mService = null;
mPurchaseListener = null;
}
What should I do to resolve this error?
I understand that I should catch and exception but I am not confident enough to change by myself this method in this class.
(Thanks for any help)
After more research I've found that this question was already asked and answered.
Unfortunately the question is still marked as not answered.
Here there is the link to the original question.
The solution is simple:
The file that you can get from the guide are outdated, and should be instead downloaded from github.
In the method onDestroy you should instead use the following code:
#Override
public void onDestroy() {
super.onDestroy();
//Always unbind the connection with the store, otherwise performance degradation of the device may follow.
if (mHelper != null) {
mHelper.disposeWhenFinished();
mHelper = null;
}
}
disposeWhenFinished it's a more elegant solution that works better than a dispose.

Difficulties checking state of boolean

I am trying to check server connectivity on splash screen. I want make if server is online then I want download data else if server does not ping, I need to show error. I am trying to user function for server status check like below
static public boolean isServerReachable(Context context) {
ConnectivityManager connMan = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo netInfo = connMan.getActiveNetworkInfo();
if (netInfo != null && netInfo.isConnected()) {
try {
URL urlServer = new URL("your server url");
HttpURLConnection urlConn = (HttpURLConnection) urlServer.openConnection();
urlConn.setConnectTimeout(3000); //<- 3Seconds Timeout
urlConn.connect();
if (urlConn.getResponseCode() == 200) {
return true;
} else {
return false;
}
} catch (MalformedURLException e1) {
return false;
} catch (IOException e) {
return false;
}
}
return false;
}
but I do not know how can I use this method. I am trying like below code
boolean isServerReachable;
if (isServerReachable=true){
}
else
{
}
But this showing me that boolean isServerReachable; is never used.
if I try like this
boolean isServerReachable;
if(isServerReachable(SplashsActivity.this)){
if (isServerReachable==true){
}
else {
}
}
its showing variable isServerReachable might not been initialized
Can someone please help me what's I am missing ? Maybe this is very simple and foolish question but I am learning yet, so please help me. Thanks
If boolean isServerReachable is a local variables then it don't get default values, they have to be initialized. ... Local variables should be initialized with value before using it .Something like this :boolean isServerReachable = false The compiler complains because local variables are not assigned any value by default.
boolean isServerReachable = false;
if(isServerReachable(SplashsActivity.this)){
if (isServerReachable==true){
}
else {
}
}
It will fix that its showing variable isServerReachable might not been initialized issue.
You can call the method inside if condition -
if(isServerReachable(context_value))
{
}
else
{
}
The code you are currently using (given below) doesn't call the method. It actually assign true value to variable - isServerReachable, which is never used in 'if'or 'else' block
boolean isServerReachable;
if (isServerReachable=true){
}
else
{
}

How to handle com.android.volley.NoConnectionError: java.net.UnknownHostException

I am using Volley for webcalls in my application and everything is working fine and smooth except one state in which somehow my device is not getting Network Connection but checking connection via code is returning true using below code.
public static boolean isNetworkAvailable() {
ConnectivityManager connectivityManager = SessionApplication.getConnectivityManager();
if(connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).getState() == NetworkInfo.State.CONNECTED ||
connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState() == NetworkInfo.State.CONNECTED) {
//we are connected to a network
return true;
}
else
return false;
}
Instead of returning network state false using above code My volley web calls returning me this exception "handle com.android.volley.NoConnectionError: java.net.UnknownHostException".
I checked my internet connection by opening browser in my device and found it is also not working. So i am okay with application behavior but still i need to handle such condition because this is not user friendly user should be prompted a dialog that "Check Your Internet Connection!".
This should be a common issues in Android could any body please help me to give me best approach to handle such cases. Thanks in advance.
Network state is :
This exception indicates the problem in connectivity. In fact you can show some dialog about the connectivity. Overriding the onErrorResponse(VolleyError error) you can do like this -
public void onErrorResponse(VolleyError error) {
Log.d(TAG, error.toString());
if (error instanceof NoConnectionError)
new AlertDialog.Builder(this).setMessage(
"Unable to connect to the server! Please ensure your internet is working!").show();
}
Try this method might help
public boolean isConnectedToInternet(){
connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (connectivityManager != null){
NetworkInfo[] info = connectivityManager.getAllNetworkInfo();
if (info != null){
for (int i = 0; i < info.length; i++){
if (info[i].getState() == NetworkInfo.State.CONNECTED){
return true;
}
}
}
}
return false;
}

How to change the output file of a mediarecorder without stopping the mediarecorder

I have a requirement in my project, where video is being recorded and uploaded to the server, but since mobile networks are not reliable, at the beginning what I decided to do was every 30 secs
stop the recorder
reset the recorder state
retrieve the file written to by the recorder and upload (multipart form data) it in a different thread.
change the outfile of the recorder to a new file based on the hash of the current timestamp.
repeat process every 30 secs
Doing this suits my needs perfectly as each of the 30sec video file sizes are not more than 1MB and upload happens smoothly.
But the problem I am facing is that every time the media recorder stops and starts again there is a delay of about 500ms, so the video that I receive at the server has these 500ms breaks every 30secs which is really bad for my current situation, so I was thinking if it would be possible to just change the file that the recorder is writing to on the fly?
Relevant code:
GenericCallback onTickListener = new GenericCallback() {
#Override
public void execute(Object data) {
int timeElapsedInSecs = (int) data;
if (timeElapsedInSecs % pingIntervalInSecs == 0) {
new API(getActivity().getApplicationContext()).pingServer(objInterviewQuestion.getCurrentAccessToken(),
new NetworkCallback() {
#Override
public void execute(int response_code, Object result) {
// TODO: HANDLE callback
}
});
}
if (timeElapsedInSecs % uploadIntervalInSecs == 0 && timeElapsedInSecs < maxTimeInSeconds) {
if (timeElapsedInSecs / uploadIntervalInSecs >= 1) {
if(stopAndResetRecorder()) {
openConnectionToUploadQueue();
uploadQueue.add(
new InterviewAnswer(0,
objInterviewQuestion.getQid(),
objInterviewQuestion.getAvf(),
objInterviewQuestion.getNext(),
objInterviewQuestion.getCurrentAccessToken()));
objInterviewQuestion.setAvf(MiscHelpers.getOutputMediaFilePath());
initializeAndStartRecording();
}
}
}
}
};
here is initializeAndStartRecording() :
private boolean initializeAndStartRecording() {
Log.i("INFO", "initializeAndStartRecording");
if (mCamera != null) {
try {
mMediaRecorder = CameraHelpers.initializeRecorder(mCamera,
mCameraPreview,
desiredVideoWidth,
desiredVideoHeight);
mMediaRecorder.setOutputFile(objInterviewQuestion.getAvf());
mMediaRecorder.prepare();
mMediaRecorder.start();
img_recording.setVisibility(View.VISIBLE);
is_recording = true;
return true;
} catch (Exception ex) {
MiscHelpers.showMsg(getActivity(),
getString(R.string.err_cannot_start_recorder),
AppMsg.STYLE_ALERT);
return false;
}
} else {
MiscHelpers.showMsg(getActivity(), getString(R.string.err_camera_not_available),
AppMsg.STYLE_ALERT);
return false;
}
}
Here is stopAndResetRecorder:
boolean stopAndResetRecorder() {
boolean success = false;
try {
if (mMediaRecorder != null) {
try {
//stop recording
mMediaRecorder.stop();
mMediaRecorder.reset();
mMediaRecorder.release();
mMediaRecorder = null;
Log.d("MediaRecorder", "Recorder Stopped");
success = true;
} catch (Exception ex) {
if(ex != null && ex.getMessage()!=null && ex.getMessage().isEmpty()){
Crashlytics.log(Log.ERROR, "Failed to stop MediaRecorder", ex.getMessage());
Crashlytics.logException(ex);
}
success = false;
} finally {
mMediaRecorder = null;
is_recording = false;
is_recording = false;
}
}
} catch (Exception ex) {
success = false;
}
Log.d("MediaRecorder", "Success = " + String.valueOf(success));
return success;
}
You can speed it up slightly by not calling the release() method and all of the rest of the destruction that you do in stopAndResetRecorder() (see the documentation for the MediaRecorder state machine).
You also don't need to call both stop() and reset().
You could instead have an intermediate resetRecorder() function which just performed reset() then call initializeAndStartRecording(). When you finish all of your recording, you could then call stopRecorder() which would perform the destruction of your mMediaRecorder.
As I say, this will save you some time, but whether the extra overhead you currently have of destroying and re-initialising the MediaRecorder is a significant portion of the delay I don't know. Give that a try, and if it doesn't fix your problem, I'd be interested to know how much time it did/didn't save.
It seems to me that the setOutputFile calls a native method regarding to MediaRecorder's source, so I don't think there's an easy way to write into seperate files at the same time.
What about uploading it in one chunk at the end, but allow the user to do anything after starting the uploading process? Then the user wouldn't notice how much time the upload takes, and you can notify him later when the upload successed/failed.
[Edit:] Try to stream upload to server, where the server does the chunking mechanism to seperate files. Here you can have a brief explanation how to do so.
Apparently MediaRecorder.setOutputFile() also accepts a FileDescriptor.
So, if you were programming at low level (JNI) you could have represented a process's input stream as a file descriptor and in turn, had that process write to different files when desired. But that would involve managing that native "router" process from java.
Unfortunately, on java API side, you are out of luck.

Categories

Resources