Commit 3603a946 authored by Brian Lee's avatar Brian Lee
Browse files

last changes i guess

parent 0170f810
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
\ No newline at end of file
[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":1,"versionName":"1.0","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]
\ No newline at end of file
......@@ -35,7 +35,8 @@
android:theme="@style/AppTheme"
android:usesCleartextTraffic="true">
<activity android:name=".MainActivity"
android:theme="@style/AppTheme.NoActionBar">
android:theme="@style/AppTheme.NoActionBar"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
......
......@@ -14,7 +14,6 @@ import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
......@@ -26,7 +25,6 @@ import java.util.Date;
import java.sql.Timestamp;
import java.util.concurrent.TimeUnit;
import androidx.annotation.NonNull;
public class BleService implements IBleService {
......@@ -67,7 +65,6 @@ public class BleService implements IBleService {
if (advtimes < 3) {
mHandler.postDelayed(advtime, 1000);
} else {
Log.d("advtime","deaaa");
bluetoothDeviceArrayList.clear();
stopAction();
startScan();
......@@ -96,34 +93,7 @@ public class BleService implements IBleService {
}
else{
/*
//sendContent = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Timestamp(new Date().getTime()));
sendContent = new SimpleDateFormat("hh:mm:ss").format(new Timestamp(new Date().getTime()));
//sendContent="short";
if (!isAdversting) {
mBluetoothAdapter.stopLeScan(callback);
startAction(sendContent + ";" + id);
updateData(true,System.currentTimeMillis() + ";" + sendContent);
input.setText("");
mHandler.postDelayed(advtime,1000);
}
else {
stopAction();
bluetoothDeviceArrayList.clear();
if (advtimes > 0){
mHandler.removeCallbacks(advtime);
}
advtimes = 0;
startAction(sendContent + ";" + id); // last time
updateData(true,System.currentTimeMillis()+";"+sendContent);
input.setText("");
mHandler.postDelayed(advtime,1000);
}
scanGeneratorCount=0;
scanGeneratorHandler.postDelayed(scanGenerator, 1000);
*/
String sendContent = new SimpleDateFormat("hh:mm:ss").format(new Timestamp(new Date().getTime()));
sendMessage(sendContent);
scanGeneratorCount=0;
......@@ -154,9 +124,7 @@ public class BleService implements IBleService {
} catch (JSONException e) {
}
if (serviceData != null && !serviceData.equals("")) {
String old_id="";
String data = serviceData.substring(4); // get everything after the first 4 characters of manufacturer data
// the first four characters is our manufacturer ID I'm assuming
if (chatRecords.size() > 0 && chatRecords.get(chatRecords.size() - 1).getRawData().equals(hexStr2Str(data))) {
......@@ -170,14 +138,12 @@ public class BleService implements IBleService {
if(null != bean)
updateData(bean);
bluetoothDeviceArrayList.clear(); // why do we clear the device list? do we just want to remove the current device?
old_id = bean.id;
}
stopScan();
if (!isAdversting) {
if(old_id!=""){startAction(hexStr2Str(data), old_id);}
else{startAction(hexStr2Str(data));}
startAction(hexStr2Str(data));
mHandler.postDelayed(advtime, 1000);
}
}
......@@ -331,18 +297,23 @@ public class BleService implements IBleService {
return new String(bytes);
}
//id represents optional parameter for ID of phone that last sent message that current phone is repropogating
private void startAction(@NonNull String v, String... id) {
private void startAction(@NonNull String v) {
isAdversting = true;
byte[] broadcastData = v.getBytes();
mBluetoothLeAdvertiser.startAdvertising(createAdvSettings(true, 0), createAdvertiseData(broadcastData), mAdvertiseCallback);
if(id.length>0){ //for propogated messages
new PostRequest().execute(v,"23.34","23.34",getId(), id[0]);
if(v.split(";")[1].equals(getId())){
mBluetoothLeAdvertiser.startAdvertising(createAdvSettings(true, 0), createAdvertiseData(broadcastData), mAdvertiseCallback);
new PostRequest().execute(v,"23.34","23.34",getId());
}
else{
new PostRequest().execute(v,"23.34","23.34",getId());
new PostRequest().execute(v,"23.34","23.34",getId(),v.split(";")[1]);
Log.d("logstatus","prpogate");
}
}
private void stopAction() {
......
package yuwang.ble_2019;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.le.AdvertiseCallback;
import android.bluetooth.le.AdvertiseData;
import android.bluetooth.le.AdvertiseSettings;
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.content.SharedPreferences;
import android.os.Handler;
import android.os.ParcelUuid;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.sql.Timestamp;
import java.util.concurrent.TimeUnit;
import androidx.annotation.NonNull;
public class BleService implements IBleService {
private BluetoothAdapter mBluetoothAdapter;
private HashMap<String, BluetoothDevice> bluetoothDeviceArrayList = new HashMap<>();
private BluetoothLeAdvertiser mBluetoothLeAdvertiser; // BLE广播
private List<ChatBean> chatRecords = new ArrayList<>();
private boolean isAdversting = false;
private Handler mHandler = new Handler();
private int advtimes = 0;
private SharedPreferences sharedPref;
private static String user_id_key = "user_id";
private static final String BLUETOOTH_GATT_ID = "0000ae8f-0000-1000-8000-00805f9b34fb";
// read more about bluetooth gatt stuff here:
// https://www.novelbits.io/bluetooth-gatt-services-characteristics/
// magic number alert - I am not sure why we settled on 'ae8f' as our special attribute type
private static final int MANUFACTURER_ID = 0x01AC;
// read more about this here:
// https://www.silabs.com/community/wireless/bluetooth/knowledge-base.entry.html/2017/11/14/bluetooth_advertisin-zCHh
// magic number alert - how did we pick 0x01AC for the manufacturer ID here?
private AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback() {
@Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
Log.i("start", "BLE Broadcasting Started");
}
};
private List<Adapters.PostAdapter> beanListeners = new ArrayList<>();
// a thread run on the back that automatically check for the new messages
// this gets run every time the user sends a message
private Runnable advtime = new Runnable() {
@Override
public void run() {
advtimes++;
if (advtimes < 3) {
mHandler.postDelayed(advtime, 1000);
} else {
Log.d("advtime","deaaa");
bluetoothDeviceArrayList.clear();
stopAction();
startScan();
advtimes = 0;
}
}
};
//for some reason, scangenerator can't be started directly
//it is done here
public void startGenerator(){
scanGeneratorHandler.post(scanGenerator);
}
//generates and sends messages
public int scanGeneratorCount=0;
public Handler scanGeneratorHandler = new Handler();
public Runnable scanGenerator = new Runnable() {
@Override
public void run() {
scanGeneratorCount++;
if(scanGeneratorCount < 60){
Log.d("testabc",Integer.toString(scanGeneratorCount));
scanGeneratorHandler.postDelayed(scanGenerator,1000);
}
else{
/*
//sendContent = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Timestamp(new Date().getTime()));
sendContent = new SimpleDateFormat("hh:mm:ss").format(new Timestamp(new Date().getTime()));
//sendContent="short";
if (!isAdversting) {
mBluetoothAdapter.stopLeScan(callback);
startAction(sendContent + ";" + id);
updateData(true,System.currentTimeMillis() + ";" + sendContent);
input.setText("");
mHandler.postDelayed(advtime,1000);
}
else {
stopAction();
bluetoothDeviceArrayList.clear();
if (advtimes > 0){
mHandler.removeCallbacks(advtime);
}
advtimes = 0;
startAction(sendContent + ";" + id); // last time
updateData(true,System.currentTimeMillis()+";"+sendContent);
input.setText("");
mHandler.postDelayed(advtime,1000);
}
scanGeneratorCount=0;
scanGeneratorHandler.postDelayed(scanGenerator, 1000);
*/
String sendContent = new SimpleDateFormat("hh:mm:ss").format(new Timestamp(new Date().getTime()));
sendMessage(sendContent);
scanGeneratorCount=0;
scanGeneratorHandler.postDelayed(scanGenerator, 1000);
}
}
};
// Callback reporting an LE device found during a device scan initiated by the BluetoothAdapter#startLeScan function.
private final BluetoothAdapter.LeScanCallback callback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
// Do we check the list first because this callback is run in background each time a device is found -
// so say we find a device, then start decoding the message
// then if that same device gets found again, we don't want to decode the message twice?
// Is that what this does?
// If that is what it does, we probably want a thread lock on accessing bluetoothDeviceArrayList
if (!bluetoothDeviceArrayList.containsKey(device.getAddress())) {
bluetoothDeviceArrayList.put(device.getAddress(), device);
JSONObject scanjson = Tools.decodeAdvData(scanRecord); // To see how android does this, checkout the ScanRecord class
if (scanjson == null) {
return;
}
String serviceData = null;
try {
serviceData = scanjson.getString(Tools.MANUFACTURER_DATA);
} catch (JSONException e) {
}
if (serviceData != null && !serviceData.equals("")) {
String old_id=""; //the ID of the last recieving phone
String message; //the message WITHOUT the recieving phone
String data = serviceData.substring(4); // get everything after the first 4 characters of manufacturer data
// the first four characters is our manufacturer ID I'm assuming
if (chatRecords.size() > 0 && chatRecords.get(chatRecords.size() - 1).getRawData().equals(hexStr2Str(data))) {
// if this message is the same as the last one we received, don't update the messages
bluetoothDeviceArrayList.clear(); // why do we clear the device list? do we just want to remove the current device?
return;
} else {
// update the messages view
String realData = hexStr2Str(data);
ChatBean bean = ChatBean.extractBeanFromRaw(realData, false);
if(null != bean)
updateData(bean);
bluetoothDeviceArrayList.clear(); // why do we clear the device list? do we just want to remove the current device?
old_id = bean.id;
message = bean.message;
}
stopScan();
if (!isAdversting) {
//getID() replaces the last Id with the braodcasting phone's ID, while keeping the old ID for the server
//propogation code disabled for alpha testing
if(old_id!=""){startAction(message+";" + getId(), old_id);}
//else{startAction(hexStr2Str(data));}
else{startAction(message + ";" + getId());}
mHandler.postDelayed(advtime, 1000);
}
}
Log.d("ble", "run: scanning...");
}
}
};
/*
Other interesting information:
android batch scan-
https://stackoverflow.com/questions/27040086/onbatchscanresults-is-not-called-in-android-ble
https://developer.radiusnetworks.com/2014/10/28/android-5.0-scanning.html
interesting stuff about bluetooth LE changes over time:
https://github.com/NordicSemiconductor/Android-Scanner-Compat-Library/blob/master/README.md
*/
/*
At a high level:
How this works right now: we use the data we can set when advertising our BLE device to send/receive message content.
To send messages, we advertise ourselves, with our message as the manufacturer description.
To receive messages, we look for BLE devices with our UUID, and we decode the manufacturer description and that becomes the text
*/
/*
Me trying to get a handle on the stuff that runs in the background:
1) User opens app
2) onCreate() function runs
2a) We init the view stuff (MyAdapter)
2b) We check for bluetooth permission
3) startScan() is called
This looks for devices that are advertising with our gatt UUID
When devices are found, the scancallback is called
4) scan callback, for each device found:
decode messages, update message view
stop scanning - kills the callback (what if a second device had been found before the callback is killed?)
start advertising the message just received -- call startAction()
run advtime in the background
5) startAction:
starts advertising our device for others to connect to, with the message as the manufacturer data
6) advtime:
after 3 seconds, stop advertising, and startscanning (go back to #3)
when a user sends a message in the app, any scanning or advertising is stopped and we skip to
step #5, advertising the message the user just sent
Possible bug in this method:
We can only advertise one message at a time.
If two messages are received close together, the app will never transmit the second message
And the first message will stop being transmitted forever
We need to come up with a more advanced protocol for sharing messages
Could involve actually making a connection over bluetooth LE, instead of just advertising and scanning
questions we need to answer-
can you scan and advertise at same time? (need proof in documentation)
can you scan/advertise while being connected to a device?
bluetooth seems to support this, but check with android docs
*/
public BleService(BluetoothAdapter bluetoothAdapter,
BluetoothLeAdvertiser bluetoothLeAdvertiser,
SharedPreferences sharedPref) {
mBluetoothAdapter = bluetoothAdapter;
mBluetoothLeAdvertiser = bluetoothLeAdvertiser;
this.sharedPref = sharedPref;
}
public String getId() {
return sharedPref.getString(user_id_key, "");
}
public void saveId(String id) {
SharedPreferences.Editor editor = sharedPref.edit();
editor.putString(user_id_key, id);
editor.commit();
}
public boolean isBleReady() {
return null != mBluetoothAdapter && null != mBluetoothLeAdvertiser;
}
public void addListener(Adapters.PostAdapter postAdapter) {
beanListeners.add(postAdapter);
}
public void removeListener(Adapters.PostAdapter postAdapter) {
beanListeners.remove(postAdapter);
}
public List<ChatBean> getChatRecords() {
return Collections.unmodifiableList(chatRecords);
}
public void sendMessage(String input) {
ChatBean content = new ChatBean(input, getId(), true);
if(isBleReady()) {
try{
if (!isAdversting) { // if we're not advertising ourselves, then...
// we are scanning for other devices. so stop scanning
mBluetoothAdapter.stopLeScan(callback);
} else {
// stop advertising ourselves, and reset advtimes
stopAction();
bluetoothDeviceArrayList.clear();
if (advtimes > 0) {
mHandler.removeCallbacks(advtime);
}
advtimes = 0;
}
// start advertising our message - content + id
// id is a random number 0 - 999 used to identifier sender
startAction(content.getRawData());
mHandler.postDelayed(advtime, 1000); // start the advertise thread
} catch (Exception e) {
Log.e("BLE ERROR", e.getMessage());
}
}
// update the chat view with our own message
updateData(content);
}
// we should be using the non-deprecated scanner class
public void startScan() {
if(isBleReady()) {
UUID[] serviceUuids = new UUID[]{UUID.fromString(BLUETOOTH_GATT_ID)};
mBluetoothAdapter.startLeScan(serviceUuids, callback);
}
}
public void stopScan() {
if(isBleReady()) {
mBluetoothAdapter.stopLeScan(callback);
}
}
// Tool.
private static String hexStr2Str(@NonNull String hexStr) {
String str = "0123456789ABCDEF";
char[] hexs = hexStr.toCharArray();
byte[] bytes = new byte[hexStr.length() / 2];
int n;
for (int i = 0; i < bytes.length; i++) {
n = str.indexOf(hexs[2 * i]) * 16;
n += str.indexOf(hexs[2 * i + 1]);
bytes[i] = (byte) (n & 0xff);
}
return new String(bytes);
}
//id represents optional parameter for ID of phone that last sent message that current phone is repropogating
private void startAction(@NonNull String v, String... id) {
isAdversting = true;
byte[] broadcastData = v.getBytes();
if(id.length>0){ //sends connection
new PostRequest().execute(v,"23.34","23.34",getId(), id[0]);
}
else{//option for first time send
new PostRequest().execute(v,"23.34","23.34",getId());
}
//disables re-propogation
if(id.length==0){
mBluetoothLeAdvertiser.startAdvertising(createAdvSettings(true, 0), createAdvertiseData(broadcastData), mAdvertiseCallback);
}
}
private void stopAction() {
isAdversting = false;
mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback);
}
// Configure the advertisement setting
private AdvertiseSettings createAdvSettings(boolean connectable, int timeoutMillis) {
AdvertiseSettings.Builder mSettingsbuilder = new AdvertiseSettings.Builder();
mSettingsbuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY);
mSettingsbuilder.setConnectable(true);
mSettingsbuilder.setTimeout(0);
AdvertiseSettings mAdvertiseSettings = mSettingsbuilder.build();
return mAdvertiseSettings;
}
// Making advertising packet
private AdvertiseData createAdvertiseData(byte[] data) {
// max length of data is 31 bytes
AdvertiseData.Builder mDataBuilder = new AdvertiseData.Builder();
mDataBuilder.addManufacturerData(MANUFACTURER_ID, data);
mDataBuilder.addServiceUuid(ParcelUuid.fromString(BLUETOOTH_GATT_ID));
// could we squeeze some more bytes our with the service data?
//mDataBuilder.addServiceData(uuid, bytes);
// how can we find out what the maximum # of bytes is for service data
AdvertiseData mAdvertiseData = mDataBuilder.build();
return mAdvertiseData;
}
// Update the new message (either sent or received message) to screen
private void updateData(ChatBean chatBean) {
// if we're not sending, and it's not a message we sent in the past
if (!chatBean.isSentByUser) {
if (!getId().equals(chatBean.id))
chatRecords.add(chatBean);
} else {// if we are sending, just send it
chatRecords.add(chatBean);
}
notifyListeners(chatRecords);
}
public void notifyListeners(List<ChatBean> metaData) {
chatRecords = metaData;
for (Adapters.PostAdapter postAdapter : beanListeners) {
postAdapter.notifyDataSetChanged();
}
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment