I am using the proximity-reference-android sample application provided by Radius Network to detect an iBeacon. I have an iPad configured as an iBeacon and have added around 3 beacon regions in the proximity kit. The problem I am facing right now I am unable to fetch the Beacon name ,and the additional url which I had in the proximity kit in Android.
I need to basically show up the url associated with beacon region in that proximity kit in Android App just like how the iOS application does.
While Debugging I had checked that even after the beacon is detected in the application,the didEnterRegion doesn't get called.I need to basically save the details of that particular beacon in the database once it is detected.
Neither is the application calling didExitRegion.
Posting the below code,please let me know what I am doing wrong in this.
public class AndroidProximityReferenceApplication extends Application implements
BootstrapNotifier {
private static final String TAG = "AndroidProximityReferenceApplication";
private RegionBootstrap regionBootstrap;
private BackgroundPowerSaver backgroundPowerSaver;
private boolean haveDetectedIBeaconsSinceBoot = false;
public void onCreate() {
super.onCreate();
Log.d(TAG,
"setting up background monitoring for iBeacons and power saving");
// wake up the app when an iBeacon is seen
Region region = new Region(
"com.radiusnetworks.androidproximityreference.backgroundRegion",
"2F234454-CF6D-4A0F-ADF2-F4911BA9FFA6", null, null);
regionBootstrap = new RegionBootstrap(this, region);
// simply constructing this class and holding a reference to it in your
// custom Application
// class will automatically cause the iBeaconLibrary to save battery
// whenever the application
// is not visible. This reduces bluetooth power usage by about 60%
backgroundPowerSaver = new BackgroundPowerSaver(this);
}
#Override
public void didDetermineStateForRegion(int arg0, Region arg1) {
// This method is not used in this example
}
#Override
public void didEnterRegion(Region arg0) {
// In this example, this class sends a notification to the user whenever
// an iBeacon
// matching a Region (defined above) are first seen.
Log.d(TAG, "did enter region.");
if (!haveDetectedIBeaconsSinceBoot) {
Log.d(TAG, "auto launching MainActivity");
// The very first time since boot that we detect an iBeacon, we
// launch the
// MainActivity
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// Important: make sure to add android:launchMode="singleInstance"
// in the manifest
// to keep multiple copies of this activity from getting created if
// the user has
// already manually launched the app.
this.startActivity(intent);
haveDetectedIBeaconsSinceBoot = true;
} else {
// If we have already seen iBeacons and launched the MainActivity
// before, we simply
// send a notification to the user on subsequent detections.
Log.d(TAG, "Sending notification.");
ParseObject beacon = new ParseObject("Beacon");
beacon.put("beacon_name", arg0.getClass().getName());
beacon.put("beacon_id", arg0.getUniqueId());
beacon.put("device_type", "Android");
beacon.put("device_UUID", android.os.Build.MODEL);
beacon.put("beacon_status", "ENTRY");
beacon.saveInBackground();
sendNotification();
}
}
#Override
public void didExitRegion(Region arg0) {
Log.d(TAG, "exited region");
ParseObject beacon = new ParseObject("Beacon");
beacon.put("beacon_name", arg0.getClass().getName());
beacon.put("beacon_id", arg0.getUniqueId());
beacon.put("device_type", "Android");
beacon.put("device_UUID", android.os.Build.MODEL);
beacon.put("beacon_status", "ENTRY");
beacon.saveInBackground();
}
private void sendNotification() {
NotificationCompat.Builder builder = new NotificationCompat.Builder(
this).setContentTitle("Proximity Reference Application")
.setContentText("An iBeacon is nearby.")
.setSmallIcon(R.drawable.ic_launcher);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addNextIntent(new Intent(this, MainActivity.class));
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0,
PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(resultPendingIntent);
NotificationManager notificationManager = (NotificationManager) this
.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(1, builder.build());
}
}
The code below is of the mainActivity class
public class MainActivity extends Activity implements IBeaconConsumer,
RangeNotifier, IBeaconDataNotifier {
public static final String TAG = "MainActivity";
IBeaconManager iBeaconManager;
Map<String, TableRow> rowMap = new HashMap<String, TableRow>();
#Override
protected void onCreate(Bundle savedInstanceState) {
Parse.initialize(this, "test123",
"test345");
IBeaconManager.LOG_DEBUG = true;
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iBeaconManager = IBeaconManager.getInstanceForApplication(this
.getApplicationContext());
iBeaconManager.bind(this);
}
#Override
public void onIBeaconServiceConnect() {
Region region = new Region("MainActivityRanging", null, null, null);
try {
iBeaconManager.startRangingBeaconsInRegion(region);
iBeaconManager.setRangeNotifier(this);
} catch (RemoteException e) {
e.printStackTrace();
}
}
#Override
public void onDestroy() {
super.onDestroy();
iBeaconManager.unBind(this);
}
#Override
public void didRangeBeaconsInRegion(Collection<IBeacon> iBeacons,
Region region) {
for (IBeacon iBeacon : iBeacons) {
iBeacon.requestData(this);
Log.d(TAG, "I see an iBeacon: " + iBeacon.getProximityUuid() + ","
+ iBeacon.getMajor() + "," + iBeacon.getMinor());
String displayString = iBeacon.getProximityUuid() + " "
+ iBeacon.getMajor() + " " + iBeacon.getMinor() + "\n";
displayTableRow(iBeacon, displayString, false);
}
}
#Override
public void iBeaconDataUpdate(IBeacon iBeacon, IBeaconData iBeaconData,
DataProviderException e) {
if (e != null) {
Log.d(TAG, "data fetch error:" + e);
}
if (iBeaconData != null) {
Log.d(TAG,
"I have an iBeacon with data: uuid="
+ iBeacon.getProximityUuid() + " major="
+ iBeacon.getMajor() + " minor="
+ iBeacon.getMinor() + " welcomeMessage="
+ iBeaconData.get("welcomeMessage"));
String displayString = iBeacon.getProximityUuid() + " "
+ iBeacon.getMajor() + " " + iBeacon.getMinor() + "\n"
+ "Welcome message:" + iBeaconData.get("welcomeMessage");
displayTableRow(iBeacon, displayString, true);
}
}
private void displayTableRow(final IBeacon iBeacon,
final String displayString, final boolean updateIfExists) {
runOnUiThread(new Runnable() {
#Override
public void run() {
TableLayout table = (TableLayout) findViewById(R.id.beacon_table);
String key = iBeacon.getProximity() + "-" + iBeacon.getMajor()
+ "-" + iBeacon.getMinor();
TableRow tr = (TableRow) rowMap.get(key);
if (tr == null) {
tr = new TableRow(MainActivity.this);
tr.setLayoutParams(new TableRow.LayoutParams(
TableRow.LayoutParams.WRAP_CONTENT,
TableRow.LayoutParams.WRAP_CONTENT));
rowMap.put(key, tr);
table.addView(tr);
} else {
if (updateIfExists == false) {
return;
}
}
tr.removeAllViews();
TextView textView = new TextView(MainActivity.this);
textView.setText(displayString);
tr.addView(textView);
}
});
}
}
Any help would be appreciated.Thanks :)
When using Proximity Kit for Android, there are two separate sets of APIs available. One set uses a ProximityKitManager, and it is intended for simpler use cases where you can pre-configure all of your iBeacon identifiers and associated data server-side, and let the ProximityKitManager handle setting up ranging and monitoring in a global Application class.
The second set of APIs use the IBeaconManager and provide more fine-grained control. But because the ProximityKitManager uses the IBeaconManager under the hood, you should not use both at the same time, because you can easily break the automatic configuration done by ProximityKitManager. I suspect that is what is causing your problem, because the code uses the ProximityKitManager in the Application class and IBeaconManager in the Activity class. See here for more info.
If you need to track beacons regardless of the identifiers set up in ProximityKit, but still want to access the data configured for certain iBeacons in ProximityKit, you should not use the ProximityKitManager and instead just use the IBeaconManager. There is a reference application that shows how to access ProximityKit data using this API available here.
Related
I have trouble with showing my app on region entered, I followed the sample here.
It works when I don't bind the BeaconManager to my Activty, but when I do, I get the logs from the Activity not from the Application and the app doesn't show up although it's not visible.
So the question is, can I use a class extends Application implements BootstrapNotifier for starting the app in background and a class extends AppCompatActivity implements BeaconConsumer to handle monitoring/ranging or do I need to handle everything in the application class because binding it in the activity fails the background launch?
(Sorry for the bad code block, I just can't handle this stackoverflow code thing correctly)
public class BeaconActivity extends AppCompatActivity implements BeaconConsumer {
protected final String TAG = "BeaconActivity";
private BeaconManager beaconManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate()");
setContentView(R.layout.activity_beacon);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
RangedBeacon.setSampleExpirationMilliseconds(8000);
beaconManager = BeaconManager.getInstanceForApplication(this);
beaconManager.bind(this);
}
#Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "Unbind Beaconmanager");
beaconManager.unbind(this);
}
#Override
public void onBeaconServiceConnect() {
beaconManager.setMonitorNotifier(new MonitorNotifier() {
#Override
public void didEnterRegion(final Region region) {
Log.d(TAG, "Enter Region identifier: " + region.getId1() + ", " + region.getId2() + ", " + region.getId3());
}
#Override
public void didExitRegion(final Region region) {
Log.d(TAG, "Exit Region identifier: " + region.getId1() + ", " + region.getId2() + ", " + region.getId3());
}
#Override
public void didDetermineStateForRegion(int state, Region region) {
}
});
beaconManager.setRangeNotifier(new RangeNotifier() {
#Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
for (final Beacon beacon : beacons) {
final String distance = new DecimalFormat("##.######").format(beacon.getDistance());
Log.d(TAG, "Distance: " + distance + ". This beacon has identifiers:" + beacon.getId1() + ", " + beacon.getId2() + ", " + beacon.getId3());
}
}
});
try {
Region region = new Region("all", null, null, null);
beaconManager.startMonitoringBeaconsInRegion(all);
beaconManager.startRangingBeaconsInRegion(all);
} catch (RemoteException e) {
}
}
}
public class BeaconApplication extends Application implements BootstrapNotifier {
private static final String TAG = "BeaconApplication";
private RegionBootstrap regionBootstrap;
#Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "App started up");
BeaconManager beaconManager = BeaconManager.getInstanceForApplication(this);
beaconManager.getBeaconParsers().add(new BeaconParser().
setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25"));
Region all = new Region("all", null, null, null);
regionBootstrap = new RegionBootstrap(this, all);
}
#Override
public void didEnterRegion(Region region) {
Log.d(TAG, "Enter region " + region.getUniqueId());
// regionBootstrap.disable();
Intent intent = new Intent(this, BeaconActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
this.startActivity(intent);
}
#Override
public void didExitRegion(Region region) {
Log.d(TAG, "Exit region " + region.getUniqueId());
}
#Override
public void didDetermineStateForRegion(int i, Region region) {
}
}
Edit:
I think I might just screwed up a little. Explained here the app only launches if killed (not in task switcher anymore). If it's available in task switcher it just handles the incoming events in the background, that's why I get the activity logs.
The problem is that you can only have one monitorNotifier for your application. The RegionBootstrap class sets one internally, and uses it to get callbacks to your Application class.
The code shown above changes the monitorNotifier here:
public void onBeaconServiceConnect() {
beaconManager.setMonitorNotifier(new MonitorNotifier() {
...
After that line executes, the RegionBootstrap won't get any more callbacks on entry/exit events, and nor will the Application class. So the problem isn't the bind() call, it's the setMonitorNotifier call.
The library does not allow multiple monitorNotifiers, so if you want to keep using the RegionBootstrap you have to refactor your code so that you get all of the entry/exit events in your Application class and somehow pass them off to your Activity.
What is the minimum required code to establish a PubNub subscription in a service class? The examples on PubNub include code for on boot subscriptions, broadcast receivers, pushalarms, etc. Am I to believe that all this code from github is the minimum required?
The reason I ask is because I am self-learning code and having a rather rough time implementing services such as PubNub because their documentations are for a level of programmer that I haven't reached yet.
I look at the examples and try to extract just the very basic, bare necessities but I am unsure of what can be stripped from those example classes.
Thank you to someone who understands what I am trying to ask.
EDIT: To be clear this is my current PubNub service class:
public class PubNubService extends Service {
SharedPreferences sP;
static final String pub_key = " - ";
static final String sub_key = " - ";
Pubnub pubnub = new Pubnub(pub_key, sub_key, false);
String channel;
PowerManager.WakeLock wl = null;
private final Handler handler = new Handler() {
public void handleMessage(Message msg) {
String pnMsg = msg.obj.toString();
final Toast toast = Toast.makeText(getApplicationContext(), pnMsg, Toast.LENGTH_SHORT);
toast.show();
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
toast.cancel();
}
}, 200);
}
};
private void notifyUser(Object message) {
Message msg = handler.obtainMessage();
try {
final String obj = (String) message;
msg.obj = obj;
handler.sendMessage(msg);
Log.i("Received msg : ", obj.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
public void onCreate() {
super.onCreate();
Toast.makeText(this, "PubnubService created...", Toast.LENGTH_LONG).show();
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SubscribeAtBoot");
if (wl != null) {
wl.acquire();
Log.i("PUBNUB", "Partial Wake Lock : " + wl.isHeld());
Toast.makeText(this, "Partial Wake Lock : " + wl.isHeld(), Toast.LENGTH_LONG).show();
}
Log.i("PUBNUB", "PubnubService created...");
try {
pubnub.subscribe(new String[] {channel}, new Callback() {
public void connectCallback(String channel) {
notifyUser("CONNECT on channel:" + channel);
}
public void disconnectCallback(String channel) {
notifyUser("DISCONNECT on channel:" + channel);
}
public void reconnectCallback(String channel) {
notifyUser("RECONNECT on channel:" + channel);
}
#Override
public void successCallback(String channel, Object message) {
notifyUser(channel + " " + message.toString());
}
#Override
public void errorCallback(String channel, Object message) {
notifyUser(channel + " " + message.toString());
}
});
} catch (PubnubException e) {
}
}
#Override
public void onDestroy() {
super.onDestroy();
if (wl != null) {
wl.release();
Log.i("PUBNUB", "Partial Wake Lock : " + wl.isHeld());
Toast.makeText(this, "Partial Wake Lock : " + wl.isHeld(), Toast.LENGTH_LONG).show();
wl = null;
}
Toast.makeText(this, "PubnubService destroyed...", Toast.LENGTH_LONG).show();
}
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
This service above is copied from this example. I call to start this service from my MainActivity. I call it like this from my onCreate method:
Intent serviceIntent = new Intent(this, PubNubService.class);
startService(serviceIntent);
The one thing that Android Studio yells at me for is that the Handler class should be static or leaks would occur. When I run my app, the error that occurs is: [Error: 128-0] : Unable to get Response Code. Please contact support with error details. Unable to resolve host "pubsub-1.pubnub.com": No address associated with hostname. And on the next line [Error: 100-1] : Timeout Occurred.
My Android Manifest has this added:
<service android:name=".PubNubService"/>
PubNub Minimal Android Sample Code to Publish & Subscribe
The simplest sort of example would be to just add all the code in a single Activity. All of the following code and be seen in PubNub Android SDK docs page.
import com.pubnub.api.*;
import org.json.*;
Pubnub pubnub = new Pubnub("your_pub_key", "your_sub_key");
pubnub.subscribe("channel1", new Callback() {
#Override
public void connectCallback(String channel, Object message) {
System.out.println("SUBSCRIBE : CONNECT on channel:" + channel
+ " : " + message.getClass() + " : "
+ message.toString());
}
#Override
public void disconnectCallback(String channel, Object message) {
System.out.println("SUBSCRIBE : DISCONNECT on channel:" + channel
+ " : " + message.getClass() + " : "
+ message.toString());
}
public void reconnectCallback(String channel, Object message) {
System.out.println("SUBSCRIBE : RECONNECT on channel:" + channel
+ " : " + message.getClass() + " : "
+ message.toString());
}
#Override
public void successCallback(String channel, Object message) {
System.out.println("SUBSCRIBE : " + channel + " : "
+ message.getClass() + " : " + message.toString());
// this is the messages received from publish
// add these messages to a list UI component
}
#Override
public void errorCallback(String channel, PubnubError error) {
System.out.println("SUBSCRIBE : ERROR on channel " + channel
+ " : " + error.toString());
}
}
);
Callback callback = new Callback() {
public void successCallback(String channel, Object response) {
System.out.println(response.toString());
}
public void errorCallback(String channel, PubnubError error) {
System.out.println(error.toString());
}
};
pubnub.publish("my_channel", "Hello from the PubNub Java SDK!" , callback);
You might have to make a few changes. For one, you should create a click method with the publish inside it that is bound to a button on your interface. And as noted in the successCallback for the subscribe method, you need to display the messages in a UI component on your Activity.
That should do it for you.
Subscribe at Boot
But there isn't really anything simpler than our Subscribe at Boot sample app that uses a Service to forward messages as Intents to an Activity.
Prevent Service Start on Device Boot
The fact that the Subscribe at Boot example starts up when the device is booted (powered on) is a matter of configuration. You can change the manifest so that it is only started when the app is started. See the SO thread Trying to start a service on boot on Android and undo the parts that make it start at boot.
This is full of helpful information on Android Services
More details on this in this SO thread "Is leaving a pubnub subscription open in a service optimal"
I'm working on an Android Wi-Fi P2p project (also called WiFi direct).
This is how I try to create the group:
public class WiFiDirectBroadcastReceiver extends BroadcastReceiver {
private WifiP2pManager mManager;
private Channel mChannel;
private MainActivity mActivity;
public WiFiDirectBroadcastReceiver(WifiP2pManager p2pManager,
Channel channel, MainActivity mainActivity) {
super();
this.mManager = p2pManager;
this.mChannel = channel;
this.mActivity = mainActivity;
}
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
String state = MainActivity.wifiP2PStateMap.get(intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1));
if (state.equalsIgnoreCase("ENABLED")) {
Log.d(MainActivity.TAG, "P2P state changed to ENABLED ");
mManager.createGroup(mChannel, new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
Log.d(MainActivity.TAG, "Group created ");
}
#Override
public void onFailure(int reason) {
Log.d(MainActivity.TAG, "Failed to create Group " + MainActivity.failureMap.get(reason));
}
});
}
}else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)){
//STUB
}else if (WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION.equals(action)){
//STUB
}else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
//STUB
}else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
//STUB
}
}
}
When I scan the available wifi groups using wifi-analyzer I see that my group was created on channel 1, and it is always on channel 1.
Since this channel might be over crowded with other Access points I want to use a different channel.
The problem is that Android's API does not have a visible method to change the channel.
Here I found these questions: Q1, Q2, but they suggest rooting the device which is not an option for me.
When I looked into Android's sources I found that there is a hidden method:
/** #hide */
public void setWifiP2pChannels(Channel c, int lc, int oc, ActionListener listener) {
checkChannel(c);
Bundle p2pChannels = new Bundle();
p2pChannels.putInt("lc", lc);
p2pChannels.putInt("oc", oc);
c.mAsyncChannel.sendMessage(SET_CHANNEL, 0, c.putListener(listener), p2pChannels);
}
Using reflection I tried the following:
Method setWifiP2pChannels = p2pManager.getClass().getMethod("setWifiP2pChannels", WifiP2pManager.Channel.class, int.class, int.class, WifiP2pManager.ActionListener.class);
setWifiP2pChannels.invoke(p2pManager, channel, 0, channel_to_set, new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
Log.d(MainActivity.TAG, "Changed channel (" + channel_to_set + ") succeeded");
}
#Override
public void onFailure(int reason) {
Log.d(MainActivity.TAG, "Changed channel (" + channel_to_set + ") failed");
}
});
When channel_to_set was 0 or 1 the actual channel did not change. When channel_to_set to something higher, creating a group returned an error.
Any idea how to solve this?
Thanks
I'm trying to detect ibeacon using an android smartphone. I bought ibeacon devices from a company that has provided me android library to make them work (this library is much like the android beacon library for AltBeacon, such as the code that I used). Here is the MainActivity
public class MainActivity extends Activity implements IBeaconConsumer {
private static final String TAG = "BB-EXAPP";
// iBeacon bluetooth scanning parameters
private static final int FOREGROUND_SCAN_PERIOD = 1000;
private static final int FOREGROUND_BETWEEN_SCAN_PERIOD = 1000;
private static final int BACKGROUND_SCAN_PERIOD = 250;
private static final int BACKGROUND_BETWEEN_SCAN_PERIOD = 2000;
// iBeacon Library Stuff
private static final Region blueupRegion = new Region("BlueUp", "acfd065e-c3c0-11e3-9bbe-1a514932ac01", null, null);
private IBeaconManager iBeaconManager = IBeaconManager.getInstanceForApplication(this);
private Intent iBeaconService;
private boolean isMonitoring = false;
private boolean isRanging = false;
// Android BLE Stuff
private BluetoothAdapter mBluetoothAdapter;
private static final int REQUEST_ENABLE_BT = 1;
// UI Stuff
private List<IBeacon> beacons;
private ListView listView;
private IBeaconListAdapter listAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initializes Bluetooth adapter
final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
// Ensures Bluetooth is available on the device and it is enabled. If not,
// displays a dialog requesting user permission to enable Bluetooth.
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
// Initializes iBeacon Service
iBeaconService = new Intent(getApplicationContext(), IBeaconService.class);
// Start the iBeacon Service
Log.d(TAG, "Starting service: iBeaconService");
startService(iBeaconService);
// Set desired scan periods
iBeaconManager.setForegroundScanPeriod(FOREGROUND_SCAN_PERIOD);
iBeaconManager.setForegroundBetweenScanPeriod(FOREGROUND_BETWEEN_SCAN_PERIOD);
iBeaconManager.setBackgroundScanPeriod(BACKGROUND_SCAN_PERIOD);
iBeaconManager.setBackgroundBetweenScanPeriod(BACKGROUND_BETWEEN_SCAN_PERIOD);
// Bind the iBeacon Service
iBeaconManager.bind(this);
//
// UI Initialization
//
// Create Empty IBeacons List
beacons = new ArrayList<IBeacon>();
// Create List Adapter
listAdapter = new IBeaconListAdapter(this, beacons);
// Get ListView
listView = (ListView)findViewById(R.id.listView);
// Set ListAdapter
listView.setAdapter(listAdapter);
}
#Override
public void onIBeaconServiceConnect() {
Log.d(TAG, "onIBeaconServiceConnect");
// Set Monitor Notifier
iBeaconManager.setMonitorNotifier(new MonitorNotifier() {
#Override
public void didExitRegion(Region region) {
Log.d(TAG, "didExitRegion: region = " + region.toString());
}
#Override
public void didEnterRegion(Region region) {
Log.d(TAG, "didEnterRegion: region = " + region.toString());
// Set Range Notifier
iBeaconManager.setRangeNotifier(new RangeNotifier() {
#Override
public void didRangeBeaconsInRegion(Collection<IBeacon> iBeacons, Region region) {
// Update UI iBeacons List
beacons = new ArrayList<IBeacon>(iBeacons);
listAdapter.notifyDataSetChanged();
// Log found iBeacons
Log.d(TAG, "didRangeBeaconsInRegion: region = " + region.toString());
if (!iBeacons.isEmpty()) {
int j = 0;
for (IBeacon beacon : iBeacons) {
Log.d(TAG, " [" + j + "] (Major = " + beacon.getMajor() + ", Minor = " + beacon.getMinor() + ", RSSI = " + beacon.getRssi() + ", Accuracy = " + beacon.getAccuracy() + ")");
j++;
}
}
}
});
// Start Ranging
try {
iBeaconManager.startRangingBeaconsInRegion(blueupRegion);
isRanging = true;
Log.d(TAG, "startRangingBeaconsInRegion: region = " + blueupRegion.toString());
} catch (RemoteException e) {
Log.d(TAG, "startRangingBeaconsInRegion [RemoteException]");
e.printStackTrace();
}
}
#Override
public void didDetermineStateForRegion(int state, Region region) {
Log.d(TAG, "didDetermineStateForRegion: state = " + state + ", region = " + region.toString());
}
});
// Start Monitoring
try {
iBeaconManager.startMonitoringBeaconsInRegion(blueupRegion);
isMonitoring = true;
Log.d(TAG, "startMonitoringBeaconsInRegion: region = " + blueupRegion.toString());
} catch (RemoteException e) {
Log.d(TAG, "startMonitoringBeaconsInRegion [RemoteException]");
e.printStackTrace();
}
}
#Override
public void onResume() {
Log.d(TAG, "onResume");
super.onResume();
if (iBeaconManager.isBound(this)) {
iBeaconManager.setBackgroundMode(this, false);
Log.d(TAG, "iBeaconManager.setBackgroundMode = false");
}
}
#Override
public void onStop() {
Log.d(TAG, "onStop");
if (iBeaconManager.isBound(this)) {
iBeaconManager.setBackgroundMode(this, true);
Log.d(TAG, "iBeaconManager.setBackgroundMode = true");
}
super.onStop();
}
#Override
public void onDestroy() {
Log.d(TAG, "onDestroy");
if (isRanging) {
try {
iBeaconManager.stopRangingBeaconsInRegion(blueupRegion);
Log.d(TAG, "stopRangingBeaconsInRegion: region = " + blueupRegion.toString());
} catch (RemoteException e) {
Log.d(TAG, "stopRangingBeaconsInRegion [RemoteException]");
e.printStackTrace();
}
}
if (isMonitoring) {
try {
iBeaconManager.stopMonitoringBeaconsInRegion(blueupRegion);
Log.d(TAG, "stopMonitoringBeaconsInRegion: region = " + blueupRegion.toString());
} catch (RemoteException e) {
Log.d(TAG, "stopMonitoringBeaconsInRegion [RemoteException]");
e.printStackTrace();
}
}
if (iBeaconManager.isBound(this)) {
Log.d(TAG, "Unbinding iBeaconManager");
iBeaconManager.unBind(this);
}
Log.d(TAG, "Stopping service: iBeaconService");
stopService(iBeaconService);
super.onDestroy();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
There's a second activity to make the list:
public class IBeaconListAdapter extends BaseAdapter {
private Activity activity;
private List<IBeacon> beacons;
private static LayoutInflater inflater = null;
public IBeaconListAdapter(Activity _activity, List<IBeacon> _beacons) {
this.activity = _activity;
this.beacons = _beacons;
inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return beacons.size();
}
#Override
public Object getItem(int position) {
return beacons.get(position);
}
#Override
public long getItemId(int position) {
/*IBeacon beacon = beacons.get(position);
if (beacon != null) {
return beacon.hashCode();
}*/
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if (convertView == null) {
view = inflater.inflate(R.layout.beacon_list_row, null);
}
TextView majorTextView = (TextView)view.findViewById(R.id.majorValue);
TextView minorTextView = (TextView)view.findViewById(R.id.minorValue);
TextView rssiTextView = (TextView)view.findViewById(R.id.rssiValue);
TextView accuracyTextView = (TextView)view.findViewById(R.id.accuracyValue);
IBeacon beacon = beacons.get(position);
if (beacon != null) {
DecimalFormat df = new DecimalFormat("#.0");
majorTextView.setText(beacon.getMajor());
minorTextView.setText(beacon.getMinor());
rssiTextView.setText(beacon.getRssi() + " dBm");
accuracyTextView.setText(df.format(beacon.getAccuracy()) + " m");
}
return view;
}
}
When I run the app on the phone, from the LogCat I can see that
it detects the devices, while on the phone I can't see anything but a blank page! I noticed from the LogCat that once the onIBeaconServiceConnect is called, than the app jumps to the startMonitoringBeaconsInRegion and never calls the didEnterRegion method, which includes the didRangeBeaconsInRegion and the code to fill the list.
I couldn't find other answers to a question similar to mine and I really don't know where is my mistake.
A few thoughts:
Make sure your beacons are really transmitting and have the identifiers you think they do. Try downloading an off-the-shelf app like Locate and make sure it sees them.
Your code sets up a region that is looking for a beacon with a UUID of acfd065e-c3c0-11e3-9bbe-1a514932ac01. Are you sure this is exactly right? Try replacing the region definition with Region("BlueUp", null, null, null); so it detects any region regardless of identifier.
If you plan to do significant development, I would strongly recommend you upgrade to the Android Beacon Library 2.0. The old 0.x library version that library is based off of is no longer supported or maintained, and it will be more difficult to find help when problems like this come up.
These are the significant lines in the LogCat.
D/BB-EXAPP(7756): onIBeaconServiceConnect
D/BB-EXAPP(7756): startMonitoringBeaconsInRegion: region = proximityUuid: acfd065e-c3c0-11e3-9bbe-1a514932ac01 major: null minor:null
D/AbsListView(7756): unregisterIRListener() is called
I/IBeaconService(7756): start monitoring received
D/BluetoothAdapter(7756): startLeScan(): null
D/BluetoothAdapter(7756): onClientRegistered() - status=0 clientIf=4
I/IBeaconService(7756): Adjusted scanStopTime to be Sat Jan 17 12:09:58 CET 2015
D/AbsListView(7756): unregisterIRListener() is called
D/BluetoothAdapter(7756): onScanResult() - Device=DA:0D:22:4B:40:17 RSSI=-59
D/Callback(7756): attempting callback via intent: ComponentInfo{com.android.appbeacon/com.blueup.libbeacon.IBeaconIntentProcessor}
D/BluetoothAdapter(7756): onScanResult() - Device=DA:0D:22:4B:40:17 RSSI=-64
I/IBeaconService(7756): iBeacon detected multiple times in scan cycle :acfd065e-c3c0-11e3-9bbe-1a514932ac01 0 0 accuracy: 0.7351236323265571 proximity: 2
D/BluetoothAdapter(7756): stopLeScan()
I am building an Android app that communicates with an Arduino board via bluetooth, I have the bluetooth code in a class of it's own called BlueComms. To connect to the device I use the following methord:
public boolean connectDevice() {
CheckBt();
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
Log.d(TAG, "Connecting to ... " + device);
mBluetoothAdapter.cancelDiscovery();
try {
btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
btSocket.connect();
outStream = btSocket.getOutputStream();
Log.d(TAG, "Connection made.");
return true;
} catch (IOException e) {
try {
btSocket.close();
} catch (IOException e2) {
Log.d(TAG, "Unable to end the connection");
return false;
}
Log.d(TAG, "Socket creation failed");
}
return false;
}
private void CheckBt() {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (!mBluetoothAdapter.isEnabled()) {
System.out.println("Bt dsbld");
}
if (mBluetoothAdapter == null) {
System.out.println("Bt null");
}
}
This connects fine but as soon as I leave the activity I connected through it drops the connection, showing this through LogCat,
D/dalvikvm(21623): GC_CONCURRENT freed 103K, 10% free 2776K/3056K, paused 5ms+2ms, total 35ms
I can no longer connect to the device, but if I call killBt() it throws a fatal error and if I try to send data I get a 'Socket creation failed' error. My send message code is as follows:
public void sendData(String data, int recvAct) {
try {
outStream = btSocket.getOutputStream();
} catch (IOException e) {
Log.d(TAG, "Bug BEFORE Sending stuff", e);
}
String message = data;
byte[] msgBuffer = message.getBytes();
try {
outStream.write(msgBuffer);
} catch (IOException e) {
Log.d(TAG, "Bug while sending stuff", e);
}
}
How should I go about preventing the connection from being paused by the activity I connect with when I switch a different activity, I am switching activities with this code:
Intent myIntent = new Intent(v.getContext(), Timelapse.class);
startActivityForResult(myIntent, 0);
Many Thanks,
Rozz
Where did you store the instance of your BlueComms class? If you put it in the first activity then the class instance would have been killed when that activity was destroyed as you left it and moved to the next activity (NB activities also get destroyed on screen rotation)
So you need to find a way to keep the instance of BlueComms class alive for as long as you need it. You could pass it between activities via public properties and store it in onRetainNonConfigurationInstance() during rotations.
An easier trick is to create a class that extends Application use it as the application delegate for your app and add public property to it to store the instance of BlueComms class within it. That way the instance of BlueComms class would be alive for the lifetime of you app.
Extend Application
import android.app.Application;
public class cBaseApplication extends Application {
public BlueComms myBlueComms;
#Override
public void onCreate()
{
super.onCreate();
myBlueComms = new BlueComms();
}
}
Make your class the application delegate in the app manifest
<application
android:name="your.app.namespace.cBaseApplication"
android:icon="#drawable/icon"
android:label="#string/app_name" >
Access the base app from any of your Activities like this
((cBaseApplication)this.getApplicationContext()).myBlueComms.SomeMethod();
What I have done is, Created a singleton class for BluetoothConnection.
So socket creation happens only for one time.
When onCreate method of any activity is created, it first fetch instance of BluetoothConnection class.
Handler is used to send messages from thread in BluetoothConnection class to the corresponding activity by settings Handler.
Like:
Class MyBTConnection{
private static MyBTConnection connectionObj;
private Handler mHandler;
public MyBTConnection() { //constructor }
public static MyBTConnection getInstance() {
if(connectionObj == null) {
connectionObj = new MyBTConnection();
}
return connectionObj;
}
}
public void setHandler(Handler handler) {
mHandler = handler;
}
..... Code for Bluetooth Connection ....
to send message :
mHandler.obtainMessage(what).sendToTarget();
}
// in first activity
class MainActivity extends Activity {
private MyBTConnection connectionObj;
public onCreate(....) {
/*
* Since this is first call for getInstance. A new object
* of MyBTConnection will be created and a connection to
* remote bluetooth device will be established.
*/
connectionObj = MyBTConnection.getInstance();
connectionObj.setHandler(mHandler);
}
private Handler mHandler = new Handler(){
public void onReceive(...) {
/// handle received messages here
}
};
}
// in second activity
class SecondActivity extends Activity {
private MyBTConnection connectionObj;
public onCreate(....) {
/*
* Since this is second call for getInstance.
* Object for MyBTConnection was already created in previous
* activity. So getInstance will return that previously
* created object and in that object, connection to remote
* bluetooth device is already established so you can
* continue your work here.
*/
connectionObj = MyBTConnection.getInstance();
connectionObj.setHandler(mHandler);
}
private Handler mHandler = new Handler(){
public void onReceive(...) {
/// handle received messages here
}
};
}
I'm currently having exactly the same issue and I was thinking of opening/closing the Bluetooth socket each time an Activity asks for it. Each Activity has it's own BlueComms instance.
Because my application will became a bit complex and there will be Bluetooth threaded requests from different activities, I'm thinking that this way will become very difficult to use and troubleshoot.
Another way I came across by reading here...
https://developer.android.com/guide/components/services.html
A Service can be created on the background having a Bluetooth socket always on. All Bluetooth requests can be made using Intent towards this service. This also creates some fair amount of complexity but feels a lot more tidy and organized.
I'm currently having this dilemma, either to use a thread for each activity or use a service. I don't know which way is actually better.
When you are Selecting A device to connect and when you are click on the device list item for requesting a connection to the device use AsyncTask
and put the connect method inside the AsyncTask like this :-
AsyncTask.execute(new Runnable() {
#Override
public void run() {
try {
bluetoothSocket = Globals.bluetoothDevice.createRfcommSocketToServiceRecord(Globals.DEFAULT_SPP_UUID);
bluetoothSocket.connect();
// After successful connect you can open InputStream
} catch (IOException e) {
e.printStackTrace();
}
**Here is the full code for the same problem that i have cracked :-**
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
lablelexconnected.setText("Connecting ...");
bdDevice = arrayListBluetoothDevices.get(position);
//bdClass = arrayListBluetoothDevices.get(position)
// Toast.makeText(getApplicationContext()," " + bdDevice.getAddress(),Toast.LENGTH_SHORT).show();
Log.i("Log", "The dvice : " + bdDevice.toString());
bdDevice = bluetoothAdapter.getRemoteDevice(bdDevice.getAddress());
Globals.bluetoothDevice = bluetoothAdapter.getRemoteDevice(bdDevice.getAddress());
System.out.println("Device in GPS Settings : " + bdDevice);
// startService(new Intent(getApplicationContext(),MyService.class));
/* Intent i = new Intent(GpsSettings.this, MyService.class);
startService(i);*/
// finish();
// connectDevice();
AsyncTask.execute(new Runnable() {
#Override
public void run() {
try {
bluetoothSocket = Globals.bluetoothDevice.createRfcommSocketToServiceRecord(Globals.DEFAULT_SPP_UUID);
bluetoothSocket.connect();
// After successful connect you can open InputStream
InputStream in = null;
in = bluetoothSocket.getInputStream();
InputStreamReader isr = new InputStreamReader(in);
br = new BufferedReader(isr);
while (found == 0) {
String nmeaMessage = br.readLine();
Log.d("NMEA", nmeaMessage);
// parse NMEA messages
sentence = nmeaMessage;
System.out.println("Sentence : " + sentence);
if (sentence.startsWith("$GPRMC")) {
String[] strValues = sentence.split(",");
System.out.println("StrValues : " + strValues[3] + " " + strValues[5] + " " + strValues[8]);
if (strValues[3].equals("") && strValues[5].equals("") && strValues[8].equals("")) {
Toast.makeText(getApplicationContext(), "Location Not Found !!! ", Toast.LENGTH_SHORT).show();
} else {
latitude = Double.parseDouble(strValues[3]);
if (strValues[4].charAt(0) == 'S') {
latitude = -latitude;
}
longitude = Double.parseDouble(strValues[5]);
if (strValues[6].charAt(0) == 'W') {
longitude = -longitude;
}
course = Double.parseDouble(strValues[8]);
// Toast.makeText(getApplicationContext(), "latitude=" + latitude + " ; longitude=" + longitude + " ; course = " + course, Toast.LENGTH_SHORT).show();
System.out.println("latitude=" + latitude + " ; longitude=" + longitude + " ; course = " + course);
// found = 1;
NMEAToDecimalConverter(latitude, longitude);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
});