| /* |
| * 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); |
| } |
| } |
| } |
| } |