Skip to content
Open
19 changes: 17 additions & 2 deletions app/src/main/java/org/sil/hearthis/AcceptNotificationHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,24 @@ public void handle(HttpRequest request, HttpResponse response, HttpContext httpC

// Enhance: allow the notification to contain a message, and pass it on.
// The copy is made because the onNotification calls may well remove listeners, leading to concurrent modification exceptions.

// HT-508: to prevent HTA from getting stuck in a bad state when sync is interrupted,
// extract and handle sync status that HT inserted into the notification.
// The notification received from the HearThis PC is an HttpRequest (RFC 7230), like this:
// POST /notify?message=sync_success HTTP/1.1
// Sync status is in the "message" portion. Extract it and send it along.
//
// NOTE: like several things in HearThisAndroid, HttpRequest is deprecated. It will be
// replaced with something more appropriate, hopefully soon.

String s1 = request.getRequestLine().toString();
// We want the part between the two space chars, and after the '='
String s2 = s1.substring(s1.indexOf(' ') + 1, s1.lastIndexOf(' '));
String status = s2.substring(s2.indexOf('=') + 1);

for (NotificationListener listener: notificationListeners.toArray(new NotificationListener[notificationListeners.size()])) {
listener.onNotification("");
listener.onNotification(status);
}
response.setEntity(new StringEntity("success"));
response.setEntity(new StringEntity(status));
}
}
72 changes: 40 additions & 32 deletions app/src/main/java/org/sil/hearthis/SyncActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Bundle;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

import android.os.Handler;
import android.os.Looper;
import android.util.SparseArray;
import android.view.Menu;
import android.view.MenuItem;
Expand All @@ -32,6 +33,8 @@
import java.net.UnknownHostException;
import java.util.Date;
import java.util.Enumeration;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;


public class SyncActivity extends AppCompatActivity implements AcceptNotificationHandler.NotificationListener,
Expand Down Expand Up @@ -125,6 +128,8 @@ public void release() {
// Toast.makeText(getApplicationContext(), "To prevent memory leaks barcode scanner has been stopped", Toast.LENGTH_SHORT).show();
}

// Replacing 'AsyncTask' (deprecated) with 'Executors' and 'Handlers' in this method is inspired by:
// https://stackoverflow.com/questions/58767733/the-asynctask-api-is-deprecated-in-android-11-what-are-the-alternatives
@Override
public void receiveDetections(Detector.Detections<Barcode> detections) {
final SparseArray<Barcode> barcodes = detections.getDetectedItems();
Expand All @@ -145,15 +150,32 @@ public void run() {
// provide some users a clue that all is not well.
ipView.setText(contents);
preview.setVisibility(View.INVISIBLE);
SendMessage sendMessageTask = new SendMessage();
sendMessageTask.ourIpAddress = getOurIpAddress();
sendMessageTask.execute();
ExecutorService executor = Executors.newSingleThreadExecutor();
Handler handler = new Handler(Looper.getMainLooper());
executor.execute(() -> {
// Background work: send UDP packet to IP address given in the QR code.
try {
String ourIpAddress = getOurIpAddress();
String ipAddress = ipView.getText().toString();
InetAddress receiverAddress = InetAddress.getByName(ipAddress);
DatagramSocket socket = new DatagramSocket();
byte[] ipBytes = ourIpAddress.getBytes("UTF-8");
DatagramPacket packet = new DatagramPacket(ipBytes, ipBytes.length, receiverAddress, desktopPort);
socket.send(packet);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
handler.post(() -> {
// Background work done, no associated foreground work needed.
});
});
cameraSource.stop();
cameraSource.release();
cameraSource = null;
}
});

}
}
}
Expand Down Expand Up @@ -216,11 +238,8 @@ private String getOurIpAddress() {
if (inetAddress.isSiteLocalAddress()) {
return inetAddress.getHostAddress();
}

}

}

} catch (SocketException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Expand Down Expand Up @@ -248,7 +267,19 @@ public boolean onOptionsItemSelected(MenuItem item) {
@Override
public void onNotification(String message) {
AcceptNotificationHandler.removeNotificationListener(this);
setProgress(getString(R.string.sync_success));

// HT-508: HearThis PC now includes sync status in its notification to the app.
// Possible sync statuses recognized:
// - success
// - interrupted; handling this should prevent the app from getting into a bad state.
if (message.equals("sync_success")) {
setProgress(getString(R.string.sync_success));
} else if (message.equals("sync_interrupted")) {
setProgress(getString(R.string.sync_interrupted));
} else {
// Should never happen. Not sure what to do here, if anything...
//Log.d("Sync", "onNotification, illegal message: " + message);
}
runOnUiThread(new Runnable() {
@Override
public void run() {
Expand Down Expand Up @@ -285,27 +316,4 @@ public void sendingFile(final String name) {
lastProgress = new Date();
setProgress("sending " + name);
}

// This class is responsible to send one message packet to the IP address we
// obtained from the desktop, containing the Android's own IP address.
private class SendMessage extends AsyncTask<Void, Void, Void> {

public String ourIpAddress;
@Override
protected Void doInBackground(Void... params) {
try {
String ipAddress = ipView.getText().toString();
InetAddress receiverAddress = InetAddress.getByName(ipAddress);
DatagramSocket socket = new DatagramSocket();
byte[] buffer = ourIpAddress.getBytes("UTF-8");
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, receiverAddress, desktopPort);
socket.send(packet);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
}
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<string name="continue_button_caption">Continue</string>
<string name="title_activity_choose_book">ChooseBookActivity</string>
<string name="sync_success">Sync completed successfully!</string>
<string name="sync_interrupted">Sync was interrupted or cancelled</string>
<string name="choose_project">Choose a project</string>
<string name="choose_book">Choose a book</string>
<string name="choose_chapter">Choose a chapter</string>
Expand Down