/*
* 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.ui;
import java.util.ArrayList;
import mobisocial.metrics.MusubiMetrics;
import mobisocial.metrics.UsageMetrics;
import mobisocial.musubi.BJDNotImplementedException;
import mobisocial.musubi.R;
import mobisocial.musubi.model.DbContactAttributes;
import mobisocial.musubi.nearby.NearbyLookup;
import mobisocial.musubi.nearby.NearbyLookup.NearbyResultListener;
import mobisocial.musubi.nearby.broadcast.MulticastBroadcastTask;
import mobisocial.musubi.nearby.item.NearbyItem;
import mobisocial.musubi.util.ActivityCallout;
import mobisocial.musubi.util.BluetoothBeacon;
import mobisocial.musubi.util.InstrumentedActivity;
import mobisocial.musubi.util.Util;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;
/**
* Shows groups and users that have been found nearby.
*
* TODO: GetNearbyDevicesTask
* onDeviceDiscovered(User d) {
* }
*/
public class NearbyActivity extends MusubiBaseActivity implements
InstrumentedActivity, OnItemClickListener {
private static final String TAG = "Nearby";
private static boolean DBG = true;
private NearbyAdapter mAdapter;
private final ArrayList<NearbyItem> mNearbyList = new ArrayList<NearbyItem>();
private static final int RESULT_BT_ENABLE = 1;
private MulticastBroadcastTask mMulticastBroadcaster;
private WifiManager mWifiManager;
NearbyLookup.LookupFuture mNearbyLookup;
public void onClickHome (View v)
{
goHome (this);
}
public void goHome(Context context)
{
final Intent intent = new Intent(context, FeedListActivity.class);
if(Build.VERSION.SDK_INT < 11)
intent.setFlags (Intent.FLAG_ACTIVITY_CLEAR_TOP);
else
intent.setFlags (Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity (intent);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nearby);
mWifiManager = (WifiManager)getSystemService(Context.WIFI_SERVICE);
findViewById(R.id.qr).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
IntentIntegrator.initiateScan(NearbyActivity.this);
}
});
findViewById(R.id.qr).setVisibility(View.GONE);
findViewById(R.id.go).setVisibility(View.GONE);
DBG = MusubiBaseActivity.isDeveloperModeEnabled(this);
setTitle("Nearby");
mAdapter = new NearbyAdapter(this, R.layout.nearby_groups_item, mNearbyList);
ListView lv = (ListView)findViewById(android.R.id.list);
lv.setAdapter(mAdapter);
lv.setOnItemClickListener(this);
if (true) {
findViewById(R.id.social).setVisibility(View.GONE);
} else {
setupMulticastScanner();
}
EditText tv = (EditText)findViewById(R.id.password);
tv.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
@Override
public void afterTextChanged(Editable s) {
synchronized (NearbyActivity.this) {
mNearbyList.clear();
mAdapter.notifyDataSetChanged();
}
scanNearby();
}
});
if (savedInstanceState == null) {
UsageMetrics.getUsageMetrics(this).report(MusubiMetrics.VISITED_NEARBY);
}
String provider = Settings.Secure.getString(getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
if (provider == null || !provider.contains("gps") || !provider.contains("network")) {
new AlertDialog.Builder(this)
.setTitle("Location Settings")
.setMessage("You should enable both network-based and GPS-based location services to ensure you can find nearby groups.")
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
})
.setPositiveButton("Go to Settings", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
try {
Intent myIntent = new Intent( Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(myIntent);
} catch(Throwable t) { Log.e(TAG, "failed to launch location settings", t);}
}
}).show();
}
}
private void setupMulticastScanner() {
CheckBox checkbox = (CheckBox)findViewById(R.id.social);
mMulticastBroadcaster = MulticastBroadcastTask.getInstance(NearbyActivity.this);
if (mMulticastBroadcaster.isRunning()) {
checkbox.setChecked(true);
}
checkbox.setOnCheckedChangeListener(
new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// TODO: Generalize to NearbyBroadcaster; do multicast, bt, gps, dns
if (buttonView.isChecked()) {
if (mMulticastBroadcaster == null) {
mMulticastBroadcaster =
MulticastBroadcastTask.getInstance(NearbyActivity.this);
}
mMulticastBroadcaster.execute();
} else {
mMulticastBroadcaster.cancel(true);
mMulticastBroadcaster = null;
}
}
});
}
@Override
public boolean onCreateOptionsMenu(android.support.v4.view.Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.nearby_activity, menu);
menu.findItem(R.id.menu_pin).setVisible(false);
return true;
}
@Override
public boolean onOptionsItemSelected(android.support.v4.view.MenuItem item) {
Log.d(TAG, " item " + item);
switch (item.getItemId()) {
case R.id.menu_refresh: {
synchronized (NearbyActivity.this) {
mNearbyList.clear();
mAdapter.notifyDataSetChanged();
}
scanNearby();
return true;
}
case R.id.menu_pin: {
doCheckin();
return true;
}
}
return super.onOptionsItemSelected(item);
}
private void doCheckin() {
WifiInfo wifi = mWifiManager.getConnectionInfo();
String myWifiName = wifi.getSSID();
if (myWifiName == null) {
/**
* TODO:
* Depending on desired outcome, either help them get on wifi
* or try connecting to gps server, etc.
*/
toast("No wifi network available.");
return;
}
String myWifiId = wifi.getBSSID();
String myIp = formatIp(wifi.getIpAddress());
String myWifiFingerprint = Util.computeWifiFingerprint(mWifiManager.getScanResults());
if (DBG) Log.d(TAG, "Checking in to " + myWifiName + "...");
JSONObject loc = new JSONObject();
try {
loc.put(DbContactAttributes.ATTR_WIFI_SSID, myWifiName);
if (myWifiId != null) {
loc.put(DbContactAttributes.ATTR_WIFI_BSSID, myWifiId);
}
if (myIp != null) {
loc.put(DbContactAttributes.ATTR_LAN_IP, myIp);
}
loc.put(DbContactAttributes.ATTR_WIFI_FINGERPRINT, myWifiFingerprint);
} catch (JSONException e) {
// Impossible json exception
}
//XXX killed for now
//mMusubi.getAppFeed().postObj(new MemObj("locUpdate", loc));
//toast("Checked in to '" + myWifiName + "'.");
}
private String formatIp(int ip) {
return String.format("%d.%d.%d.%d",
(ip & 0xff), (ip >> 8 & 0xff), (ip >> 16 & 0xff), (ip >> 24 & 0xff));
}
private void scanNearby() {
if (DBG) Log.d(TAG, "initiating nearby scan...");
String password = ((EditText) findViewById(R.id.password)).getText().toString();
if (mNearbyLookup != null) {
mNearbyLookup.cancel(true);
}
NearbyLookup lookup = new NearbyLookup(this, password);
mNearbyLookup = lookup.doLookup(mLookupResultListener);
}
@Override
protected void onResume() {
super.onResume();
mAdapter.notifyDataSetChanged();
scanNearby();
}
@Override
protected void onPause() {
super.onPause();
if (mNearbyLookup != null) {
mNearbyLookup.cancel(true);
mNearbyLookup = null;
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_ACTIVITY_CALLOUT) {
mCurrentCallout.handleResult(resultCode, data);
return;
}
IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
if (result != null && result.getContents() != null) {
try {
Uri uri = Uri.parse(result.getContents());
Intent i = new Intent(Intent.ACTION_VIEW, uri);
i.setPackage(getPackageName());
startActivity(i);
finish();
} catch (IllegalArgumentException e) {
}
return;
}
if (requestCode == RESULT_BT_ENABLE) {
if (resultCode == Activity.RESULT_CANCELED) {
finish();
} else {
findBluetooth();
}
}
}
private NearbyResultListener mLookupResultListener = new NearbyResultListener() {
@Override
public void onItemDiscovered(NearbyItem item) {
mNearbyList.add(item);
mAdapter.notifyDataSetChanged();
}
@Override
public void onDiscoveryComplete() {
}
@Override
public void onDiscoveryBegin() {
mNearbyList.clear();
mAdapter.notifyDataSetChanged();
}
};
private class NearbyAdapter extends ArrayAdapter<NearbyItem> {
private ArrayList<NearbyItem> nearby;
public NearbyAdapter(Context context, int textViewResourceId, ArrayList<NearbyItem> groups) {
super(context, textViewResourceId, groups);
this.nearby = groups;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if (convertView == null) {
LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = vi.inflate(R.layout.nearby_groups_item, null);
}
final NearbyItem g = nearby.get(position);
TextView text = (TextView) row.findViewById(R.id.name_text);
text.setText(g.name);
TextView dtext = (TextView) row.findViewById(R.id.detail_text);
dtext.setText(g.getDetail());
((ImageView)row.findViewById(R.id.icon)).setImageBitmap(g.getIcon());
return row;
}
}
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
NearbyItem g = mAdapter.getItem(position);
g.view(this);
}
private void findBluetooth() {
if (!BluetoothAdapter.getDefaultAdapter().isEnabled()) {
Intent bt = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(bt, RESULT_BT_ENABLE);
return;
}
// Create a BroadcastReceiver for ACTION_FOUND
final IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
final BroadcastReceiver receiver = new BroadcastReceiver() {
public void onReceive(final Context context, final Intent intent) {
String action = intent.getAction();
// When discovery finds a device
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
new Thread() {
public void run() {
BluetoothBeacon.OnDiscovered discovered = new BluetoothBeacon.OnDiscovered() {
@Override
public void onDiscovered(final byte[] data) {
runOnUiThread(new Runnable() {
@Override
public void run() {
try {
JSONObject obj = new JSONObject(new String(data));
BJDNotImplementedException.except("bluetooth not implemented");
// mNearbyList.add(new NearbyFeed(NearbyActivity.this,
// obj.getString("name"), Uri.parse(obj
// .getString("dynuri"))));
mAdapter.notifyDataSetChanged();
} catch (JSONException e) {
Log.e(TAG,
"Error getting group info over bluetooth",
e);
}
}
});
}
};
// Get the BluetoothDevice object from the Intent
BluetoothDevice device = intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
BluetoothBeacon.discover(NearbyActivity.this, device, discovered);
};
}.start();
}
if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
unregisterReceiver(this);
}
}
};
registerReceiver(receiver, filter); // Don't forget to unregister during
// onDestroy
BluetoothAdapter.getDefaultAdapter().startDiscovery();
Toast.makeText(this, "Scanning Bluetooth...", 500).show();
}
private static int REQUEST_ACTIVITY_CALLOUT = 39;
private static ActivityCallout mCurrentCallout;
public void doActivityForResult(ActivityCallout callout) {
mCurrentCallout = callout;
Intent launch = callout.getStartIntent();
startActivityForResult(launch, REQUEST_ACTIVITY_CALLOUT);
}
}