com.duy.pascal.interperter.libraries.android.connection.bluetooth.AndroidBluetoothLib.java Source code

Java tutorial

Introduction

Here is the source code for com.duy.pascal.interperter.libraries.android.connection.bluetooth.AndroidBluetoothLib.java

Source

/*
 *  Copyright (c) 2017 Tran Le Duy
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.duy.pascal.interperter.libraries.android.connection.bluetooth;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;

import com.duy.pascal.interperter.ast.expressioncontext.ExpressionContextMixin;
import com.duy.pascal.interperter.libraries.PascalLibrary;
import com.duy.pascal.interperter.libraries.android.AndroidLibraryManager;
import com.duy.pascal.interperter.libraries.android.temp.AndroidUtilsLib;
import com.duy.pascal.interperter.libraries.annotations.PascalMethod;
import com.duy.pascal.interperter.libraries.annotations.PascalParameter;
import com.googlecode.sl4a.Constants;
import com.googlecode.sl4a.MainThread;
import com.googlecode.sl4a.rpc.RpcDefault;
import com.googlecode.sl4a.rpc.RpcOptional;

import org.apache.commons.codec.binary.Base64;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Callable;

/**
 * Bluetooth functions.
 */
// Discovery functions added by Eden Sayag

@SuppressWarnings("unused")
public class AndroidBluetoothLib extends PascalLibrary {

    public static final String NAME = "aBluetooth".toLowerCase();
    // UUID for SL4A.
    private static final String DEFAULT_UUID = "457807c0-4897-11df-9879-0800200c9a66";
    private static final String SDP_NAME = "SL4A";
    private Map<String, BluetoothConnection> connections = new HashMap<>();
    private AndroidUtilsLib mAndroidFacade;
    private BluetoothAdapter mBluetoothAdapter;

    public AndroidBluetoothLib() {

    }

    public AndroidBluetoothLib(AndroidLibraryManager manager) {
        mAndroidFacade = manager.getReceiver(AndroidUtilsLib.class);
        mBluetoothAdapter = MainThread.run(manager.getContext(), new Callable<BluetoothAdapter>() {
            @Override
            public BluetoothAdapter call() throws Exception {
                return BluetoothAdapter.getDefaultAdapter();
            }
        });
    }

    @SuppressWarnings("unused")
    @PascalMethod(description = "Returns active Bluetooth connections.")
    public Map<String, String> bluetoothActiveConnections() {
        Map<String, String> out = new HashMap<>();
        for (Map.Entry<String, BluetoothConnection> entry : connections.entrySet()) {
            if (entry.getValue().isConnected()) {
                out.put(entry.getKey(), entry.getValue().getRemoteBluetoothAddress());
            }
        }

        return out;
    }

    private BluetoothConnection getConnection(String connID) throws IOException {
        BluetoothConnection conn = null;
        if (connID.trim().length() > 0) {
            conn = connections.get(connID);
        } else if (connections.size() == 1) {
            conn = (BluetoothConnection) connections.values().toArray()[0];
        }
        if (conn == null) {
            throw new IOException("Bluetooth not ready for this connID.");
        }
        return conn;
    }

    @SuppressWarnings("unused")
    @PascalMethod(description = "Send bytes over the currently open Bluetooth connection.")
    public void bluetoothWriteBinary(
            @PascalParameter(name = "base64", description = "A base64 encoded String of the bytes to be sent.") String base64,
            @PascalParameter(name = "connID", description = "Connection id") @RpcDefault("") @RpcOptional String connID)
            throws IOException {
        BluetoothConnection conn = getConnection(connID);
        try {
            conn.write(Base64.decodeBase64(base64));
        } catch (IOException e) {
            connections.remove(conn.getUUID());
            throw e;
        }
    }

    @SuppressWarnings("unused")
    @PascalMethod(description = "Read up to bufferSize bytes and return a chunked, base64 encoded string.")
    public String bluetoothReadBinary(@PascalParameter(name = "bufferSize") @RpcDefault("4096") Integer bufferSize,
            @PascalParameter(name = "connID", description = "Connection id") @RpcDefault("") @RpcOptional String connID)
            throws IOException {

        BluetoothConnection conn = getConnection(connID);
        try {
            return Base64.encodeBase64String(conn.readBinary(bufferSize));
        } catch (IOException e) {
            connections.remove(conn.getUUID());
            throw e;
        }
    }

    private String addConnection(BluetoothConnection conn) {
        String uuid = UUID.randomUUID().toString();
        connections.put(uuid, conn);
        conn.setUUID(uuid);
        return uuid;
    }

    @SuppressWarnings("unused")
    @PascalMethod(description = "Connect to a device over Bluetooth. Blocks until the connection is established or fails.", returns = "True if the connection was established successfully.")
    public String bluetoothConnect(
            @PascalParameter(name = "uuid", description = "The UUID passed here must match the UUID used by the server device.") @RpcDefault(DEFAULT_UUID) String uuid,
            @PascalParameter(name = "address", description = "The user will be presented with a list of discovered devices to choose from if an address is not provided.") @RpcOptional String address)
            throws IOException {
        if (address == null) {
            Intent deviceChooserIntent = new Intent();
            deviceChooserIntent.setComponent(Constants.BLUETOOTH_DEVICE_LIST_COMPONENT_NAME);
            Intent result = mAndroidFacade.startActivityForResult(deviceChooserIntent);
            if (result != null && result.hasExtra(Constants.EXTRA_DEVICE_ADDRESS)) {
                address = result.getStringExtra(Constants.EXTRA_DEVICE_ADDRESS);
            } else {
                return null;
            }
        }
        BluetoothDevice mDevice;
        BluetoothSocket mSocket;
        BluetoothConnection conn;
        mDevice = mBluetoothAdapter.getRemoteDevice(address);
        mSocket = mDevice.createRfcommSocketToServiceRecord(UUID.fromString(uuid));
        // Always cancel discovery because it will slow down a connection.
        mBluetoothAdapter.cancelDiscovery();
        mSocket.connect();
        conn = new BluetoothConnection(mSocket);
        return addConnection(conn);
    }

    @SuppressWarnings("unused")
    @PascalMethod(description = "Listens for and accepts a Bluetooth connection. Blocks until the connection is established or fails.")
    public String bluetoothAccept(@PascalParameter(name = "uuid") @RpcDefault(DEFAULT_UUID) String uuid,
            @PascalParameter(name = "timeout", description = "How long to wait for a new connection, 0 is wait for ever") @RpcDefault("0") Integer timeout)
            throws IOException {
        BluetoothServerSocket mServerSocket;
        mServerSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(SDP_NAME, UUID.fromString(uuid));
        BluetoothSocket mSocket = mServerSocket.accept(timeout);
        BluetoothConnection conn = new BluetoothConnection(mSocket, mServerSocket);
        return addConnection(conn);
    }

    @SuppressWarnings("unused")
    @PascalMethod(description = "Requests that the device be discoverable for Bluetooth connections.")
    public void bluetoothMakeDiscoverable(
            @PascalParameter(name = "duration", description = "period of time, in seconds, during which the device should be discoverable") @RpcDefault("300") Integer duration) {
        if (mBluetoothAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
            Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
            discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, duration);
            // Use startActivityForResult to make this a synchronous call.
            mAndroidFacade.startActivityForResult(discoverableIntent);
        }
    }

    @SuppressWarnings("unused")
    @PascalMethod(description = "Sends ASCII characters over the currently open Bluetooth connection.")
    public void bluetoothWrite(@PascalParameter(name = "ascii") String ascii,
            @PascalParameter(name = "connID", description = "Connection id") @RpcDefault("") String connID)
            throws IOException {
        BluetoothConnection conn = getConnection(connID);
        try {
            conn.write(ascii);
        } catch (IOException e) {
            connections.remove(conn.getUUID());
            throw e;
        }
    }

    @SuppressWarnings("unused")
    @PascalMethod(description = "Returns True if the next read is guaranteed not to block.")
    public Boolean bluetoothReadReady(
            @PascalParameter(name = "connID", description = "Connection id") @RpcDefault("") @RpcOptional String connID)
            throws IOException {
        BluetoothConnection conn = getConnection(connID);
        try {
            return conn.readReady();
        } catch (IOException e) {
            connections.remove(conn.getUUID());
            throw e;
        }
    }

    @SuppressWarnings("unused")
    @PascalMethod(description = "Read up to bufferSize ASCII characters.")
    public String bluetoothRead(@PascalParameter(name = "bufferSize") @RpcDefault("4096") Integer bufferSize,
            @PascalParameter(name = "connID", description = "Connection id") @RpcOptional @RpcDefault("") String connID)
            throws IOException {
        BluetoothConnection conn = getConnection(connID);
        try {
            return conn.read(bufferSize);
        } catch (IOException e) {
            connections.remove(conn.getUUID());
            throw e;
        }
    }

    @SuppressWarnings("unused")
    @PascalMethod(description = "Read the next line.")
    public String bluetoothReadLine(
            @PascalParameter(name = "connID", description = "Connection id") @RpcOptional @RpcDefault("") String connID)
            throws IOException {
        BluetoothConnection conn = getConnection(connID);
        try {
            return conn.readLine();
        } catch (IOException e) {
            connections.remove(conn.getUUID());
            throw e;
        }
    }

    @SuppressWarnings("unused")
    @PascalMethod(description = "Queries a remote device for it's name or null if it can't be resolved")
    public String bluetoothGetRemoteDeviceName(
            @PascalParameter(name = "address", description = "Bluetooth Address For Target Device") String address) {
        try {
            BluetoothDevice mDevice;
            mDevice = mBluetoothAdapter.getRemoteDevice(address);
            return mDevice.getName();
        } catch (Exception e) {
            return null;
        }
    }

    @SuppressWarnings("unused")
    @PascalMethod(description = "Gets the Bluetooth Visible device name")
    public String bluetoothGetLocalName() {
        return mBluetoothAdapter.getName();
    }

    @SuppressWarnings("unused")
    @PascalMethod(description = "Sets the Bluetooth Visible device name, returns True on success")
    public boolean bluetoothSetLocalName(
            @PascalParameter(name = "name", description = "New local name") String name) {
        return mBluetoothAdapter.setName(name);
    }

    @SuppressWarnings("unused")
    @PascalMethod(description = "Gets the scan mode for the local dongle.\r\n" + "Return values:\r\n"
            + "\t-1 when Bluetooth is disabled.\r\n" + "\t0 if non discoverable and non connectable.\r\n"
            + "\r1 connectable non discoverable." + "\r3 connectable and discoverable.")
    public int bluetoothGetScanMode() {
        if (mBluetoothAdapter.getState() == BluetoothAdapter.STATE_OFF
                || mBluetoothAdapter.getState() == BluetoothAdapter.STATE_TURNING_OFF) {
            return -1;
        }

        switch (mBluetoothAdapter.getScanMode()) {
        case BluetoothAdapter.SCAN_MODE_NONE:
            return 0;
        case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
            return 1;
        case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
            return 3;
        default:
            return mBluetoothAdapter.getScanMode() - 20;
        }
    }

    @SuppressWarnings("unused")
    @PascalMethod(description = "Returns the name of the connected device.")
    public String bluetoothGetConnectedDeviceName(
            @PascalParameter(name = "connID", description = "Connection id") @RpcOptional @RpcDefault("") String connID)
            throws IOException {
        BluetoothConnection conn = getConnection(connID);
        return conn.getConnectedDeviceName();
    }

    @PascalMethod(description = "Checks Bluetooth state.", returns = "True if Bluetooth is enabled.")
    public Boolean checkBluetoothState() {
        return mBluetoothAdapter.isEnabled();
    }

    @SuppressWarnings("unused")
    @PascalMethod(description = "Toggle Bluetooth on and off.", returns = "True if Bluetooth is enabled.")
    public Boolean toggleBluetoothState(@PascalParameter(name = "enabled") @RpcOptional Boolean enabled,
            @PascalParameter(name = "prompt", description = "Prompt the user to confirm changing the Bluetooth state.") @RpcDefault("true") Boolean prompt) {
        if (enabled == null) {
            enabled = !checkBluetoothState();
        }
        if (enabled) {
            if (prompt) {
                Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                // TODO(damonkohler): Use the result to determine if this was successful. At any rate, keep
                // using startActivityForResult in order to synchronize this call.
                mAndroidFacade.startActivityForResult(intent);
            } else {
                // TODO(damonkohler): Make this synchronous as well.
                mBluetoothAdapter.enable();
            }
        } else {
            // TODO(damonkohler): Add support for prompting on disable.
            // TODO(damonkohler): Make this synchronous as well.
            onFinalize();
            mBluetoothAdapter.disable();
        }
        return enabled;
    }

    @SuppressWarnings("unused")
    @PascalMethod(description = "Stops Bluetooth connection.")
    public void bluetoothStop(
            @PascalParameter(name = "connID", description = "Connection id") @RpcOptional @RpcDefault("") String connID) {
        BluetoothConnection conn;
        try {
            conn = getConnection(connID);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return;
        }
        if (conn == null) {
            return;
        }

        conn.stop();
        connections.remove(conn.getUUID());
    }

    @SuppressWarnings("unused")
    @PascalMethod(description = "Returns the hardware address of the local Bluetooth adapter. ")
    public String bluetoothGetLocalAddress() {
        return mBluetoothAdapter.getAddress();
    }

    @SuppressWarnings("unused")
    @PascalMethod(description = "Start the remote device discovery process. ", returns = "true on success, false on error")
    public Boolean bluetoothDiscoveryStart() {
        return mBluetoothAdapter.startDiscovery();
    }

    @SuppressWarnings("unused")
    @PascalMethod(description = "Cancel the current device discovery process.", returns = "true on success, false on error")
    public Boolean bluetoothDiscoveryCancel() {
        return mBluetoothAdapter.cancelDiscovery();
    }

    @SuppressWarnings("unused")
    @PascalMethod(description = "Return true if the local Bluetooth adapter is currently in the device discovery process. ")
    public Boolean bluetoothIsDiscovering() {
        return mBluetoothAdapter.isDiscovering();
    }

    @Override
    @PascalMethod(description = "stop")

    public void onFinalize() {
        for (Map.Entry<String, BluetoothConnection> entry : connections.entrySet()) {
            entry.getValue().stop();
        }
        connections.clear();
    }

    @Override
    public String getName() {
        return null;
    }

    @Override
    public void declareConstants(ExpressionContextMixin context) {

    }

    @Override
    public void declareTypes(ExpressionContextMixin context) {

    }

    @Override
    public void declareVariables(ExpressionContextMixin context) {

    }

    @Override
    public void declareFunctions(ExpressionContextMixin context) {

    }
}