blob: 62520e0c19feddbf76245acc9891f08cc5d70e10 [file] [log] [blame]
/*
* 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 com.google.samples.apps.iosched.nearby;
import android.annotation.TargetApi;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.os.Build;
import android.os.Handler;
import android.util.Log;
import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;
import static com.google.samples.apps.iosched.util.LogUtils.makeLogTag;
/**
* Keeps track of all devices nearby.
*
* Posts notifications when a new device is near, or if an old device is not
* longer nearby.
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public class NearbyDeviceManager {
private String TAG = makeLogTag("NearbyDeviceManager");
private BluetoothAdapter mBluetoothAdapter;
private Timer mExpireTimer;
private Timer mSearchTimer;
private Handler mQueryHandler;
private boolean mIsSearching = false;
private NearbyDeviceAdapter mNearbyDeviceAdapter;
private ArrayList<NearbyDevice> mDeviceBatchList;
private Activity mActivity;
private boolean mIsQueuing = false;
// How often we should batch requests for metadata.
private int QUERY_PERIOD = 500;
// How often to search for new devices (ms).
private int SEARCH_PERIOD = 5000;
// How often to check for expired devices.
private int EXPIRE_PERIOD = 3000;
// How much time has to pass with a nearby device not being discovered before
// we declare it gone.
public static int MAX_INACTIVE_TIME = 10000;
public NearbyDeviceManager(Activity activity, BluetoothAdapter adapter) {
mBluetoothAdapter = adapter;
mDeviceBatchList = new ArrayList<NearbyDevice>();
mNearbyDeviceAdapter = new NearbyDeviceAdapter(activity);
mQueryHandler = new Handler();
mActivity = activity;
}
public boolean isEnabled() {
return mBluetoothAdapter.isEnabled();
}
// Begins searching for nearby devices if not already searching. If the adapter is disabled,
// does nothing.
public void startSearchingForDevices() {
if (mIsSearching || !mBluetoothAdapter.isEnabled()) {
return;
}
mIsSearching = true;
// Start a timer to do scans.
if (mSearchTimer == null) {
mSearchTimer = new Timer();
mSearchTimer.scheduleAtFixedRate(new SearchTask(), 0, SEARCH_PERIOD);
}
// Start a timer to check for expired devices.
if (mExpireTimer == null) {
mExpireTimer = new Timer();
mExpireTimer.scheduleAtFixedRate(new ExpireTask(), 0, EXPIRE_PERIOD);
}
}
public void stopSearchingForDevices() {
if (!mIsSearching) {
return;
}
mIsSearching = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
// Stop expired device timer.
mExpireTimer.cancel();
mExpireTimer.purge();
mExpireTimer = null;
mSearchTimer.cancel();
mSearchTimer.purge();
mSearchTimer = null;
}
public NearbyDeviceAdapter getAdapter() {
return mNearbyDeviceAdapter;
}
private class SearchTask extends TimerTask {
@Override
public void run() {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
boolean result = mBluetoothAdapter.startLeScan(mLeScanCallback);
if (!result) {
Log.e(TAG, "startLeScan failed.");
}
}
}
private class ExpireTask extends TimerTask {
@Override
public void run() {
mNearbyDeviceAdapter.removeExpiredDevices();
}
}
private Runnable mBatchMetadataRunnable = new Runnable () {
@Override
public void run() {
batchFetchMetaData();
mIsQueuing = false;
}
};
private void batchFetchMetaData() {
if(mDeviceBatchList.size() > 0) {
MetadataResolver.getBatchMetadata(mDeviceBatchList);
mDeviceBatchList = new ArrayList<NearbyDevice>(); // Clear out the list
}
}
// NearbyDevice scan callback.
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, final int RSSI, byte[] scanRecord) {
Log.i(TAG, String.format("onLeScan: %s, RSSI: %d", device.getName(), RSSI));
if (device.getName() == null) {
return;
}
NearbyDevice candidateNearbyDevice = new NearbyDevice(device, RSSI);
handleDeviceFound(candidateNearbyDevice);
}
};
private void handleDeviceFound(NearbyDevice candidateNearbyDevice) {
NearbyDevice nearbyDevice = mNearbyDeviceAdapter.getExistingDevice(candidateNearbyDevice);
// Check if this is a new device.
if (nearbyDevice != null) {
// For existing devices, update their RSSI.
nearbyDevice.updateLastSeen(candidateNearbyDevice.getLastRSSI());
mNearbyDeviceAdapter.updateListUI();
} else {
// For new devices, add the device to the adapter.
nearbyDevice = candidateNearbyDevice;
if (nearbyDevice.isBroadcastingUrl()) {
if (!mIsQueuing) {
mIsQueuing = true;
// We wait QUERY_PERIOD ms to see if any other devices are discovered so we can batch.
mQueryHandler.postAtTime(mBatchMetadataRunnable, QUERY_PERIOD);
}
// Add the device to the queue of devices to look for.
mDeviceBatchList.add(nearbyDevice);
mNearbyDeviceAdapter.addDevice(nearbyDevice);
}
}
}
}