Fliegl Counter HD Android

In dieser Anleitung erfahren Sie, wie Sie in wenigen Schritten in Ihrer eigenen iOS Application die Fliegl Counter HD Beacons unterstützen können.
Die Verbindung zu den Beacons basiert auf Bluetooth LE Kommunikation und ist somit Endgeräten (und deren neuere Generationen) vorbehalten, welche über eine Bluetooth 4.0 Schnittstelle und Android API Plattform 18 oder neuer verfügen. Dieses Beispiel nutzt API v23+

Manifest

Um die Bluetooth-Schnittstelle zu benutzen, fügen Sie, falls noch nicht vorhanden, folgende Zeilen in das Manifest der App hinzu.

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
Die MainActivity.java
package fliegl.com.tcb;

import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.ParcelUuid;
import android.support.v7.app.AppCompatActivity;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class TankDetailController extends AppCompatActivity {

    final static public UUID flieglCounterHD_UUID_1 = UUID.fromString("c13aaaa0-c497-4c95-8699-01b142af0c24");
    private static final String TAG = "Fliegl Counter HD: ";
    private static final int PERMISSION_REQUEST_COARSE_LOCATION = 1;

    private BluetoothAdapter mBluetoothAdapter;
    private int REQUEST_ENABLE_BT = 1;
    private Handler mHandler;
    private static final long SCAN_PERIOD = 30000;
    private BluetoothLeScanner mLEScanner;
    private ScanSettings settings;
    private List<ScanFilter> filters;
    private BluetoothGatt mGatt;

    private TextView logTextView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tank_detail_controller);

        logTextView = (TextView) findViewById(R.id.logTextView);
        logTextView.setText("Starting Example Application\n\n---\n ");
        logTextView.setMovementMethod(new ScrollingMovementMethod());

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            // Android M Permission check

            if (this.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)
            {
                final AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setTitle("This app needs location access");
                builder.setMessage("Please grant location access so this app can detect beacons.");
                builder.setPositiveButton(android.R.string.ok, null);
                builder.setOnDismissListener(new DialogInterface.OnDismissListener(){

                    public void onDismiss(DialogInterface dialog) {
                        requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSION_REQUEST_COARSE_LOCATION);
                    }
                });
                builder.show();
            }
        }

        mHandler = new Handler();
        if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            Toast.makeText(this, "BLE Not Supported",
                    Toast.LENGTH_SHORT).show();
            finish();
        }
        final BluetoothManager bluetoothManager =
                (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        mBluetoothAdapter = bluetoothManager.getAdapter();
    }

    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           String permissions[],
                                           int[] grantResults) {
        switch (requestCode) {
            case PERMISSION_REQUEST_COARSE_LOCATION: {
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                } else {
                    final AlertDialog.Builder builder = new AlertDialog.Builder(this);
                    builder.setTitle("Functionality limited");
                    builder.setMessage("Since location access has not been granted, this app will not be able to discover beacons when in the background.");
                    builder.setPositiveButton(android.R.string.ok, null);
                    builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
                        @Override
                        public void onDismiss(DialogInterface dialog) {
                        }
                    });
                    builder.show();
                }
                return;
            }
        }
    }

    private List<ScanFilter> scanFilters(UUID[] serviceUUIDs) {
        List<ScanFilter> list = new ArrayList<>();
        /*for (int i = 0; i < serviceUUIDs.length; i++) {
            ScanFilter filter = new ScanFilter.Builder().setServiceUuid(ParcelUuid.fromString(serviceUUIDs[i].toString())).build();
            list.add(filter);
        }*/
        return list;
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
        } else {
            if (Build.VERSION.SDK_INT >= 21) {
                mLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
                settings = new ScanSettings.Builder()
                        .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
                        .build();
                UUID[] myKnownServices = {flieglCounterHD_UUID_1};
                filters = scanFilters(myKnownServices);
            }
            scanLeDevice(true);
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
            scanLeDevice(false);
        }
    }

    @Override
    protected void onDestroy() {
        if (mGatt == null) {
            return;
        }
        mGatt.close();
        mGatt = null;
        super.onDestroy();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == REQUEST_ENABLE_BT) {
            if (resultCode == Activity.RESULT_CANCELED) {
                //Bluetooth not enabled.
                finish();
                return;
            }
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

    private void scanLeDevice(final boolean enable) {
        if (enable) {
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mLEScanner.stopScan(mScanCallback);
                }
            }, SCAN_PERIOD);
            mLEScanner.startScan(filters, settings, mScanCallback);
        } else {
            mLEScanner.stopScan(mScanCallback);
        }
    }

    private ScanCallback mScanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            BluetoothDevice btDevice = result.getDevice();

            List<ParcelUuid> uuids = result.getScanRecord().getServiceUuids();
            if(uuids != null)
            {
                if(!uuids.isEmpty())
                {
                    for(ParcelUuid uid : uuids)
                    {
                        if (uid.getUuid().toString().equalsIgnoreCase(flieglCounterHD_UUID_1.toString()))
                        {
                            if (btDevice.getName().length() > 0)
                                updateLogTextInUserInterface(btDevice.getName());
                            Log.i(TAG, "\n\n---> Found Counter Service UUID: \n" + uid.getUuid().toString());
                            updateLogTextInUserInterface("\nFound Counter Service UUID: \n" + uid.getUuid().toString());
                            connectToDevice(btDevice);
                        }
                    }
                }
            }
        }

        @Override
        public void onBatchScanResults(List<ScanResult> results) {
            for (ScanResult sr : results) {
            }
        }

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

    public void connectToDevice(BluetoothDevice device) {
        if (mGatt == null) {
            mGatt = device.connectGatt(this, false, gattCallback);
            scanLeDevice(false);// will stop after first device detection
        }
    }

    private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            switch (newState) {
                case BluetoothProfile.STATE_CONNECTED:
                    Log.i(TAG, "STATE_CONNECTED");
                    updateLogTextInUserInterface("State --> Connected");
                    gatt.discoverServices();
                    break;
                case BluetoothProfile.STATE_DISCONNECTED:
                    Log.e(TAG, "STATE_DISCONNECTED");
                    updateLogTextInUserInterface("State --> Disconnected");
                    scanLeDevice(false);
                    break;
                default:
                    Log.e(TAG, "STATE_OTHER");
                    updateLogTextInUserInterface("State --> Other");
            }

        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            List<BluetoothGattService> services = gatt.getServices();
            Log.i(TAG, services.toString());
            for(BluetoothGattService s : services)
            {
                Log.i(TAG ,"--> Discovered Service: \n" + s.getUuid().toString());
                updateLogTextInUserInterface("--> Discovered Service: \n" + s.getUuid().toString());

                for(BluetoothGattCharacteristic c : s.getCharacteristics())
                {
                    Log.i(TAG ,"--> Found Characteristic: " + c.getUuid().toString());
                    updateLogTextInUserInterface("--> Found Characteristic: \n" + c.getUuid().toString());
                    gatt.readCharacteristic(c);
                }
            }
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                                         BluetoothGattCharacteristic
                                                 characteristic, int status) {
            Log.i(TAG,"--> Read characteristic: \n" + characteristic.getUuid().toString());
            updateLogTextInUserInterface("--> Read characteristic: \n" + characteristic.getUuid().toString());
            Log.i(TAG,"--> Characteristic Value: \n" + characteristic.getValue().toString());
            updateLogTextInUserInterface("--> Characteristic Value: \n" + characteristic.getValue().toString());
        }
    };

    public void updateLogTextInUserInterface(final String appendingString)
    {
        runOnUiThread(new Runnable() {
            public void run() {
                logTextView.append("\n" + appendingString);
            }
        });
    }
}
Das User Interface
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="fliegl.com.tcb.TankDetailController"
    tools:showIn="@layout/activity_tank_detail_controller"
    android:scrollbars="vertical">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentTop="true"
        android:layout_alignParentStart="true">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Fliegl Counter HD Example"
            android:layout_alignParentTop="true"
            android:layout_alignParentStart="true"
            android:textStyle="normal|bold"
            android:textAlignment="center" />

        <TextView
            android:text="TextView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/logTextView"
            android:editable="false"
            android:layout_marginTop="17dp"
            android:layout_below="@+id/textView"
            android:layout_alignParentStart="true"
            android:scrollbarAlwaysDrawHorizontalTrack="true"
            android:scrollbars="vertical"
            android:scrollbarStyle="insideOverlay"
            android:scrollIndicators="right|start|end"
            android:scrollHorizontally="true"
            android:scrollbarAlwaysDrawVerticalTrack="true" />
    </LinearLayout>

</RelativeLayout>
Projekt als Download

Selbstverständlich stellen wir Ihnen hier das Beispiel als Download für Android Studio zur Verfügung.