-
Notifications
You must be signed in to change notification settings - Fork 73
[MRG] Bluetooth Feature for Form Exchanging #260
[MRG] Bluetooth Feature for Form Exchanging #260
Conversation
built alert dialogs for choose different transfer approaches.
added alert dialog while connecting to other device; extracted the common methods to PermissionUtilies.java.
Now, I finished the bluetooth tools for file sending and receiving, I made a demo for sending a short message using bluetooth, you can see this video demo. I created a
I built the whole bluetooth tools with data receiving/sending, but when I refer to the form instances, I saw there are a lot of code related to data processing in @shobhitagarwal1612 @lakshyagupta21 @Chromicle I think I need some suggestions for further updating, thanks. |
@lakshyagupta21 @shobhitagarwal1612 I think it's ready for a review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Things looks good to me, but it needs some code changes related to style and some case handling please take a look.
skunkworks_crow/src/main/java/org/odk/share/services/ReceiverService.java
Outdated
Show resolved
Hide resolved
PersistableBundleCompat extras = new PersistableBundleCompat(); | ||
extras.putLongArray(UploadJob.INSTANCES, instancesToSend); | ||
extras.putBoolean("isBluetooth", false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above.
skunkworks_crow/src/main/java/org/odk/share/tasks/UploadJob.java
Outdated
Show resolved
Hide resolved
skunkworks_crow/src/main/java/org/odk/share/views/ui/send/fragment/FilledFormsFragment.java
Outdated
Show resolved
Hide resolved
@@ -45,6 +49,7 @@ | |||
public static final String INSTANCE_IDS = "instance_ids"; | |||
private static final String INSTANCE_LIST_ACTIVITY_SORTING_ORDER = "instanceListActivitySortingOrder"; | |||
private static final int INSTANCE_LOADER = 1; | |||
private static final String[] TRANSFER_OPTIONS = {"via bluetooth", "via hotspot"}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lets create or resuse the string resources and via
can be removed from the string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
skunkworks_crow/src/main/java/org/odk/share/tasks/DownloadJob.java
Outdated
Show resolved
Hide resolved
skunkworks_crow/src/main/java/org/odk/share/tasks/DownloadJob.java
Outdated
Show resolved
Hide resolved
// show dialog and connected | ||
Timber.d("Start Sending"); | ||
processSelectedFiles(instancesToSend); | ||
|
||
// close connection | ||
socket.close(); | ||
serverSocket.close(); | ||
if (socket != null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When all the form transfer is done newly created bluetoothSocket
also needs to closed for both sender and receiver other it may result in memory leaks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, thanks for reminding. Fixed.
/** | ||
* rescan the bluetooth devices and update the list. | ||
*/ | ||
public void rescan() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rescan logic should not be present in adpater class same for below method addBoundedDevices
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See updated.
.subscribe(bluetoothEvent -> { | ||
switch (bluetoothEvent.getStatus()) { | ||
case CONNECTED: | ||
progressDialog.setMessage("Connected, start downloading..."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Create string resource for this or use the same that is defined for hotspot.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
Also one thing that I noticed while scanning the devices is that it is showing all the devices which was paired before to the device but not available at the moment of scanning, so please take a look at that as well. |
@@ -18,6 +18,10 @@ | |||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> | |||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> | |||
|
|||
<!-- Bluetooth Related --> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The permissions are self explanatory. No need for this comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
} | ||
} | ||
|
||
public interface Listener { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please rename this interface to something less generic like BluetoothReceiverListener
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
} | ||
|
||
public interface Listener { | ||
void foundDevice(BluetoothDevice device); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name of the method should display its intention. So, onDeviceFound
would sound more accurate. Comments?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactored.
skunkworks_crow/src/main/java/org/odk/share/bluetooth/BluetoothUtils.java
Show resolved
Hide resolved
/** | ||
* Checking if current device supports bluetooth. | ||
*/ | ||
public static boolean isSupportBluetooth() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Method name should be isBluetoothSupported
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed.
Intent intent = new Intent(getActivity(), SendActivity.class); | ||
Intent hotspotIntent = new Intent(getActivity(), HpSenderActivity.class); | ||
Intent bluetoothIntent = new Intent(getActivity(), BtSenderActivity.class); | ||
setupSendingIntent(hotspotIntent); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
setup should happen after the TRANSFER_OPTION has been selected to prevent extra initialisation of the other intent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
startActivity(hotspotIntent); | ||
getActivity().finish(); | ||
} | ||
break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switch cases have most of their code common. Please reuse the code.
android:gravity="center_vertical" | ||
android:textSize="12sp" | ||
tools:text="default device address" /> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove blank line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
tools:text="default device address" /> | ||
|
||
</LinearLayout> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same as above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
@lakshyagupta21 @shobhitagarwal1612 I have improved the code as your suggested, please have a new round review and make sure the code is good to merge, thanks. I have tested the new feature using sender/receiver with success:
|
skunkworks_crow/src/main/java/org/odk/share/bluetooth/BluetoothReceiver.java
Outdated
Show resolved
Hide resolved
if (bluetoothSocket.isConnected()) { | ||
rxEventBus.post(new BluetoothEvent(BluetoothEvent.Status.CONNECTED)); | ||
} | ||
if (bluetoothSocket.isConnected()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this can be replaced with else
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this should go in else because previous if block says it to connect so I think this if
stmt check can be removed and we can directly write
rxEventBus.post(new BluetoothEvent(BluetoothEvent.Status.CONNECTED));
as it is expected that
if (!bluetoothSocket.isConnected()) {
bluetoothSocket.connect();
}
will ensure that bluetooth is connected and if by any chance it fails then it should throw some error or callback that we need to handle explicitly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agreed with @lakshyagupta21 , just removing the if-else
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is bluetoothSocket.connect();
a synchronous event? Are there any callbacks for bluetooth connected or disconnected? If not then we need to fix it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Second problem is that we shouldn't establish bluetooth socket connection within DownloadJob
. DownloadJob's purpose is to start download task and it shouldn't be doing anything else. This would testing the code in the later part difficult.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is
bluetoothSocket.connect();
a synchronous event? Are there any callbacks for bluetooth connected or disconnected? If not then we need to fix it.
This method is synchronous yes, and if the connection cannot be established, it will throw an IO Exception. So we don't have to fix that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Second problem is that we shouldn't establish bluetooth socket connection within
DownloadJob
. DownloadJob's purpose is to start download task and it shouldn't be doing anything else. This would testing the code in the later part difficult.
An issue is, we cannot pass an object as a parameter into our jobs, I tried to establish the socket connection in the activity but it seems the job is not relay on that. Besides, the socket and stream for hotspot are created in the job as well, so I think it doesn't matter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@shobhitagarwal1612 Can you suggest a forward, I tried the workarounds but I'm not able to think one because DownloadJob needs socket objects and we cannot pass the socket objects directly in the DownloadJob
and UploadJob
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay. I'll give it a try and post it here.
I notice an issue when we are trying to send form instances, using two Google Nexus 6P (android 8.0.0 and android 8.0.1): The progress will stuck when we send form instance(s), I cannot use hotspot method to verify where this issue comes from (since wifi cannot be established in android 8.0.1, the hotspot cannot be turned on and the QR Code didn't show up either), so can you help me with that issue? @lakshyagupta21 @shobhitagarwal1612 I don't know if you have notice the android 8+ issue with hotspot, and I want to validate if the issue comes from here and fix that. |
@huangyz0918 I'm still not sure about this PR whether it will break anything or not, and I need some more time to test it and I know that some of your tasks are dependent on this PR and @shobhitagarwal1612 is also thinking about solving the above issue. So we can merge this PR in some intermediate branch and then you can work considering that as your development branch. This way you'll get unblocked. |
Good idea to merge that into a |
I think merging into a |
|
||
boolean receiverFound = false; | ||
for (ShadowApplication.Wrapper wrapper : registeredReceivers) { | ||
if (!receiverFound) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its always going to be false first so whenever it gets inside the for each loop then the receiver will be assigned, then there is no use of iterating over registered receivers.
Anyways, the code requires clean up that we can do later.
@huangyz0918 @shobhitagarwal1612 Merging this PR, I did some testing on Bluetooth form transfer from Android 7 to Android 9, but I was not able to transfer the other way. And there were some issues related to UI/UX as well I think it is not in parity with the Hotspot functionality, like
|
I think you should wait longer since the bluetooth classic takes a very long time to discovery some other devices. I'm not sure how to improve that, any suggestions? @lakshyagupta21 |
I can fix this, and I'll open an issue for that. |
Closes #253 #265
What has been done to verify that this works as intended?
I have tested it using my Huawei Nexus 6P (Android 8.0) and Meizu M5 (Android 6.0).
Why is this the best possible solution? Were any other approaches considered?
Now, we are going to build a bluetooth transfer feature. Using bluetooth, we can share form instances between two devices. For bluetooth BLE and bluetooth classic, I choose bluetooth classic because bluetooth classic is compatible with more devices (BLE can only supports device with Android Version >= 4.3).
I'm going to introduce the basic bluetooth tools for ODK Skunkworks-Crow in the PR, The basic tools can be divides into two parts, the bluetooth client and bluetooth server. For data transmission, we need one device act as a bluetooth client and another as a server. If one is not ready, the bluetooth transmission will failed. So I want to build an alert dialog to ask which method our user would like to use, the Wifi Hotspot? or the Bluetooth? Only two device choose the same approach, the connection can be established.
For building this new feature, we have several tasks,
BluetoothActivity.java
for showing scanning results and device list.HpSenderActivity.java
andBtSenderActivity.java
)How does this change affect users? Describe intentional changes to behavior and behavior that could have accidentally been affected by code changes. In other words, what are the regression risks?
It needs tests with new features and make sure the old features work well.
Before submitting this PR, please make sure you have:
./gradlew checkCode
and confirmed all checks still pass OR confirm CircleCI build passes