/*
* Copyright 2012 The Stanford MobiSocial Laboratory
*
* 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 mobisocial.musubi.nearby;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import mobisocial.musubi.nearby.item.NearbyItem;
import mobisocial.musubi.nearby.scanner.GpsScannerTask;
import mobisocial.musubi.nearby.scanner.NearbyScannerTask;
import mobisocial.musubi.ui.MusubiBaseActivity;
import android.app.Activity;
import android.content.Context;
import android.net.Uri;
import android.net.wifi.WifiManager;
import android.util.Log;
/**
* An AsyncTask for discovering nearby users and feeds.
*/
public class NearbyLookup {
private static final boolean DBG = MusubiBaseActivity.DBG;
private static final String TAG = "NearbyLookup";
private final Activity mContext;
private final String mGpsPassword;
public NearbyLookup(Activity context) {
this(context, null);
}
public NearbyLookup(Activity context, String gpsPassword) {
mContext = context;
mGpsPassword = gpsPassword;
}
/**
* Initiates a lookup for nearby people, devices, and feeds.
* @param listener an (optional) callback for handling discovered results
*/
public LookupFuture doLookup(NearbyResultListener listener) {
return new LookupFuture(this, listener);
}
public interface NearbyResultListener {
public void onDiscoveryBegin();
public void onDiscoveryComplete();
public void onItemDiscovered(NearbyItem item);
}
public class LookupFuture implements Future<List<NearbyItem>> {
private final List<NearbyScannerTask> mScannerTasks = new ArrayList<NearbyScannerTask>();
private int mCompletedDiscoveries = 0;
private NearbyLookup mLookup;
private boolean mCancelled = false;
private boolean mDone = false;
private NearbyResultListener mSuppliedListener;
private final Set<Uri> mNearbyUris = new HashSet<Uri>();
private final List<NearbyItem> mNearbyList = new LinkedList<NearbyItem>();
private final NearbyResultListener mInternalListener = new NearbyResultListener() {
@Override
public synchronized void onItemDiscovered(NearbyItem item) {
if (DBG) Log.d(TAG, "Discovered nearby item " + item);
if (!mNearbyUris.contains(item.uri)) {
mNearbyUris.add(item.uri);
mNearbyList.add(item);
if (mSuppliedListener != null) {
mSuppliedListener.onItemDiscovered(item);
}
}
}
public void onDiscoveryComplete() {
if (++mCompletedDiscoveries == mScannerTasks.size()) {
if (mSuppliedListener != null) {
mSuppliedListener.onDiscoveryComplete();
}
synchronized (mInternalListener) {
notify();
}
}
}
@Override
public void onDiscoveryBegin() {
if (mSuppliedListener != null) {
mSuppliedListener.onDiscoveryBegin();
}
}
};
private LookupFuture(NearbyLookup lookup, NearbyResultListener listener) {
mLookup = lookup;
mSuppliedListener = listener;
Activity context = mLookup.mContext;
WifiManager wifi = (WifiManager)context.getSystemService(Context.WIFI_SERVICE);
mScannerTasks.add(new GpsScannerTask(context, mLookup.mGpsPassword));
// mScannerTasks.add(new MulticastScannerTask(context, wifi));
// mScannerTasks.add(new AttributeScannerTask(context, wifi));
for (NearbyScannerTask task : mScannerTasks) {
task.setNearbyResultListener(mInternalListener);
mInternalListener.onDiscoveryBegin();
task.execute();
}
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
if (mDone) {
return false;
}
boolean cancelled = true;
for (NearbyScannerTask task : mScannerTasks) {
cancelled &= task.cancel(true);
}
mCancelled = cancelled;
return mCancelled;
}
@Override
public List<NearbyItem> get() throws InterruptedException, ExecutionException {
synchronized (mInternalListener) {
while (!mDone) {
try {
wait();
} catch (InterruptedException e) {}
}
}
return mNearbyList;
}
@Override
public List<NearbyItem> get(long timeout, TimeUnit unit) throws InterruptedException,
ExecutionException, TimeoutException {
synchronized (mInternalListener) {
while (!mDone) {
try {
wait(unit.convert(timeout, TimeUnit.MILLISECONDS));
} catch (InterruptedException e) {}
}
}
return mNearbyList;
}
@Override
public boolean isCancelled() {
return mCancelled;
}
@Override
public boolean isDone() {
return mDone;
}
public int getDiscoveredItemCount() {
return mNearbyList.size();
}
public List<NearbyItem> getDiscoveredItems() {
ArrayList<NearbyItem> items;
synchronized (mInternalListener) {
items = new ArrayList<NearbyItem>(mNearbyList.size());
items.addAll(mNearbyList);
}
return items;
}
}
}