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