I'm working with Bluetooth LE devices and I was thinking about my current approach and best practices. Currently I have an activity which handles the connection and the GattCallbacks and I decided to restructure the code to get an better overview and maintainability cause its quite messy actually.
I found the BleManager from NordicSemiconductor https://github.com/NordicSemiconductor/Android-BLE-Library/
It's an abstraction of the basic steps for connecting with a BLE device and it handles the GattCallbacks + providing an appropriate interface to use it from a service or a ViewModel.
I'd like to use the ViewModel approach but I'm not so familiar with MVC, MVP, MVVM patterns and there are some questions that I still can't reply
This class is extending the BleManager (BlinkyManager.java)
It shows how to make use of the BleManager so I adopted the class and called it ECountBleManager
.
EDIT:
The last 6 days I did reaearches especially facing the MVVM pattern and the Architecture Components. Unfortunately there are still a lot of questions that I can't reply myself. But I really want to get better so I made a draft of my current concept. I hope you can help me answering my questions and improving my project.
I'm especially interested in best practices.
Here is my draft:
And here are my class implementations:
ECountActivity.java
public class ECountActivity extends AppCompatActivity {
private ECountViewModel viewModel;
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.detail_view);
// hide magnifier icon
GifImageView customLoader = findViewById(R.id.progressBar);
customLoader.setVisibility(View.GONE);
// Get additional data from previous activity
final BluetoothDevice device = getIntent().getParcelableExtra("device");
initViewModel();
viewModel.connect(device);
}
private void initViewModel() {
viewModel = ViewModelProviders.of(this).get(ECountViewModel.class);
subscribeDataStreams(viewModel);
}
private void subscribeDataStreams(ECountViewModel viewModel) {
viewModel.isDeviceReady().observe(this, deviceReady -> openOptionsFragment());
viewModel.isConnected().observe(this, status -> {
// Todo: ...
});
}
private void openOptionsFragment() {
// load options fragment
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.contentFragment, new OptionsFragment());
ft.commitNow();
}
}
OtaFragment.java
public class OtaFragment extends Fragment implements FolderChooserDialog.FolderCallback,
FileChooserDialog.FileCallback {
private Button partialOtaButton;
private Button fullOtaButton;
private Button submitButton;
private SeekBar mtuSeekBar;
private EditText mtuInput;
private LinearLayout stacklayout;
private Button browseAppButton;
private TextView folderPathText;
private TextView appFileNameText;
private MaterialDialog otaPrepareDialog;
private MaterialDialog otaProgressDialog;
private ECountViewModel viewModel;
private OtaViewModel otaViewModel;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initViewModel();
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// inflate the layout for this fragment
View view = inflater.inflate(R.layout.ota_fragment, container, false);
initViews(view);
return view;
}
@Override
public void onFolderSelection(@NonNull FolderChooserDialog dialog, @NonNull File folder) {
final String otaFolderPath = folder.toString();
otaViewModel.setOtaFolderPath(otaFolderPath);
folderPathText.setText(otaFolderPath.substring(otaFolderPath.lastIndexOf("/")));
// enable app browse
browseAppButton.setClickable(true);
browseAppButton.setEnabled(true);
}
@Override
public void onFolderChooserDismissed(@NonNull FolderChooserDialog dialog) {}
@Override
public void onFileSelection(@NonNull FileChooserDialog dialog, @NonNull File file) {
final String otaAppFilePath = file.toString();
otaViewModel.setOtaAppFilePath(otaAppFilePath);
appFileNameText.setText(otaAppFilePath.substring(otaAppFilePath.lastIndexOf("/")));
// enable submitButton button
submitButton.setClickable(true);
submitButton.setEnabled(true);
}
@Override
public void onFileChooserDismissed(@NonNull FileChooserDialog dialog) {}
private void subscribeDataStreams(ECountViewModel viewModel) {
viewModel.isOtaMode().observe(this, otaMode -> {
otaPrepareDialog.dismiss();
initOtaProgressDialog();
otaProgressDialog.show();
// Todo: how can i get mtu?
viewModel.requestMtu(512);
});
}
private void initViewModel() {
viewModel = ViewModelProviders.of(getActivity()).get(ECountViewModel.class);
otaViewModel = ViewModelProviders.of(getActivity()).get(OtaViewModel.class);
subscribeDataStreams(viewModel);
}
private void initViews(View view) {
// get resources
final Button browseFolderButton = view.findViewById(R.id.browseFolder);
final Button cancelButton = view.findViewById(R.id.ota_cancel);
final SeekBar prioritySeekBar = view.findViewById(R.id.connection_seekBar);
partialOtaButton = view.findViewById(R.id.radio_ota);
fullOtaButton = view.findViewById(R.id.radio_ota_full);
browseAppButton = view.findViewById(R.id.browseApp);
folderPathText = view.findViewById(R.id.folderPathText);
appFileNameText = view.findViewById(R.id.appFileNameText);
stacklayout = view.findViewById(R.id.stacklayout);
submitButton = view.findViewById(R.id.ota_proceed);
mtuSeekBar = view.findViewById(R.id.mtu_seekBar);
mtuInput = view.findViewById(R.id.mtu_value);
// set initial states
mtuSeekBar.setMax(512-23);
mtuSeekBar.setProgress(244);
prioritySeekBar.setMax(2);
prioritySeekBar.setProgress(1);
browseAppButton.setClickable(false);
browseAppButton.setEnabled(false);
submitButton.setClickable(false);
submitButton.setEnabled(false);
mtuInput.setOnEditorActionListener((v, actionId, event) -> {
final Editable mtuText = mtuInput.getText();
if (mtuText != null) {
int mtu = Integer.valueOf(mtuText.toString());
if (mtu < 23) mtu = 23;
if (mtu > 512) mtu = 512;
mtuSeekBar.setProgress(mtu);
viewModel.setMtu(mtu);
}
return false;
});
mtuSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onStartTrackingTouch(SeekBar seekBar) {}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
mtuInput.setText(String.valueOf(progress));
viewModel.setMtu(progress);
}
});
prioritySeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener(){
@Override
public void onStartTrackingTouch(SeekBar seekBar) {}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
viewModel.setPriority(progress);
}
});
browseFolderButton.setOnClickListener(v -> new FolderChooserDialog.Builder(getActivity())
.chooseButton(R.string.positiveTextChoose)
.tag("#folder")
.show(getChildFragmentManager()));
browseAppButton.setOnClickListener(v -> new FileChooserDialog.Builder(getActivity())
.initialPath(otaViewModel.getOtaFolderPath())
.extensionsFilter(".ebl")
.tag("#app")
.show(getChildFragmentManager()));
cancelButton.setOnClickListener(v -> Log.i("ota", "cancelButton"));
submitButton.setOnClickListener(v -> {
// disable OTA submitButton button
submitButton.setClickable(false);
submitButton.setEnabled(false);
// init OTA process
viewModel.initOtaMode();
// show OTA preparing dialog
otaPrepareDialog.show();
});
fullOtaButton.setOnClickListener(v -> {
stacklayout.setVisibility(View.VISIBLE);
partialOtaButton.setBackgroundColor(getResources().getColor(R.color.colorPrimary));
fullOtaButton.setBackgroundColor(getResources().getColor(R.color.colorPrimaryDark));
});
partialOtaButton.setOnClickListener(v -> {
stacklayout.setVisibility(View.GONE);
partialOtaButton.setBackgroundColor(getResources().getColor(R.color.colorPrimaryDark));
fullOtaButton.setBackgroundColor(getResources().getColor(R.color.colorPrimary));
});
otaPrepareDialog = new MaterialDialog.Builder(getActivity())
.title(R.string.otaDialogHeaderText)
.content(R.string.waiting)
.progress(true, 0)
.progressIndeterminateStyle(true)
.build();
otaProgressDialog = new MaterialDialog.Builder(getActivity())
.title("test")
.customView(R.layout.ota_progress2, false)
.build();
}
private void initOtaProgressDialog() {
// Todo: ...
}
}
ECountViewModel.java
public class ECountViewModel extends AndroidViewModel implements ECountBleManagerCallbacks {
private final ECountBleManager eCountBleManager;
// Connection states Connecting, Connected, Disconnecting, Disconnected etc.
private final MutableLiveData<String> connectionState = new MutableLiveData<>();
// Flag to determine if the device is connected
private final MutableLiveData<Boolean> isConnected = new MutableLiveData<>();
// Flag to determine if the device is ready
private final MutableLiveData<Void> onDeviceReady = new MutableLiveData<>();
// Flag to determine if the device is in OTA mode
private final MutableLiveData<Void> onOtaMode = new MutableLiveData<>();
public LiveData<Void> isDeviceReady() {
return onDeviceReady;
}
public LiveData<Void> isOtaMode() {
return onOtaMode;
}
public LiveData<String> getConnectionState() {
return connectionState;
}
public LiveData<Boolean> isConnected() {
return isConnected;
}
public ECountViewModel(@NonNull final Application application) {
super(application);
// Initialize the manager
eCountBleManager = new ECountBleManager(getApplication());
eCountBleManager.setGattCallbacks(this);
}
/**
* Connect to peripheral
*/
public void connect(final BluetoothDevice device) {
eCountBleManager.connect(device);
}
/**
* Disconnect from peripheral
*/
private void disconnect() {
eCountBleManager.disconnect();
}
@Override
protected void onCleared() {
super.onCleared();
if (eCountBleManager.isConnected()) {
disconnect();
}
}
@Override
public void onDeviceConnecting(BluetoothDevice device) {
}
@Override
public void onDeviceConnected(BluetoothDevice device) {
isConnected.postValue(true);
}
@Override
public void onDeviceDisconnecting(BluetoothDevice device) {
isConnected.postValue(false);
}
@Override
public void onDeviceDisconnected(BluetoothDevice device) {
isConnected.postValue(false);
}
@Override
public void onLinklossOccur(BluetoothDevice device) {
isConnected.postValue(false);
}
@Override
public void onServicesDiscovered(BluetoothDevice device, boolean optionalServicesFound) {
}
@Override
public void onDeviceReady(BluetoothDevice device) {
onDeviceReady.postValue(null);
}
@Override
public void onOptionalServiceSupported(BluetoothDevice device) {
onOtaMode.postValue(null);
}
@Override
public void onBondingRequired(BluetoothDevice device) {
}
@Override
public void onBonded(BluetoothDevice device) {
}
@Override
public void onError(BluetoothDevice device, String message, int errorCode) {
}
@Override
public void onDeviceNotSupported(BluetoothDevice device) {
disconnect();
}
// delegate call from options fragment to ECountBleManager
public String getDeviceId() {
return BinaryUtils.byteArrayToHexString(eCountBleManager.getDeviceId());
}
// delegate call from ota fragment to ECountBleManager
public void setMtu(final int value) {
eCountBleManager.setMtu(value);
}
public void setPriority(final int value) {
eCountBleManager.setPriority(value);
}
ECountBleManager.java
public class ECountBleManager extends BleManager<BleManagerCallbacks> {
private static final String TAG = ECountBleManager.class.getSimpleName();
private final Handler handler;
private BluetoothGattCharacteristic authCharacteristic;
private BluetoothGattCharacteristic deviceIdCharacteristic;
private BluetoothGattCharacteristic deviceVersionCharacteristic;
private BluetoothGattCharacteristic configIdCharacteristic;
private BluetoothGattCharacteristic configTransmissionIntervalCharacteristic;
private BluetoothGattCharacteristic configKeepAliveIntervalCharacteristic;
private BluetoothGattCharacteristic configRadioModeCharacteristic;
private BluetoothGattCharacteristic configGpsCharacteristic;
private BluetoothGattCharacteristic configRadarCharacteristic;
private BluetoothGattCharacteristic configOperationModeCharacteristic;
private BluetoothGattCharacteristic configLoRaAppEuiCharacteristic;
private BluetoothGattCharacteristic configLoRaAppKeyCharacteristic;
private BluetoothGattCharacteristic configLoRaDeviceEuiCharacteristic;
private BluetoothGattCharacteristic operationCmdCharacteristic;
private BluetoothGattCharacteristic nemeusStatusCharacteristic;
private BluetoothGattCharacteristic gmrStatusCharacteristic;
private BluetoothGattCharacteristic radarStatusCharacteristic;
private BluetoothGattCharacteristic otaControlCharacteristic;
private BluetoothGattCharacteristic otaDataCharacteristic;
private byte[] configTransmissionInterval;
private byte[] configKeepAliveInterval;
private byte[] configRadioMode;
private byte[] configOperationMode;
private byte[] configId;
private byte[] deviceId;
private byte[] deviceVersion;
private byte[] configGps;
private byte[] configRadar;
private byte[] configLoRaAppEui;
private byte[] configLoRaAppKey;
private byte[] configLoRaDeviceEui;
private byte[] operationCmd;
private byte[] nemeusStatus;
private byte[] gmrStatus;
private byte[] radarStatus;
// OTA flags
private boolean isOtaProcessing = false;
private boolean isReconnectRequired = false;
private MutableLiveData<Boolean> isOtaMode = new MutableLiveData<>();
// OTA variables
private int mtu = 512;
private int priority = BluetoothGatt.CONNECTION_PRIORITY_HIGH;
private byte[] otaAppFileStream;
////////////////////////////
public ECountBleManager(Context context) {
super(context);
handler = new Handler();
}
@Override
protected BleManagerGattCallback getGattCallback() {
return gattCallback;
}
@Override
protected boolean shouldAutoConnect() {
return true;
}
/**
* BluetoothGatt callbacks for connection/disconnection, service discovery, receiving indication, etc
*/
private final BleManagerGattCallback gattCallback = new BleManagerGattCallback() {
@Override
protected void onDeviceReady() {
super.onDeviceReady();
}
@Override
protected void onOptionalServiceSupported() {
super.onOptionalServiceSupported();
isOtaMode.postValue(true);
}
@Override
protected boolean isOptionalServiceSupported(BluetoothGatt gatt) {
final BluetoothGattService otaService = gatt.getService(DC_UUID.otaService);
otaDataCharacteristic = otaService.getCharacteristic(DC_UUID.otaData);
return otaDataCharacteristic != null;
}
@Override
protected boolean isRequiredServiceSupported(BluetoothGatt gatt) {
final BluetoothGattService dcService = gatt.getService(DC_UUID.dcService);
final BluetoothGattService otaService = gatt.getService(DC_UUID.otaService);
if (dcService == null || otaService == null) return false;
authCharacteristic = dcService.getCharacteristic(DC_UUID.authentication);
deviceIdCharacteristic = dcService.getCharacteristic(DC_UUID.deviceId);
deviceVersionCharacteristic = dcService.getCharacteristic(DC_UUID.deviceVersion);
configIdCharacteristic = dcService.getCharacteristic(DC_UUID.configId);
configTransmissionIntervalCharacteristic = dcService.getCharacteristic(DC_UUID.configTransmissionInterval);
configKeepAliveIntervalCharacteristic = dcService.getCharacteristic(DC_UUID.configKeepAliveInterval);
configRadioModeCharacteristic = dcService.getCharacteristic(DC_UUID.configRadioMode);
configGpsCharacteristic = dcService.getCharacteristic(DC_UUID.configGps);
configRadarCharacteristic = dcService.getCharacteristic(DC_UUID.configRadar);
configOperationModeCharacteristic = dcService.getCharacteristic(DC_UUID.configOperationMode);
configLoRaAppEuiCharacteristic = dcService.getCharacteristic(DC_UUID.configLoRaAppEui);
configLoRaAppKeyCharacteristic = dcService.getCharacteristic(DC_UUID.configLoRaAppKey);
configLoRaDeviceEuiCharacteristic = dcService.getCharacteristic(DC_UUID.configLoRaDeviceEui);
operationCmdCharacteristic = dcService.getCharacteristic(DC_UUID.operationCmd);
nemeusStatusCharacteristic = dcService.getCharacteristic(DC_UUID.nemeusStatus);
gmrStatusCharacteristic = dcService.getCharacteristic(DC_UUID.gmrStatus);
radarStatusCharacteristic = dcService.getCharacteristic(DC_UUID.radarStatus);
otaControlCharacteristic = otaService.getCharacteristic(DC_UUID.otaControl);
return authCharacteristic != null &&
deviceIdCharacteristic != null &&
deviceVersionCharacteristic != null&&
configIdCharacteristic != null &&
configTransmissionIntervalCharacteristic != null &&
configKeepAliveIntervalCharacteristic != null &&
configRadioModeCharacteristic != null &&
configGpsCharacteristic != null &&
configRadarCharacteristic != null &&
configOperationModeCharacteristic != null &&
configLoRaAppEuiCharacteristic != null &&
configLoRaAppKeyCharacteristic != null &&
configLoRaDeviceEuiCharacteristic != null &&
operationCmdCharacteristic != null &&
nemeusStatusCharacteristic != null &&
gmrStatusCharacteristic != null &&
radarStatusCharacteristic != null &&
otaControlCharacteristic != null;
}
@Override
protected Deque<Request> initGatt(BluetoothGatt gatt) {
final LinkedList<Request> requests = new LinkedList<>();
requests.push(Request.readRequest(deviceIdCharacteristic));
requests.push(Request.readRequest(deviceVersionCharacteristic));
requests.push(Request.readRequest(configIdCharacteristic));
requests.push(Request.readRequest(configTransmissionIntervalCharacteristic));
requests.push(Request.readRequest(configKeepAliveIntervalCharacteristic));
requests.push(Request.readRequest(configRadioModeCharacteristic));
requests.push(Request.readRequest(configGpsCharacteristic));
requests.push(Request.readRequest(configRadarCharacteristic));
requests.push(Request.readRequest(configOperationModeCharacteristic));
requests.push(Request.readRequest(configLoRaAppEuiCharacteristic));
requests.push(Request.readRequest(configLoRaAppKeyCharacteristic));
requests.push(Request.readRequest(operationCmdCharacteristic));
requests.push(Request.readRequest(configLoRaDeviceEuiCharacteristic));
requests.push(Request.readRequest(nemeusStatusCharacteristic));
requests.push(Request.readRequest(gmrStatusCharacteristic));
requests.push(Request.readRequest(radarStatusCharacteristic));
// write authentication key to characteristic
requests.push(Request.writeRequest(authCharacteristic));
// perform server authentication
requests.push(Request.readRequest(authCharacteristic));
return requests;
}
@Override
protected void onDeviceDisconnected() {
authCharacteristic = null;
deviceIdCharacteristic = null;
deviceVersionCharacteristic = null;
configIdCharacteristic = null;
configTransmissionIntervalCharacteristic = null;
configKeepAliveIntervalCharacteristic = null;
configRadioModeCharacteristic = null;
configGpsCharacteristic = null;
configRadarCharacteristic = null;
configOperationModeCharacteristic = null;
configLoRaAppEuiCharacteristic = null;
configLoRaAppKeyCharacteristic = null;
configLoRaDeviceEuiCharacteristic = null;
nemeusStatusCharacteristic = null;
gmrStatusCharacteristic = null;
radarStatusCharacteristic = null;
otaDataCharacteristic = null;
}
@Override
protected void onMtuChanged(int mtu) {
super.onMtuChanged(mtu);
ECountBleManager.this.mtu = mtu;
}
@Override
protected void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicRead(gatt, characteristic);
if (characteristic.getUuid().equals(DC_UUID.authentication)) {
byte encryptedData[];
try {
encryptedData = BinaryUtils.encryptByteArray(characteristic.getValue());
} catch (Exception e) {
e.printStackTrace();
return;
}
characteristic.setValue(encryptedData);
} else if (characteristic.getUuid().equals(DC_UUID.deviceId)) {
deviceId = characteristic.getValue();
} else if (characteristic.getUuid().equals(DC_UUID.deviceVersion)) {
deviceVersion = characteristic.getValue();
} else if (characteristic.getUuid().equals(DC_UUID.configId)) {
configId = characteristic.getValue();
} else if (characteristic.getUuid().equals(DC_UUID.configTransmissionInterval)) {
configTransmissionInterval = characteristic.getValue();
} else if (characteristic.getUuid().equals(DC_UUID.configKeepAliveInterval)) {
configKeepAliveInterval = characteristic.getValue();
} else if (characteristic.getUuid().equals(DC_UUID.configRadioMode)) {
configRadioMode = characteristic.getValue();
} else if (characteristic.getUuid().equals(DC_UUID.configGps)) {
configGps = characteristic.getValue();
} else if (characteristic.getUuid().equals(DC_UUID.configRadar)) {
configRadar = characteristic.getValue();
} else if (characteristic.getUuid().equals(DC_UUID.configOperationMode)) {
configOperationMode = characteristic.getValue();
} else if (characteristic.getUuid().equals(DC_UUID.configLoRaAppEui)) {
configLoRaAppEui = characteristic.getValue();
} else if (characteristic.getUuid().equals(DC_UUID.configLoRaAppKey)) {
configLoRaAppKey = characteristic.getValue();
} else if (characteristic.getUuid().equals(DC_UUID.configLoRaDeviceEui)) {
configLoRaDeviceEui = characteristic.getValue();
} else if (characteristic.getUuid().equals(DC_UUID.nemeusStatus)) {
nemeusStatus = characteristic.getValue();
} else if (characteristic.getUuid().equals(DC_UUID.gmrStatus)) {
gmrStatus = characteristic.getValue();
} else if (characteristic.getUuid().equals(DC_UUID.radarStatus)) {
radarStatus = characteristic.getValue();
}
}
@Override
protected void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
super.onCharacteristicWrite(gatt, characteristic);
if (characteristic.getUuid().equals(DC_UUID.otaControl)) {
final byte[] otaControl = characteristic.getValue();
if (otaControl.length == 1) {
// OTA client initiates the update process
if (otaControl[0] == (byte) 0x00) {
// set OTA process flag
isOtaProcessing = true;
// check whether device is in OTA mode
if (isOtaMode.getValue()) {
// request MTU size
requestMtu(mtu);
// start update process,but ensure MTU size has been requested
handler.postDelayed(() -> uploadOta(), 2000);
} else {
// reconnect to establish OTA mode
isReconnectRequired = true;
// enforces device to reconnect
gatt.disconnect();
}
}
// OTA client finishes the update process
if (otaControl[0] == (byte) 0x03) {
if (isOtaProcessing) { // if device is in OTA mode and update process was successful
isOtaProcessing = false;
disconnect();
} else { // if device is in OTA mode, but update process was not established
// enforces device to reconnect
gatt.disconnect();
}
}
}
}
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
}
};
public byte[] getDeviceId() {
return deviceId;
}
public void setDeviceId(final byte[] value) {
writeCharacteristic(deviceIdCharacteristic,
BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT,
value);
}
// Todo: implement other getters and setters
// Here I have to get the otaAppFilePath which I discovered in OtaFragment
public void uploadOta(final String otaAppFilePath) {
if (otaDataCharacteristic != null) {
otaDataCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
byte[] ebl = null;
try {
FileInputStream fileInputStream = new FileInputStream(otaAppFilePath);
int size = fileInputStream.available();
byte[] temp = new byte[size];
fileInputStream.read(temp);
fileInputStream.close();
ebl = temp;
} catch (Exception e) {
Logger.e(TAG, "Couldn't open file " + e);
}
otaAppFileStream = ebl;
pack = 0;
// start update process in another thread
Thread otaUploadThread = new Thread(() -> otaWriteDataReliable());
otaUploadThread.start();
}
}
private void writeCharacteristic(final BluetoothGattCharacteristic c,
final int writeType,
final byte[] value) {
if (c == null)
return;
c.setWriteType(writeType);
c.setValue(value);
writeCharacteristic(c); // will call the underlying API of BleManager
}
}
The code covers the basic use cases but I'm still not sure how to link the particular components with each other.
While reading about MVVM I noticed that there is always more than one possible solution/approach. I discovered the following questions:
ECountBleManager
the right place to store the variables that I got by calling characteristics.getValue()
and when yes, should I place the variables that I discover in OtaFragment
in it too (that would mean, that I have to forward the values e.g. of mtu to the ECountBleManager
)? Consider that I have to access the variables that I discover in OtaFragment
and maybe other Fragments. OtaFragment
? In ECountVieModel
or in ECountBleManager
or do I create an OtaViewModel (but how could I access the ECountBleManager
instance that I already created in ECountViewModel
within the OtaViewModel?) ECountBleManager
instance, see question 2?ECountBleManager
fit in the MVVM pattern? I would guess it is part of the Model?! But which part? Repository, Interactor, Controller, Mediator?The code is not less so I'm sorry but you see I'm really try harding and I want to get better. I hope someone can help me with my questions and to improve my code. Thanks in advance!
My 5 cents about BLE and an architecture:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With