physical_web.org.physicalweb.DeviceDiscoveryService.java Source code

Java tutorial

Introduction

Here is the source code for physical_web.org.physicalweb.DeviceDiscoveryService.java

Source

/*
 * Copyright 2014 Google Inc. All rights reserved.
 *
 * 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 physical_web.org.physicalweb;

import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import org.uribeacon.beacon.UriBeacon;
import org.uribeacon.scan.compat.BluetoothLeScannerCompat;
import org.uribeacon.scan.compat.BluetoothLeScannerCompatProvider;
import org.uribeacon.scan.compat.ScanCallback;
import org.uribeacon.scan.compat.ScanFilter;
import org.uribeacon.scan.compat.ScanResult;
import org.uribeacon.scan.compat.ScanSettings;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

/**
 * This is the services that scans for devices.
 * When the application loads, it checks
 * if the service is running and if it is not,
 * the applications creates the service (from MainActivity).
 * The service finds nearby ble devices,
 * and stores a count of them.
 * Also, the service listens for screen on/off events
 * and start/stops the scanning accordingly.
 * Also, this service issues a notification
 * informing the user of nearby devices.
 * As devices are found and lost,
 * the notification is updated to reflect
 * the current number of nearby devices.
 */

public class DeviceDiscoveryService extends Service {

    private static String TAG = "DeviceDiscoveryService";
    private static int ID_NOTIFICATION = 23;
    private ScreenBroadcastReceiver mScreenStateBroadcastReceiver;
    private HashSet<String> mDeviceAddressesFound;

    public DeviceDiscoveryService() {
    }

    private void initialize() {
        mDeviceAddressesFound = new HashSet<>();
        initializeScreenStateBroadcastReceiver();
        startSearchingForDevices();
    }

    /**
     * Create the broadcast receiver that will listen
     * for screen on/off events
     */
    private void initializeScreenStateBroadcastReceiver() {
        mScreenStateBroadcastReceiver = new ScreenBroadcastReceiver();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
        registerReceiver(mScreenStateBroadcastReceiver, intentFilter);
    }

    /////////////////////////////////
    // accessors
    /////////////////////////////////

    private BluetoothLeScannerCompat getLeScanner() {
        return BluetoothLeScannerCompatProvider.getBluetoothLeScannerCompat(getApplicationContext());
    }

    /////////////////////////////////
    // callbacks
    /////////////////////////////////

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        initialize();
        //make sure the service keeps running
        return START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public void onDestroy() {
        stopSearchingForDevices();
    }

    private final ScanCallback mScanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult scanResult) {
            String address = scanResult.getDevice().getAddress();
            switch (callbackType) {
            case ScanSettings.CALLBACK_TYPE_FIRST_MATCH:
                if (mDeviceAddressesFound.add(address)) {
                    updateNearbyDevicesNotification();
                }
                break;
            case ScanSettings.CALLBACK_TYPE_MATCH_LOST:
                if (mDeviceAddressesFound.remove(address)) {
                    updateNearbyDevicesNotification();
                }
                break;
            default:
                Log.e(TAG, "Unrecognized callback type constant received: " + callbackType);
            }
        }

        @Override
        public void onScanFailed(int errorCode) {
            Log.d(TAG, "onScanFailed  " + "errorCode: " + errorCode);
        }
    };

    /**
     * This is the class that listens for screen on/off events
     */
    private class ScreenBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            boolean isScreenOn = Intent.ACTION_SCREEN_ON.equals(intent.getAction());
            if (isScreenOn) {
                startSearchingForDevices();
            } else {
                stopSearchingForDevices();
            }
        }
    }

    /////////////////////////////////
    // utilities
    /////////////////////////////////

    private void startSearchingForDevices() {
        Log.v(TAG, "startSearchingForDevices");

        ScanSettings settings = new ScanSettings.Builder()
                .setCallbackType(ScanSettings.CALLBACK_TYPE_FIRST_MATCH | ScanSettings.CALLBACK_TYPE_MATCH_LOST)
                .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER).build();

        List<ScanFilter> filters = new ArrayList<>();

        ScanFilter filter = new ScanFilter.Builder()
                .setServiceData(UriBeacon.URI_SERVICE_UUID, new byte[] {}, new byte[] {}).build();

        filters.add(filter);

        boolean started = getLeScanner().startScan(filters, settings, mScanCallback);
        Log.v(TAG, started ? "... scan started" : "... scan NOT started");
    }

    private void stopSearchingForDevices() {
        Log.v(TAG, "stopSearchingForDevices");
        getLeScanner().stopScan(mScanCallback);
    }

    /**
     * Update the notification that displays
     * the number of nearby devices.
     * If there are no devices the notification
     * is removed.
     */
    private void updateNearbyDevicesNotification() {
        // Create the notification builder
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
        // Set the notification icon
        builder.setSmallIcon(R.drawable.ic_notification);

        // Set the title
        String contentTitle = "";
        // Get the number of current nearby devices
        int numNearbyDevices = mDeviceAddressesFound.size();

        // If there are no nearby devices
        if (numNearbyDevices == 0) {
            // Remove the notification
            NotificationManager mNotificationManager = (NotificationManager) getSystemService(
                    Context.NOTIFICATION_SERVICE);
            mNotificationManager.cancel(ID_NOTIFICATION);
            return;
        }

        // Add the ending part of the title
        // which is either singular or plural
        // based on the number of devices
        contentTitle += String.valueOf(numNearbyDevices) + " ";
        Resources resources = getResources();
        contentTitle += resources.getQuantityString(R.plurals.numFoundBeacons, numNearbyDevices, numNearbyDevices);
        builder.setContentTitle(contentTitle);

        // Have the app launch when the user taps the notification
        Intent resultIntent = new Intent(this, MainActivity.class);
        int requestID = (int) System.currentTimeMillis();
        PendingIntent resultPendingIntent = PendingIntent.getActivity(this, requestID, resultIntent, 0);

        // Build the notification
        builder.setContentIntent(resultPendingIntent);
        NotificationManager mNotificationManager = (NotificationManager) getSystemService(
                Context.NOTIFICATION_SERVICE);
        mNotificationManager.notify(ID_NOTIFICATION, builder.build());
    }
}