I'm using the example code on this page (http://altbeacon.github.io/android-beacon-library/samples.html) in the Starting an App in the Background section and I've got a working app.
It detects whenever a beacon is in range even on background.
The problem is I need to know which beacon is it (UUID, Major, Minor) to then match it against my local database and throw a notification with the app still on background.
The didEnterRegion(Region region) function only has a matchesBeacon method, and I've tried doing the following to identify which of the beacons is being seen but it's throwing a NullPointerException:
public class SightSeeing extends Activity implements BootstrapNotifier, RangeNotifier {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Region region = new Region("sightRegion", null, null, null);
regionBootstrap = new RegionBootstrap(this, region);
BeaconManager.getInstanceForApplication(this).getBeaconParsers().add(
new BeaconParser(). setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24")
);
BeaconManager.getInstanceForApplication(this).setRangeNotifier(this);
}
#Override
public void didEnterRegion(Region region) {
regionBootstrap.disable();
BeaconManager.getInstanceForApplication(this).setRangeNotifier(this);
try {
BeaconManager.getInstanceForApplication(this).startRangingBeaconsInRegion(region);
}
catch (RemoteException e) {
Log.e(TAG, "Can't start ranging");
}
}
#Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
if (beacons.size() > 0) {
Iterator<Beacon> beaconIterator = beacons.iterator();
while (beaconIterator.hasNext()) {
Beacon beacon = beaconIterator.next();
//check if beacon exists in our DB and throw notification
}
}
}
Am I missing something obvious or isn't this possible with this library?
EDIT:
I've updated the code sample to give you guys a broader idea and I've tried implementing the suggestion by FOliveira without any success.
EDIT2:
Updated code to reflect the davidgyoung's suggestion. Still no luck. I have a Log.d() right on the first line of the didRangeBeaconsInRegion() function and it isn't being called.
I've tried adding BeaconManager.getInstanceForApplication(this).setRangeNotifier(this); before the try/catch block and the result is the same.
Did I implement the suggestion wrong or is there any other way to get this working?
If you want the app to launch itself on beacon detection, then the RegionBootstrap is the easiest way to go. In order to combine this with Ranging needed to detect individual beacons, then add code in your didEnterRegion method like this:
try {
BeaconManager.getInstanceForApplication(this).startRangingBeaconsInRegion(region);
}
catch (RemoteException e) {
Log.e(TAG, "Can't start ranging");
}
Then implement a ranging callback like you have.
You also need to remove the code below, which is probably what is causing your NullPointerException, because the :
for(int i=0; i< beaconsList.size(); i++) {
Beacon b = new Beacon.Builder()
.setId1(beaconsList.get(i).get("uuid"))
.setId2(beaconsList.get(i).get("major"))
.setId3(beaconsList.get(i).get("minor"))
.build();
if(region.matchesBeacon(b)) {
//get info from DB and throw notification
}
}
EDIT: I have updated the library's reference application to show how this can be done successfully. See here: https://github.com/AltBeacon/android-beacon-library-reference/blob/master/src/org/altbeacon/beaconreference/BeaconReferenceApplication.java
you can implement RangeNotifier interface and you can access all the beacon information captured in the public void didRangeBeaconsInRegion(Collection<Beacon> Beacons, Region region) method of that interface. Hope i got the question right
Related
I want to implement Google Play Games Services in my game on the libgdx engine. I tried using gdx-gamesvcs for this. But I am having trouble saving data. I understood from the example that one value is being saved, not the entire state of the game. So I decided to check it out: save and load one value using gsClient.loadGameState and gsClient.saveGameState. I deliberately deleted the game data from the device. But as a result, not only the test value changed, but many others as well. I thought that the state of the entire game is being saved, but the values obtained do not fit into the logic of the game and could not be obtained in it.
How should I use this tool and is it worth it at all, or is it better to use what libgdx itself offers?
Here is a piece of code:
if (gsClient.isSessionActive()) {
try {
gsClient.saveGameState("data", intToByteArray(testValue), 0, null);
} catch (UnsupportedOperationException unsupportedOperationException) {
}
if (gsClient.isSessionActive()) {
try {
gsClient.loadGameState("data", new ILoadGameStateResponseListener() {
#Override
public void gsGameStateLoaded(byte[] gameState) {
if (gameState != null) {
setTestValue(bytesToInt(gameState));
}
}
});
} catch (UnsupportedOperationException unsupportedOperationException) {
}
}
UPD
Yes, saving occurs both to the cloud and to the device, for saving to the device I use Preferences. I have a Google account login button in the game, it works, I have repeatedly seen this standard bar of my account level, which appears at the top when I log in. Everything is set up in the developer console too, I have an id for achievements and leaderboards. In code, I work with the client like this (In the create() method):
public IGameServiceClient gsClient;
if (gsClient == null) {
gsClient = new MockGameServiceClient(1) {
#Override
protected Array<ILeaderBoardEntry> getLeaderboardEntries() {
return null;
}
#Override
protected Array<String> getGameStates() {
return null;
}
#Override
protected byte[] getGameState() {
return new byte[0];
}
#Override
protected Array<IAchievement> getAchievements() {
return null;
}
#Override
protected String getPlayerName() {
return null;
}
};
}
gsClient.setListener(this);
gsClient.resumeSession();
Next is loading.
The exception is not caught, I removed it and everything works as before.
Well, libgdx offers no built-in cloud-save, it is hard to use it for that. :-)
You should in any case save to local AND to cloud, as the cloud is not very fast to load its state.
I can see no problem in your code besides the fact that you swallow an UnsupportedOperationException that is thrown if you did not activate cloud save feature. So the interesting question is: what happens if you don't swallow the exception, and did you intialize GpgsClient with cloud save enabled? Are you really logged in to Gpgs, and is the feature also activated in your developer console?
The main problem was that gameState was null, this arose due to the fact that you had to wait 24 hours after enabling the save function in the developer console, and the advice on clearing the memory of google play games on the test device did not help. After a while gameState began to pass the existing values, but I started having problems with the graphics flow, probably due to the asynchronous loading.
I am using the new Google Places SDK: AutocompleteSupportFragment.
whenever i select an address from the result list it doesn't save it and show it on the field.
I am not sure why.
this is my code below:
i declaed the variable at the top of the class:
Place shipAddress;
and called this method where i needed:
final AutocompleteSupportFragment editAddress = (AutocompleteSupportFragment) getSupportFragmentManager().findFragmentById(R.id.autocomplete_fragment);
editAddress.setPlaceFields(Arrays.asList(Place.Field.ID, Place.Field.NAME));
editAddress.setOnPlaceSelectedListener(new PlaceSelectionListener() {
#Override
public void onPlaceSelected(Place place) {
shipAddress = place;
}
#Override
public void onError(Status status) {
Log.e("ERROR", status.getStatusMessage());
}
});
i am also using the result in another method calling:
address = shipAddress.getAddress().toString();
but finally i get an error that
variable address is invoked on a null object.
Places SDK has been Depricated You need to migrate to new PLACES
API.. You can find solution by below guideline.
https://stackoverflow.com/a/55045772/10579969
Official page https://developers.google.com/places/android-sdk/client-migration#place_picker
After calling a batch.commit or docRef.update (part of it calls FieldValue.serverTimeStamp to update time of submission), I call finish(); to go back to previous activity that loads a recycleView of the list of documents that was updated.
I get this error:
java.lang.NullPointerException: Attempt to invoke virtual method 'java.util.Date com.google.firebase.Timestamp.toDate()' on a null object reference`
I suspect it's that FieldValue.servertimeStamp takes more time to compute and the app crashes. However, the same field where recyclerView is pulling the datetime from already have an old value.
I'm not sure why the old value is not retrieved, but crashes on null instead.
Q1) Does FieldValue.servertimeStamp make the field null until new datetime is computed?
My guess is, this particular call is waiting for an answer from Firebase server, thus taking more time but other calls are done locally first on the device before updating in the cloud. Some of your insights is appreciated.
In the mean time, as a work-around to stop this asynchronous error, I have used a Thread loop with Thread.sleep while waiting for onCompleteSuccess to respond:
FirestoreFieldUpdated = false;
Thread myThread = new Thread(
new Runnable() {
#Override
public void run() {
try {
while (!FirestoreFieldUpdated) { //db.updateFields will change FirestorefieldUpdated to true uponSuccess
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
finish();
}
}
}
);
myThread.start();
Q2) Is there a more elegant way or better way to do this? Or to enable synchronicity only for this particular transaction of updating datetime?
EDIT (added on more details on what I'm trying to do):
I am trying to call this method from AddNewOrder.java:
public void updateFields(String actionDateField) {
Map<String, Object> updates = new HashMap<>();
updates.put(actionDateField, FieldValue.serverTimestamp());
updateSubmit.update(updates);
}
from a class outside (AddNewOrder.java):
db = new DatabaseHelper(getApplicationContext());
db.updateFields("OrderDate");
finish();
Finish(); will then pass me back to the previous activity that calls RecyclerView:
Query query = mFirestore
.collection("Org")
.document(RootCollection)
.collection("Stores")
.document(StoreID)
.collection("Orders")
.whereGreaterThan("OrderVerified", "")
.limit(queryLimit);
mAdapter = new OrdersAdapter(query, FulfilmentActivity.this) {
#Override
protected void onError(FirebaseFirestoreException e) {
// Show a snackbar on errors
Snackbar.make(findViewById(android.R.id.content),
"Error: check logs for info.", Snackbar.LENGTH_LONG).show();
}
};
mAdapter.setQuery(query);
mOrdersRecycler.setAdapter(mAdapter);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
mOrdersRecycler.setLayoutManager(layoutManager);
In OrdersAdapter.java, I have this:
Orders orders = snapshot.toObject(Orders.class);
submitDate.setText(FORMAT.format(orders.getOrderDate()));
in public void bind.
The above is the line that NullPointerException appeared on.
Orders.java:
public class Orders {
private Timestamp OrderDate;
public Orders(Timestamp orderDate) { this.OrderDate = orderDate; }
public java.util.Date getOrderDate() { return OrderDate.toDate(); }
}
How do I fix this properly?
First of all, if you're working with threading in order to deal with Firestore, you're almost certainly doing the wrong thing. All Firestore APIs (actually, all Firebase APIs) are asynchronous, and require no threading on the part of your app.
Q1 - there is no intermediate null value in a document that's going to be created with a timestamp. The server interprets the server timestamp token immediately and writes a Timestamp object to the document atomically.
Q2 - I can't really tell what you're trying to accomplish with this code. It's way out of bounds of what you would normally do to write to Firestore. If you want to know if a document has changed in Firestore, you attach a listener to a reference to that document, and the listener is invoked when the document is seen to change. Again, there's no need for threading in this case, because the callbacks are all asynchronous. As long as the listener is added, it will be called.
serverTimeStamp() indeed becomes Null in the cache before getting a confirm response from the server: https://github.com/firebase/firebase-js-sdk/issues/192
Refer to this for solution: https://firebase.google.com/docs/reference/android/com/google/firebase/firestore/Document
I fixed this by adding this:
DocumentSnapshot.ServerTimestampBehavior behavior = ESTIMATE;
Date date = snapshot.getDate("OrderDate", behavior);
I have this piece of code that runs alright when I put it in Eclipse, but for some reason it does not want to execute when I put it in an activity's onCreate method in Android studio.
Here is the code:
public class ItemListView extends AppCompatActivity{
String itemURL;
int listSize;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_item_list_view);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
Bundle itemData = getIntent().getExtras();
if(itemData==null){
return;
}
//Gets URL from search bar
itemURL = itemData.getString("itemURL");
try {
Document doc = Jsoup.connect("https://www.amazon.com/s/ref=nb_sb_ss_c_1_6?url=search-alias%3Daps&field-keywords=rx+390&sprefix=rx+390%2Caps%2C166&crid=2MTUBA4KGNY06").get();
String link = doc.select("h2#s-result-count").first().text();
System.out.println(link);
System.out.println(link.substring(1));
if (link.substring(1, 2).equals("-")) {
System.out.println("run1");
listSize = Integer.parseInt(link.substring(2, 3));
System.out.println(listSize);
try {
listSize = Integer.parseInt(link.substring(2, 4));
System.out.println(listSize);
} catch (Exception e) {
}
} else {
System.out.println("run2");
listSize = Integer.parseInt(link.substring(0, 1));
System.out.println(listSize);
try {
listSize = Integer.parseInt(link.substring(0, 2));
System.out.println(listSize);
} catch (Exception e) {
}
}
} catch (Exception e) {
}
System.out.println("listSize: " +listSize);
...
}
}
I need listSize to create a variable array depending on the URL, but when I print the value to make sure it's working it always gives me 0. I have tried running the code in a separate Java Class with AsyncTask and it works, but by the time the code executes in onPostExecute, it's too late since the class above has already tried to initialize the array.
Any advice would be appreciated, thanks!
What you need is a callback to allow you to initialize variable onPostExecute:
interface OnCallCompleteCallBack {
void onCallComplete(int listSize);
}
In your AsyncTask do this:
public class MyTask extends AsyncTask < ... > {
// Maintain a ref to callback
OnCallCompleteCallBack callback;
MyTask(OnCallCompleteCallBack callback) {
this.callback = callback;
}
...
#Override
protected void onPostExecute(Void aVoid) {
if (callback != null) {
callback.onCallComplete(listSize);
}
}
}
Make your Activity implement OnCallCompleteCallBack and start the AsyncTask like this:
new MyTask(this).execute();
You can then use the value inside your activity's implementation of onCallComplete()
Before I answer, just a few observations:
Naming your activity ItemListView seems wrong (an Activity is not a View).
You should never perform networks calls on the UI thread.
If you want to log, you should use Log instead of System.out.println().
And now to the answer. You should execute the code that fetches the data in an AsyncTask (as you mentions it works). Once the data is fetched, you should update array and at that point update the UI in onPostExecute().
Android works pretty well using the MVC (Model-View–Controller) pattern and your problem is a classic case where you need to update the model using data from the server and update the views accordingly. The activity represents controller in this case.
Please go through the topic in android developer site Android Developer, Read the section "Introducing Network Operations on a Separate Thread" - To avoid creating an unresponsive UI, don't perform network operations on the UI thread. By default, Android 3.0 (API level 11) and higher requires you to perform network operations on a thread other than the main UI thread; if you don't, a NetworkOnMainThreadException is thrown.
Thanks Ashish
When I sign in on my application and try to get the size from the datastore's table, it is empty. But after I restart the application, it works as it should and gives back the size of the datastore's table.
In my case, there are two activities. The activity, and in-turn the fragment, SettingsFragment.java has the sign-in part. The activity MainActivity.java has the part with getting the datastore's table size.
SettingsFragment.java (Sign-in).
// Dropbox Signin
mAccountManager = DbxAccountManager.getInstance(getActivity().getApplicationContext(), APP_KEY, SECRET_KEY);
mDropBoxPreference = findPreference("preference_sync_dbx");
mDropBoxPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
if (mAccountManager.hasLinkedAccount()) {
mAccountManager.unlink();
} else {
mAccountManager.startLink(getActivity(), REQUEST_LINK_TO_DBX);
}
return true;
}
});
MainActivity.java (Get datastore size).
// Dropbox Get Datastore Size
mAccountManager = DbxAccountManager.getInstance(getApplicationContext(), APP_KEY, SECRET_KEY);
if (mAccountManager.hasLinkedAccount()) {
DbxAccount mDbxAccount = mAccountManager.getLinkedAccount();
try {
mDbxStore = DbxDatastore.openDefault(mDbxAccount);
mDbxNotesTable = mDbxStore.getTable("notes");
mDbxNotesCount = mDbxNotesTable.query().count();
Log.e("mDbxNotesCount", Integer.toString(mDbxNotesCount));
} catch (DbxException e) {
e.printStackTrace();
}
}
Let's say there are 3 records in the datastore.
After I sign in, this shows up from the Logcat.
E/mDbxNotesCount﹕ 0
Once I restart it, this shows up from the Logcat.
E/mDbxNotesCount﹕ 3
What's causing it to work like this?
Edit: Added listener after using smarx's answer, and it works! Here is what I did.
mDbxStore.addSyncStatusListener(new DbxDatastore.SyncStatusListener() {
#Override
public void onDatastoreStatusChange(DbxDatastore dbxDatastore) {
if (!dbxDatastore.getSyncStatus().isDownloading) {
try {
mDbxStore.sync();
mDbxNotesCount = mDbxNotesTable.query().count();
} catch (DbxException e) {
e.printStackTrace();
}
Log.e("mDbxNotesCount", Integer.toString(mDbxNotesCount));
}
}
});
It looks like you're trying to read the contents of the datastore before it's had a chance to sync. You'll need to wait until the initial sync is completed. I think one way to do that would be to add a listener via addSyncStatusListener and wait for store.getSyncStatus().isDownloading to be false.
But typically the pattern you'll want to follow is to always show the current information... so set up a listener and update your UI any time it changes. That way whether it's the initial sync bringing in new records or updates from a different device, your app will respond appropriately when the information changes.