android: calling Google API .getPlaceById with multiple place_id's - android

In order to reduce the number of API calls, I'm trying to query place details by passing several place_ids at a time (up to 10). I haven't found any useful information beyond the docs.
https://developers.google.com/android/reference/com/google/android/gms/location/places/GeoDataApi.html#getPlaceById(com.google.android.gms.common.api.GoogleApiClient, java.lang.String...)
Notably, the constructor is:
public abstract PendingResult<PlaceBuffer> getPlaceById (GoogleApiClient client, **String... placeIds**)
and the doc says: Returns Place objects for each of the given place IDs.
I don't have any problem when passing a single place_id, but when I pass a comma delimted string of id's, all of which are known to be good, I get a status = "SUCCESS" but a buffer count of 0.
Does anyone know the correct way to pass multiple id's to getPlaceById()?
Here's my code if that helps at all:
Places.GeoDataApi.getPlaceById(mGoogleApiClient, searchIds)
.setResultCallback(new ResultCallback<PlaceBuffer>() {
#Override
public void onResult(PlaceBuffer places) {
int cnt = places.getCount();
if (places.getStatus().isSuccess() && places.getCount() > 0) {
for (int i=0; i < places.getCount(); i++) {
final Place myPlace = places.get(i);
Log.d("<< cache >> ", "Place found: " + myPlace.getName());
}
} else {
Log.d("<< cache >> ", "Place not found");
}
places.release();
}
});

It's a varargs argument. You call it like this:
Places.GeoDataApi.getPlaceById(mGoogleApiClient,
"placeId1", "placeId2", "placeId3");
More detail in this SO question: How does the Java array argument declaration syntax "..." work?

Although I've read that String... can be passed as either a comma delimited string or a string array, for one reason or other, getPlaceById appears to require an array. When I use this code to prepare the place id parameter, it works fine:
String search[] = new String[idsToSearch.size()];
search = idsToSearch.toArray(search);

Related

How can I implement a custom autocomplete dictionary for non standard text input?

I'm re-writing part of a larger GTK/Python program to Android. In one of the edit texts, my users will be typing a botanic taxon name, things that include words like Pachypharynx or Erythroxylaceae.
Such words are part of a "Botanic lexicon", and people who speak different languages pronounce them differently, and make different typing mistakes when entering them. Some have quasi-homonyms, like the /Hieronima/Hyeronima/Hieronyma/ group, where the first two are classified as Euphorbiaceae, the latter as Phyllanthaceae. Some are valid names but a better choice is available (eg: Coccus is considered a synonym of Cocos)
So where's the programming problem? Well, I want to help the user!
I need a TextView,
in this TextView, I need to help the user type the name,
I can't dirty the user's dictionary with botanic names,
allow for possible typing mistakes, but prefer literal matching,
give context information (the family name),
help the user prefer accepted epithets.
I have considered and discarded the following:
define your own input method, (overkill)
alter the user dictionary, (unacceptable)
I would like to
use an AutoCompleteTextView (but how?)
and I consider this question worth being asked (I don't know why you guys/ladies downvote without explanation) and I think that the answer I found and built is going to help others.
This answer builds on others, but most of all on Carl Anderson's. It is based on AutoCompleteTextView option, and I am going to look one by one at each element of the solution.
First of all, when creating the view, let's grab the ACTV and associate it with a custom adapter:
public View onCreateView([...]) {
.
.
TaxonomyDatabase db = new TaxonomyDatabase(getContext());
List<Epithet> allEpithets = db.getAllGenera();
EpithetAdapter hints = new EpithetAdapter(getContext(),
android.R.layout.select_dialog_item, allEpithets);
AutoCompleteTextView widget = rootView.findViewById(R.id.etCollectSpecies);
widget.setThreshold(2); // start hinting from second character
widget.setAdapter(hints);
The TaxonomyDatabase.getAllGenera grabs all genera from the database, with their accepted equivalent, and the family they belong to.
List<Epithet> getAllGenera() {
ArrayList<Epithet> r = new ArrayList<>();
SQLiteDatabase db = getReadableDatabase();
Cursor cr = db.rawQuery(
"select o.epithet, a.epithet, o.phonetic, a.family_name " +
"from taxon o "+"" +
"left join taxon a on o.accepted_id = a.id " +
"where o.rank = 5 " +
"order by o.epithet",
new String[]{});
.
.
Now the EpithetAdapter constructor accepts this list, and first adds it to its internal candidates list, but then again copies each Epithet again in the list: the first instance is for literal match, the second instance is to allow for phonetic match. The logic of this is hidden in Epithet copy constructor, that sets the isExact field to false.
EpithetAdapter(Context context, int textViewResourceId, List<Epithet> epithets) {
super(context, textViewResourceId, epithets);
// copy all the epithets twice
mEpithets = new ArrayList<>(epithets.size() * 2);
// first to be used as exact matches (String×4 constructor)
mEpithets.addAll(epithets);
// then again to be used as phonetic matches (copy constructor)
for(Epithet e : epithets) {
mEpithets.add(new Epithet(e));
}
}
The Epithet class has a toString method, that takes care of how the Epithet shows in the ACTV drop down list:
public String toString() {
if (accepted != null) {
return epithet + " → " + accepted;
} else {
return epithet + " (" + family + ")";
}
}
the Filter in the EpithetAdapter does the rest of the task: when user clicks on a suggestion, ConvertResultToString is invoked:
public String convertResultToString(Object resultValue) {
return ((Epithet)resultValue).epithet;
}
Filtering itself, that is the performFiltering method, is just the logical consequence of all said up to now:
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
if (constraint != null) {
String exact = constraint.toString();
String phonetic = TaxonomyDatabase.phonetic(exact);
exact = exact.substring(0, 1).toUpperCase() + exact.substring(1).toLowerCase();
ArrayList<Epithet> suggestions = new ArrayList<>();
for (Epithet epithet : mEpithets) {
if (epithet.isExact) {
if (epithet.epithet.startsWith(exact)) {
suggestions.add(epithet);
}
} else {
if (epithet.phonetic.startsWith(phonetic) &&
!epithet.epithet.startsWith(exact)) {
suggestions.add(epithet);
}
}
}
results.values = suggestions;
results.count = suggestions.size();
}
return results;
}
last minor detail, disable text suggestions (android:inputType="textNoSuggestions" in the layout) in order not to dirty user dictionary.
when user types coccus, the software suggests:
when user types »galanth«, literal matches come first:
when user types »calanth«, again literal matches come first:
What you see in the drop down list is Epithet.toString. Clicking on a hint only copies Epithet.epithet.

Looping out JSON Array using ArrayList

I am trying to learn retrofit and I have made successful attempts at posting data and now I am trying to retrieve JSON array which looks as follows:
{
"result": "success",
"message": "All Questions Have Been Selected",
"question": {
"all_question_ids": ["1","2","3"]
}
}
I am using the following getter
public ArrayList getAll_question_ids(){
return all_question_ids;
}
I am retrieving using Retrofit as follows
if (resp.getResult().equals(Constants.SUCCESS)) {
SharedPreferences.Editor editor = pref.edit();
Log.d("Question_IDs", "getAllQuestionID() = " + response.body().getQuestion().getAll_question_ids() );
editor.putString(Constants.All_QUESTION_IDS,((resp.getQuestion().getAll_question_ids().toString())));
editor.apply();
}
progress.setVisibility(View.INVISIBLE);
It is here that I am stuck, as I am retrieving the array ok but I am unsure how to loop out the Array which is now stored in Shared Preferences.
When I place a toast to show me how the IDs are coming across, my toast confirms the data as [1,2,3]
The goal is to add a dynamic button and the individual ID, i.e button 1, button 2 etc every-time the loop is iterated.
I have tried the following:
String questionNumber = pref.getString(Constants.All_QUESTION_IDS, "");
for (int i =0; i < questionNumber.length(); i++) {
try {
/*Dynamically create new Button which includes the question name
*/
AppCompatButton btn_question = new AppCompatButton(getActivity());
/*LayoutParams (int width, int height,float weight)
As LayoutParams is defaulted in px, I have called a method called dpToPX to make sure
the dynamically added EditText is the same size on all devices.
*/
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(dpToPx(280), dpToPx(45), 1);
btn_question.setBackgroundColor(Color.parseColor("#3B5998"));
btn_question.setTextColor(Color.WHITE);
// btn_question.setText(String.valueOf(x));
btn_question.setText("Question "+ pref.getString(Constants.All_QUESTION_IDS,""));
btn_question.setGravity(Gravity.CENTER);
//generate unique ID for each new EditText dynamically created
View.generateViewId();
//Log.d("TEST VALUE", "Question1 generated ID = " + btn_question.generateViewId());
params.setMargins(0, dpToPx(10), 0, dpToPx(10));
btn_question.setPadding(0, 0, 0, 0);
btn_question.setLayoutParams(params);
allEds.add(btn_question);
mLayout.addView(btn_question);
} catch (Exception e) {
Log.d(TAG, "Failed to create new edit text");
}
}
However the above is adding the value as it appears in the array e.g [1,2,3] which is obviously not what I want.
I have added a photo in case my explanation isn't clear. I want a button with 1 number added to it each time the loop iterates but I am unable to figure this out.
I have looked through lots of resource but cannot find an answer that is relevant to my problem, although, if there is, I am not familiar enough to recognise a similar issue.
If someone can offer some assistance, I would appreciate it!
When you call editor.putString(Constants.All_QUESTION_IDS,((SOMETHING.toString())));, what is actually stored depends on the implementation of the toString method in the type of SOMETHING (in this case String[]). So avoid doing that. Instead, since you're already using Gson or Jackson (or others), store the question_idsas JSON:
final String jsonIds = gson.toJson (resp.getQuestion().getAll_question_ids());
editor.putString(Constants.All_QUESTION_IDS, jsonIds);
Your actual stored value no longer depends on the implementation of something that you don't control (String[].toString). It is a valid JSON array and regardless of what tool/library you use to read it back, it's valid.
Now, to read back the stored data:
final String storedJson = pref.getString(Constants.All_QUESTION_IDS, null);
if (null == storedJson) {
// TODO: No question ids found
}
final String[] ids = gson.fromJson (storedJson, String[].class);
for (int i = 0; i < ids.length; i++) {
// make your buttons
}
This is a problem of saving and then reading out a List of items (in this case, String instances).
You've chosen to save the list by calling editor.putString() with a value of getAll_question_ids().toString(). That toString() call is going to return a string representation of your list, or, in other words, a String instance with the value [1, 2, 3]. At this point, you no longer have a List proper, but a String that looks like a list.
This is all technically fine, but it means you have to take this into account when you're trying to read out that list.
You've written this to read the list back out:
String questionNumber = pref.getString(Constants.All_QUESTION_IDS, "");
Once this line executes, questionNumber will be a String instance with the value [1, 2, 3]. Again, this is fine, but now we come to the key point: we have to convert this String back into a List.
If you know for sure that the values in this list won't have commas in them, you can do it easily:
Trim the braces off the string using substring()
Create a String[] using split()
Convert your array to a list using Arrays.asList() (you could even skip this step since iterating over an array is just as easy as iterating over a list)
Put that together and you get:
String questionNumber = pref.getString(Constants.All_QUESTION_IDS, "");
questionNumber = questionNumber.substring(1, questionNumber.length() - 1);
String[] array = questionNumber.split(", ");
List list = Arrays.asList(array);
At this point, you can iterate over your array or list:
for (String value : list) {
...
btn_question.setText("Question " + value);
...
}

How to auto_increment a field or index in my Parse class?

I want something like
objectid id name lastname pic
hx5w887 1 name1 lastname1 pic1
lops4wus 2 name2 lastname2 pic2
zh7w8sa 3 name3 lastname3 pic3
I don't want to change the objectId, just I want that field and every time I save an object increment in 1. I am searched a lot in google, about this, it is no possible at least you can something with Cloud Parse code, but I do not know how to make this function, I don't know if "Increment" can help me with this, and I do not know how to run the function anyway.
Parse.Cloud.afterSave("counter", function(request) {
var nameId = request.object.get("name").id;
var Name = Parse.Object.extend("Name");
var query = new Parse.Query(Name);
query.get(nameId).then(function(post) {
post.increment("idkey",+1);
post.save();
}, function(error) {
throw "Got an error " + error.code + " : " + error.message;
});
});
I deploy and
call the function in Android
ParseCloud.callFunctionInBackground("counter", new HashMap<String, Object>(), new FunctionCallback<String>() {
// #Override
public void done(String result, ParseException e) {
if (e == null) {
} else {
// handleError();
}
}
});
But nothing happens, what can be the problem? Sorry my bad english.
You can use ParseCloud 'beforeSave' functionality.
You can declare a code which will run before saving a new object of a specific class.
In this code you query for your class items, order it and get the first item (the highest value) then you can the next value (highest +1) to the new saved object.
For more info you can take a look at Parse documentation and in this thread (it is not in java but it is very similar)
EDIT: Since Parse is not longer is now an open source might be that things have changed.

Realm on Android - How to select multiple objects by list of ids (#PrimaryKey)?

I'm building an Android app with the Realm database.
I have a RealmObject subclass called Article which has an id field (it's and int and also a #PrimaryKey). I would like to pass to a query a list of ints (a Set, int[], or whatever) of article id's and retrieve only those articles.
In SQL would be like this:
SELECT *
FROM `table`
where ID in (5263, 5625, 5628, 5621)
I've seen it's possible to do this in iOS in this StackOverflow question.
How can I do this in Android? Thanks!
Edit: Just to inform, I also asked this on the GitHub repo here.
Update:
Realm 1.2.0 has added RealmQuery.in() for a comparison against multiple values. The documentation details all the available overloads. This one is the method we can use if our ids are Integers:
public RealmQuery<E> in(String fieldName, Integer[] values)
Original answer:
The answer from #ChristianMelchior returns all articles if the list of ids is empty. I want it to return an empty RealmResults<Article>. That's what I've ended up doing:
Set<Integer> articleIds = this.getArticleIds();
RealmQuery<Article> query = realm.where(Article.class);
if (articleIds.size() == 0) {
// We want to return an empty list if the list of ids is empty.
// Just use alwaysFalse
query = query.alwaysFalse();
} else {
int i = 0;
for (int id : articleIds) {
// The or() operator requires left hand and right hand elements.
// If articleIds had only one element then it would crash with
// "Missing right-hand side of OR"
if (i++ > 0) {
query = query.or();
}
query = query.equalTo("id", id);
}
}
return query.findAll();
Now realm v 1.2.0 support RealmQuery.in() for a comparison against multiple values.
The Realm Java API's doesn't support this yet unfortunately. You can follow the feature request here https://github.com/realm/realm-java/issues/841
The current work-around would be to build up the query yourself in a for-loop:
RealmResults<Article> articles = realm.allObjects(Article.class);
RealmQuery q = articles.where();
for (int id : ids) {
q = q.equalTo("id", id);
}
RealmResults<Article> filteredArticles = q.findAll();
This is the way Realm does it since 1.2.0:
public RealmQuery<E> in(String fieldName, String[] values) {
if (values == null || values.length == 0) {
throw new IllegalArgumentException(EMPTY_VALUES);
}
beginGroup().equalTo(fieldName, values[0]);
for (int i = 1; i < values.length; i++) {
or().equalTo(fieldName, values[i]);
}
return endGroup();
}
Previously this is how I did it
I just came across this post and I thought I could throw in my 2 cents on this. As much as I appreciate Christian Melchior and his answers I think in this case his answer is not working (at least in the current version).
I prefer to do it like this - I personally think it's more readable than Albert Vila's answer:
List<String> listOfIds = [..];
RealmQuery<SomeClass> query = realm.where(SomeClass.class);
boolean first = true;
for (String id : listOfIds) {
if (!first) {
query.or();
} else {
first = false;
}
query.equalTo("id", id);
}
RealmResults<SomeClass> results = query.findAll();

Android - Returning a position of an item in an ArrayList that appears multiple times

I have three ArrayLists, two are Strings and the last one is an Integer.
The first ArrayList contains the specific Variant (variantArray) for a certain Product like the flavor variant of a cola, the second one contains the Unit (unitArray) that contains the unit like the size (80oz, 500mL, 1L) of the product, and the last one is the quantity *(quantityArray).
This is the class I use.
public class CurrentOrderClass {
//ArrayLists
private ArrayList<String> variantArray = new ArrayList<String>();
private ArrayList<String> unitArray = new ArrayList<String>();
private ArrayList<Integer> quantityArray = new ArrayList<Integer>();
//TODO ArrayList functions
public ArrayList<String> getUnitArray() {
return unitArray;
}
public void setUnitArray(ArrayList<String> unitArray) {
this.unitArray = unitArray;
}
public void addToUnitArray(String unit){
this.unitArray.add(unit);
}
public ArrayList<Integer> getQuantityArray() {
return quantityArray;
}
public void setQuantityArray(ArrayList<Integer> quantityArray) {
this.quantityArray = quantityArray;
}
public void addToQuantityArray(int quantity){
this.quantityArray.add(quantity);
}
public ArrayList<String> getVariantArray() {
return variantArray;
}
public void setVariantArray(ArrayList<String> variantArray) {
this.variantArray = variantArray;
}
public void addToVariantArray(String variantArray){
this.variantArray.add(variantArray);
}
#Override
public String toString() {
return "[ product=" + productName + ", variants=" +
variants + " , unit=" + unit + " , quantity=" + quantity + "]";
}
}
I take in user input so the user chooses the Variant, Unit, and Quantity and the input is then stored in their respective ArrayLists.
However, I'm having a problem updating the ArrayLists when the user inputs a Variant and a Unit that already exists in the ArrayLists but only with a different Quantity. What I want to do is not to add the new entry, but update the current ArrayLists in such a way that when the user inputs a variant and a unit but different quantity, I'd only update the quantity.
The first thing I tried was to see if the input already exists in the ArrayList by using indexOf(userInputVariant) and then check if it matches with a indexOf(userInputUnit), this would mean that the user input repeated already. However, I don't think indexOf runs on that logic and the value it returns is the value where it found the first instance of the userInputVariant string.
The second attempt I tried was using a for each loop, however, I'm once again having a hard time returning the index I want properly.
I instantiate an object of the CurrentOrderClass named currentOrder and prepopulated the first 10 elements of its variantArray with "Grape"
After that, I tried this if-else statement:
if( (currentOrder.getVariantArray().indexOf(product.getVariant()[variantPosition]) ==
currentOrder.getUnitArray().indexOf(product.getUnit()[position])
However, as mentioned above, indexOf returns an int where it first found the String that I told it to find, I don't think it checks if it exists after that certain position.
The second code I tried was to use a for-each and return the position from there, but again, it only returned position = 0.
for( String Grape : currentOrder.getVariantArray() ){
Log.d("Angelo", "Retrived Element: " + Grape
+ " at position = " + currentOrder.getVariantArray().indexOf(Grape));
}
I'm currently thinking of running a for-each loop inside a for-each loop but I don't know how to make it return the proper position that I want.
Does anyone have an idea on how to make it work properly? I need to return the position of an item that appears multiple times in my variantArray by "cross-referencing" it with my unitArray. Thanks.
One for loop is enough in this case.
This will some thing like:
isExist = false;
for(index = 0; index<variantArray.size(); index++){
if(variantArray.get(index) == userChoiceVariant &&
unitArray.get(index) == userChoiceUnit){
//update quantity
isExist = true;
}
}
if(!isExist){
//insert order
}
The first solution I reach is: "Search all the indexes matching with the user input in the variant array, store this in another ArrayList<Integer>, name it, matchingVariants for now. Then, for each of the ints in matchingVariants get that position of the units list. If one of this items matches, then you should update, otherwise insert.
However, I think a better solution is wrap Variants and Units in one class, for example Product with attributes String variant and String unit. Implementing equals method you can forget about your variants and units lists and have only one ArrayList<Product>
As a next step, I would use a Map with Product as key and Integer as values. So I can just update the Map rather than having all the lists you have.

Categories

Resources