no.android.proxime.profile.BleProfileService.java Source code

Java tutorial

Introduction

Here is the source code for no.android.proxime.profile.BleProfileService.java

Source

/*******************************************************************************
 * Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved.
 * 
 * The information contained herein is property of Nordic Semiconductor ASA.
 * Terms and conditions of usage are described in detail in NORDIC SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
 * Licensees are granted free, non-transferable use of the information. NO WARRANTY of ANY KIND is provided. 
 * This heading must NOT be removed from the file.
 ******************************************************************************/
package no.android.proxime.profile;

import no.nordicsemi.android.log.LogSession;
import no.nordicsemi.android.log.Logger;
import no.android.proxime.R;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.content.Intent;
import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.support.v4.content.LocalBroadcastManager;
import android.widget.Toast;

public abstract class BleProfileService extends Service implements BleManagerCallbacks {
    @SuppressWarnings("unused")
    private static final String TAG = "BleProfileService";

    public static final String BROADCAST_CONNECTION_STATE = "no.nordicsemi.android.nrftoolbox.BROADCAST_CONNECTION_STATE";
    public static final String BROADCAST_SERVICES_DISCOVERED = "no.nordicsemi.android.nrftoolbox.BROADCAST_SERVICES_DISCOVERED";
    public static final String BROADCAST_BOND_STATE = "no.nordicsemi.android.nrftoolbox.BROADCAST_BOND_STATE";
    public static final String BROADCAST_BATTERY_LEVEL = "no.nordicsemi.android.nrftoolbox.BROADCAST_BATTERY_LEVEL";
    public static final String BROADCAST_ERROR = "no.nordicsemi.android.nrftoolbox.BROADCAST_ERROR";

    /** The parameter passed when creating the service. Must contain the address of the sensor that we want to connect to */
    public static final String EXTRA_DEVICE_ADDRESS = "no.nordicsemi.android.nrftoolbox.EXTRA_DEVICE_ADDRESS";
    /** The key for the device name that is returned in {@link #BROADCAST_CONNECTION_STATE} with state {@link #STATE_CONNECTED}. */
    public static final String EXTRA_DEVICE_NAME = "no.nordicsemi.android.nrftoolbox.EXTRA_DEVICE_NAME";
    public static final String EXTRA_LOG_URI = "no.nordicsemi.android.nrftoolbox.EXTRA_LOG_URI";
    public static final String EXTRA_CONNECTION_STATE = "no.nordicsemi.android.nrftoolbox.EXTRA_CONNECTION_STATE";
    public static final String EXTRA_BOND_STATE = "no.nordicsemi.android.nrftoolbox.EXTRA_BOND_STATE";
    public static final String EXTRA_SERVICE_PRIMARY = "no.nordicsemi.android.nrftoolbox.EXTRA_SERVICE_PRIMARY";
    public static final String EXTRA_SERVICE_SECONDARY = "no.nordicsemi.android.nrftoolbox.EXTRA_SERVICE_SECONDARY";
    public static final String EXTRA_BATTERY_LEVEL = "no.nordicsemi.android.nrftoolbox.EXTRA_BATTERY_LEVEL";
    public static final String EXTRA_ERROR_MESSAGE = "no.nordicsemi.android.nrftoolbox.EXTRA_ERROR_MESSAGE";
    public static final String EXTRA_ERROR_CODE = "no.nordicsemi.android.nrftoolbox.EXTRA_ERROR_CODE";

    public static final int STATE_LINK_LOSS = -1;
    public static final int STATE_DISCONNECTED = 0;
    public static final int STATE_CONNECTED = 1;
    public static final int STATE_CONNECTING = 2;
    public static final int STATE_DISCONNECTING = 3;

    private BleManager<BleManagerCallbacks> mBleManager;
    private Handler mHandler;

    private boolean mConnected;
    private String mDeviceAddress;
    private String mDeviceName;
    private LogSession mLogSession;

    public class LocalBinder extends Binder {
        /**
         * Disconnects from the sensor.
         */
        public final void disconnect() {
            if (!mConnected) {
                onDeviceDisconnected();
                return;
            }

            // notify user about changing the state to DISCONNECTING
            final Intent broadcast = new Intent(BROADCAST_CONNECTION_STATE);
            broadcast.putExtra(EXTRA_CONNECTION_STATE, STATE_DISCONNECTING);
            LocalBroadcastManager.getInstance(BleProfileService.this).sendBroadcast(broadcast);

            mBleManager.disconnect();
        }

        /**
         * Returns the device address
         * 
         * @return device address
         */
        public String getDeviceAddress() {
            return mDeviceAddress;
        }

        /**
         * Returns the device name
         * 
         * @return the device name
         */
        public String getDeviceName() {
            return mDeviceName;
        }

        /**
         * Returns <code>true</code> if the device is connected to the sensor.
         * 
         * @return <code>true</code> if device is connected to the sensor, <code>false</code> otherwise
         */
        public boolean isConnected() {
            return mConnected;
        }

        /**
         * Returns the log session that can be used to append log entries. The log session is created when the service is being created. The method returns <code>null</code> if the nRF Logger app was
         * not installed.
         * 
         * @return the log session
         */
        protected LogSession getLogSession() {
            return mLogSession;
        }
    }

    @Override
    public IBinder onBind(final Intent intent) {
        return getBinder();
    }

    /**
     * Returns the binder implementation. This must return class implementing the additional manager interface that may be used in the binded activity.
     * 
     * @return the service binder
     */
    protected LocalBinder getBinder() {
        // default implementation returns the basic binder. You can overwrite the LocalBinder with your own, wider implementation
        return new LocalBinder();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        // we must allow to rebind to the same service
        return true;
    }

    @SuppressWarnings("unchecked")
    @Override
    public void onCreate() {
        super.onCreate();

        mHandler = new Handler();

        // initialize the manager
        mBleManager = initializeManager();
        mBleManager.setGattCallbacks(this);
    }

    @SuppressWarnings("rawtypes")
    protected abstract BleManager initializeManager();

    @Override
    public int onStartCommand(final Intent intent, final int flags, final int startId) {
        if (intent == null || !intent.hasExtra(EXTRA_DEVICE_ADDRESS))
            throw new UnsupportedOperationException("No device address at EXTRA_DEVICE_ADDRESS key");

        final Uri logUri = intent.getParcelableExtra(EXTRA_LOG_URI);
        mLogSession = Logger.openSession(getApplicationContext(), logUri);
        mDeviceAddress = intent.getStringExtra(EXTRA_DEVICE_ADDRESS);

        Logger.i(mLogSession, "Service started");

        // notify user about changing the state to CONNECTING
        final Intent broadcast = new Intent(BROADCAST_CONNECTION_STATE);
        broadcast.putExtra(EXTRA_CONNECTION_STATE, STATE_CONNECTING);
        LocalBroadcastManager.getInstance(BleProfileService.this).sendBroadcast(broadcast);

        final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
        final BluetoothAdapter adapter = bluetoothManager.getAdapter();
        final BluetoothDevice device = adapter.getRemoteDevice(mDeviceAddress);
        mDeviceName = device.getName();
        onServiceStarted();

        Logger.v(mLogSession, "Connecting...");
        mBleManager.connect(BleProfileService.this, device);
        return START_REDELIVER_INTENT;
    }

    /**
     * Called when the service has been started. The device name and address are set. It nRF Logger is installed than logger was also initialized.
     */
    protected void onServiceStarted() {
        // empty default implementation
    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        // shutdown the manager
        mBleManager.closeBluetoothGatt();
        Logger.i(mLogSession, "Service destroyed");
        mBleManager = null;
        mDeviceAddress = null;
        mDeviceName = null;
        mConnected = false;
        mLogSession = null;
    }

    @Override
    public void onDeviceConnected() {
        Logger.i(mLogSession, "Connected to " + mDeviceAddress);
        mConnected = true;

        final Intent broadcast = new Intent(BROADCAST_CONNECTION_STATE);
        broadcast.putExtra(EXTRA_CONNECTION_STATE, STATE_CONNECTED);
        broadcast.putExtra(EXTRA_DEVICE_ADDRESS, mDeviceAddress);
        broadcast.putExtra(EXTRA_DEVICE_NAME, mDeviceName);
        LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
    }

    @Override
    public void onDeviceDisconnected() {
        Logger.i(mLogSession, "Disconnected");
        mConnected = false;
        mDeviceAddress = null;
        mDeviceName = null;

        final Intent broadcast = new Intent(BROADCAST_CONNECTION_STATE);
        broadcast.putExtra(EXTRA_CONNECTION_STATE, STATE_DISCONNECTED);
        LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);

        // user requested disconnection. We must stop the service
        Logger.v(mLogSession, "Stopping service...");
        stopSelf();
    }

    @Override
    public void onLinklossOccur() {
        Logger.w(mLogSession, "Connection lost");
        mConnected = false;

        final Intent broadcast = new Intent(BROADCAST_CONNECTION_STATE);
        broadcast.putExtra(EXTRA_CONNECTION_STATE, STATE_LINK_LOSS);
        LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
    }

    @Override
    public void onDrop() {
        Logger.i(mLogSession, "item dropped");

    }

    @Override
    public void onServicesDiscovered(final boolean optionalServicesFound) {
        Logger.i(mLogSession, "Services Discovered");
        Logger.v(mLogSession, "Primary service found");
        if (optionalServicesFound)
            Logger.v(mLogSession, "Secondary service found");

        final Intent broadcast = new Intent(BROADCAST_SERVICES_DISCOVERED);
        broadcast.putExtra(EXTRA_SERVICE_PRIMARY, true);
        broadcast.putExtra(EXTRA_SERVICE_SECONDARY, optionalServicesFound);
        LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
    }

    @Override
    public void onDeviceNotSupported() {
        Logger.i(mLogSession, "Services Discovered");
        Logger.w(mLogSession, "Device is not supported");

        final Intent broadcast = new Intent(BROADCAST_SERVICES_DISCOVERED);
        broadcast.putExtra(EXTRA_SERVICE_PRIMARY, false);
        broadcast.putExtra(EXTRA_SERVICE_SECONDARY, false);
        LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);

        // no need for disconnecting, it will be disconnected by the manager automatically
    }

    /*
    public int getRssi(int i) {
       Logger.i(mLogSession, "getRssicalled from bleservice");
    }
    */

    @Override
    public void onBatteryValueReceived(final int value) {
        Logger.i(mLogSession, "Battery level received: " + value + "%");

        final Intent broadcast = new Intent(BROADCAST_BATTERY_LEVEL);
        broadcast.putExtra(EXTRA_BATTERY_LEVEL, value);
        LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
    }

    @Override
    public void onBondingRequired() {
        Logger.v(mLogSession, "Bond state: Bonding...");
        showToast(R.string.bonding);

        final Intent broadcast = new Intent(BROADCAST_BOND_STATE);
        broadcast.putExtra(EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDING);
        LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
    }

    @Override
    public void onBonded() {
        Logger.i(mLogSession, "Bond state: Bonded");
        showToast(R.string.bonded);

        final Intent broadcast = new Intent(BROADCAST_BOND_STATE);
        broadcast.putExtra(EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDED);
        LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);
    }

    @Override
    public void onError(final String message, final int errorCode) {
        Logger.e(mLogSession, message + " (" + errorCode + ")");

        final Intent broadcast = new Intent(BROADCAST_ERROR);
        broadcast.putExtra(EXTRA_ERROR_MESSAGE, message);
        broadcast.putExtra(EXTRA_ERROR_CODE, errorCode);
        LocalBroadcastManager.getInstance(this).sendBroadcast(broadcast);

        mBleManager.disconnect();
        stopSelf();
    }

    /**
     * Shows a message as a Toast notification. This method is thread safe, you can call it from any thread
     * 
     * @param messageResId
     *            an resource id of the message to be shown
     */
    protected void showToast(final int messageResId) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(BleProfileService.this, messageResId, Toast.LENGTH_SHORT).show();
            }
        });
    }

    /**
     * Shows a message as a Toast notification. This method is thread safe, you can call it from any thread
     * 
     * @param message
     *            a message to be shown
     */
    protected void showToast(final String message) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(BleProfileService.this, message, Toast.LENGTH_SHORT).show();
            }
        });
    }

    /**
     * Returns the log session that can be used to append log entries. The method returns <code>null</code> if the nRF Logger app was not installed. It is safe to use logger when
     * {@link #onServiceStarted()} has been called.
     * 
     * @return the log session
     */
    protected LogSession getLogSession() {
        return mLogSession;
    }

    /**
     * Returns the device address
     * 
     * @return device address
     */
    protected String getDeviceAddress() {
        return mDeviceAddress;
    }

    /**
     * Returns the device name
     * 
     * @return the device name
     */
    protected String getDeviceName() {
        return mDeviceName;
    }

    /**
     * Returns <code>true</code> if the device is connected to the sensor.
     * 
     * @return <code>true</code> if device is connected to the sensor, <code>false</code> otherwise
     */
    protected boolean isConnected() {
        return mConnected;
    }
}