¿Qué es Jetpack Compose?

Jetpack Compose es la biblioteca de IU de Android más reciente que ha tomado la plataforma de desarrollo móvil de Android por sorpresa. Con Jetpack Compose, los desarrolladores pueden construir aplicaciones de alta calidad y sofisticadas que son más fáciles de mantener y escalar. La introducción de Jetpack Compose representa un cambio significativo en la forma en que se crea la interfaz de usuario de una aplicación de Android. En lugar de trabajar con una jerarquía de vistas de Android, Jetpack Compose utiliza un enfoque de programación declarativa para definir la IU de una aplicación. Esto significa que los desarrolladores pueden escribir código que describe cómo debe verse la interfaz de usuario de una aplicación, en lugar de manipular directamente los objetos de vista. Jetpack Compose también viene con una serie de herramientas que facilitan el diseño y la personalización de la interfaz de usuario de una aplicación. Desde una amplia variedad de widgets personalizados hasta la capaci...

Trabajando con Bluetooth Low Energy (BLE) en Android


Actualmente estoy desarrollando un app que requiere la comunicación con dispositivos bluetooth low energy. Todo parecía muy sencillo, pero la verdad es que hay que escribir mucho código para hacer que funcione servicios, broadcast receivers, binding a servicios, entre otros.

Mientras voy desarrollando tengo en mi gradle el minSDK de mi teléfono (Nexus 6p - SDK 24) esto con el simple hecho de compilar mas rápido.  La aplicación se espera que trabaje desde Android 4.3 SDK18 que fue cuando se introdujo BLE en Android.

Todo iba muy bien hasta que cambié el gradle a minSDK 18.. encontré que todo mi código estaba hecho para SDK21 donde hay métodos nuevos para escanear BLE devices.

Para ser mas concreto cambian los callBacks a partir del SDK 21. Lo resolví de la siguiente manera

Primero nos creamos dos métodos: scanLeDevice21 y scanLeDevice18.
    /**
     * Scan for BLE devices with Android API 21 and up
     *
     * @param enable Enabled scanning
     */
    @RequiresApi(21)
    private void scanLeDevice21(final boolean enable) {

        ScanCallback mLeScanCallback = new ScanCallback() {

            @Override
            public void onScanResult(int callbackType, ScanResult result) {

                super.onScanResult(callbackType, result);

                BluetoothDevice bluetoothDevice = result.getDevice();

                if (!bluetoothDeviceList.contains(bluetoothDevice)) {
                    Log.d("DEVICE", bluetoothDevice.getName() + "[" + bluetoothDevice.getAddress() + "]");
                    bluetoothDeviceArrayAdapter.add(bluetoothDevice);
                    bluetoothDeviceArrayAdapter.notifyDataSetChanged();
                }
            }

            @Override
            public void onBatchScanResults(List results) {
                super.onBatchScanResults(results);

            }

            @Override
            public void onScanFailed(int errorCode) {
                super.onScanFailed(errorCode);
            }
        };

        final BluetoothLeScanner bluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();

        if (enable) {
            // Stops scanning after a pre-defined scan period.
            mHandler.postDelayed(() -> {
                mScanning = false;
                swipeRefreshLayout.setRefreshing(false);
                bluetoothLeScanner.stopScan(mLeScanCallback);
            }, SCAN_PERIOD);

            mScanning = true;
            bluetoothLeScanner.startScan(mLeScanCallback);
        } else {
            mScanning = false;
            bluetoothLeScanner.stopScan(mLeScanCallback);
        }
    }

    /**
     * Scan BLE devices on Android API 18 to 20
     *
     * @param enable Enable scan
     */
    private void scanLeDevice18(boolean enable) {

        BluetoothAdapter.LeScanCallback mLeScanCallback =
                new BluetoothAdapter.LeScanCallback() {
                    @Override
                    public void onLeScan(final BluetoothDevice bluetoothDevice, int rssi,
                                         byte[] scanRecord) {
                        getActivity().runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                bluetoothDeviceArrayAdapter.add(bluetoothDevice);
                                bluetoothDeviceArrayAdapter.notifyDataSetChanged();
                            }
                        });
                    }
                };
        if (enable) {
            // Stops scanning after a pre-defined scan period.
            mHandler.postDelayed(() -> {
                mScanning = false;
                mBluetoothAdapter.stopLeScan(mLeScanCallback);
            }, SCAN_PERIOD);

            mScanning = true;
            mBluetoothAdapter.startLeScan(mLeScanCallback);
        } else {
            mScanning = false;
            mBluetoothAdapter.stopLeScan(mLeScanCallback);
        }

    }

Si observamos, el método scanLeDevice21 tiene la anotación @RequiresApi(21) que dice lo siguiente:

"Denotes that the annotated element should only be called on the given API level or higher. This is similar in purpose to the older @TargetApi annotation, but more clearly expresses that this is a requirement on the caller, rather than being used to "suppress" warnings within the method that exceed the minSdkVersion"

Luego, cada vez que necesitamos escanear un dispositivo preguntamos en cuál API estamos y utilizamos el método correspondiente. Por ejemplo, tengo mi lista de dispositivos dentro de un RefreshLayout.

    /**
     * Refresh listener
     */
    private void refreshScan() {
        if (!hasFineLocationPermissions()) {
            swipeRefreshLayout.setRefreshing(false);
            requestFineLocationPermission();
        } else {
            swipeRefreshLayout.setRefreshing(true);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                scanLeDevice21(true);
            } else {
                scanLeDevice18(true);
            }
        }
    }

Y ya, eso es todo! con esto tenemos nuestra api funcional para las diferentes versiones de Android.

Comentarios

Entradas más populares de este blog

¿Qué es Jetpack Compose?

Hacer la barra de status transparente en Android.

Solicitar permisos en Android en tiempo de ejecución más fácilmente