dynamic list of parameters without having repeated parameter name - android

I have my rest api like:
http://MY_SERVER/api/story?feed_ids=1,2,3&page=1
here i should provide dynamic list of feed_ids separated by comma,
for that i wrote my rest service like:
#GET("/story")
void getStory( #Query("feed_ids") List<Integer> feed_items, #Query("page") int page,Callback<StoryCollection> callback);
and:
private List<Integer> items = Arrays.asList(1, 2, 3); // items is a list of feed ids subscribed by user (retrieved from app db). initialization is done here just for testing
public void getLatestStoryCollection(int page ,Callback<StoryCollection> callback) {
newsService.getStory(items, page ,callback);
}
my code runs fine but retrofit sends request url like:
http://MY_SERVER/api/story?feed_ids=1&feed_ids=2&feed_ids=3&page=1
is there a way to send such dynamic list of parameters just like feed_ids=1,2,3 without having repeated parameter name?

You could create a custom class that overrides toString() to format them as a comma-separated list. Something like:
class FeedIdCollection extends List<Integer> {
public FeedIdCollection(int... ids) {
super(Arrays.asList(ids));
}
#Override
public String toString() {
return TextUtils.join(",", this);
}
}
And then make your declaration:
#GET("/story")
void getStory( #Query("feed_ids") FeedIdCollection feed_items, #Query("page") int page, Callback<StoryCollection> callback);

There is not a way to do it in retrofit, but you can do it pretty easily yourself. Since you are using android, you can use TextUtils.join() to convert any list to a String. Then pass that string to as your query parameter instead of a list. First, update your interface to take a String instead of a List.
#GET("/story")
void getStory( #Query("feed_ids") String feed_items, #Query("page") int page, Callback<StoryCollection> callback);
then when you call your getStory method, pass the items through join first --
String items = TextUtils.join(",", Arrays.asList(1, 2, 3));
newsService.getStory(items, page, callback);

Related

Add query parameter to Retrofit get in Android Studio

I'm trying to use retrofit for get records from my API and it works fine when i do something like this.
public interface materialAPI {
#GET("/mlearningServices/Course")
public void getMaterials(Callback<List<materialClass>> response); } public void getMaterials()
{
RestAdapter adapter = new RestAdapter.Builder().setEndpoint(Root_Url).build();
Log.i(TAG , "hERE IS THE LINK"+adapter.toString());
materialAPI api = adapter.create(materialAPI.class);
api.getMaterials(new Callback <List<materialClass>>() {
#Override
public void success(List<materialClass> list, Response response) {
materials = list;
showList();
customAdapter customAdapter = new customAdapter();
listView.setAdapter(customAdapter);
}
#Override
public void failure(RetrofitError error) {
}
});
}
The above code works fine and i can get all my materials but what i want to achieve next is get material with any id . When a user selects a paticular material, i want to pass the id into the get url so i can get the records
meaning i have to do something like this
#GET("/mlearningServices/Course/{myId}")
..
How to i add myId to the callback method. This is my first time of using retrofit
Use the #Path annotation
#POST("/mlearningServices/Course/{myId}")
public void getMaterials(#Path("myId") String id, Callback<Response> response);
References:
https://square.github.io/retrofit/2.x/retrofit/index.html?retrofit2/http/Path.html
http://square.github.io/retrofit/
That you are asking about is called a path variable. To set one, you must rewrite your method signature as this:
public void getMaterials(#Path("myId") String id, Callback<List<materialClass>> response);
This way, the variable defined as /path/to/your/endpoint/{nameOfPathVariable} will be injected into that String parameter passed to the method. You could also define it as an Integer, and retrofit will try to cast it accordingly.
Solution:
You can use this to pass your id, Use the #Path annotation
#GET("/mlearningServices/Course/{myId}")
Call<materialClass> getMaterials(#Path("myId") String id);
#Path is some data that you wish to provide it to GET method before Question Mark ("?") and #Query("..") is some data you wish to provide after "?"
Hope you have understood.

how to map values returned from observable to be used in another observable

I have the following observable to get list of feed ids from database ( i use sugar ORM library)
public Observable<Set<Long>> getFeedIdsFromDB() {
return Observable.create(subscriber -> {
Set<Integer> subscribedFeedIds = new HashSet<>();
//get feed ids from FeedEntity Table
for (FeedEntity feed : FeedEntity.listAll(FeedEntity.class)){
if (feed.isSubscribed()){
subscribedFeedIds.add(feed.getFeedId());
}
}
});
}
this Observable should emits ids to be used for api call in the following:
public Observable<StoryCollectionEntity> storyEntityList(final int page) {
return this.restApi.storyCollection(/* this is feed ids*/ id, page)
.distinct(storyCollectionEntity -> storyCollectionEntity)
.doOnNext(saveStoryCollectionToCacheAction)
}
i guess i should use some sort of mapping but have no idea how can i implement it.
EDIT:
i did the following modification:
// To map feed ids (retrieved from database) to getAllStoryEntityList Observable:
#Override
public Observable<StoryCollectionEntity> storyEntityList(final int page) {
return this.mNewsCache.getFeedIdsFromDB().flatMap(id -> getAllStoryEntityList(page, id));
}
//call restApi
public Observable<StoryCollectionEntity> getAllStoryEntityList(final int page, Set<Long> id){
return this.restApi.storyCollection( id, page)
.distinct(storyCollectionEntity -> storyCollectionEntity)
.doOnNext(saveStoryCollectionToCacheAction);
}
but api service is never called. something wrong in the mapping.
#GET("story")
Observable<StoryCollectionEntity> storyCollection(
#Query("feed_ids") Set<Long> feedIds,
#Query("page") int page);
The Observable created in getFeedIdsFromDB isn't emitting any items, so your flatMap and other data transformations never occur because the stream actually has no data. You can test this by subscribing directly to the returned Observable and doing something for onNext.
getFeedIdsFromDB().subscribe(feedId -> System.out.println(feedId));
You should see that nothing gets printed. When using Observable#create, the onNext method of subscriber in the anonymous class must be manually called with whatever data you wish to pass downstream. The documentation provides sample code for this.
So modifying your Observable to call onNext, we get this:
public Observable<Set<Long>> getFeedIdsFromDB() {
return Observable.create(subscriber -> {
Set<Integer> feedIds = new HashSet<>();
// get feed ids from FeedEntity Table
for (FeedEntity feed : FeedEntity.listAll(FeedEntity.class)){
feedIds.add(feed.getFeedId());
}
// emit a single Set and complete
if (subscriber.isSubscribed()) {
subscriber.onNext(feedIds);
subscriber.onCompleted();
}
});
}
Now the Set should get passed along. If your goal is to end up with a the emission of a single StoryCollectionEntity object after the transformations (and if I'm reading this properly), then your mapping looks correct.
I'm not sure what the expected output is, but I'll show you a way to do this sort of thing. Maybe you can alter it to fit your use case.
The first step is to allow id as a function parameter in storyEntityList:
public Observable<StoryCollectionEntity> storyEntityList(final int page, int id) {
return this.restApi.storyCollection(/* this is feed ids*/ id, page)
.distinct(storyCollectionEntity -> storyCollectionEntity)
.doOnNext(saveStoryCollectionToCacheAction)
Now you can use an Observable.flatMap:
public Observable<StoryCollectionEntity> getAllStoryEntityList(int page){
return getFeedIdsFromDB().flatMap(id -> storyEntityList(page, id));
}
The naming might be off, but again - I'm not sure what the entities are.

get list of IDs from DB and Pass them to Retrofit interface

I am trying to pass set of IDs to retrofit interface
#GET("story")
Observable<StoryCollectionEntity> storyCollection(
#Query("feed_ids") Set<Long> feedIds // this is the ids i want to pass,
#Query("page") int page);
Those IDs retrieved from DB and should be passed to api in comma separated i.e. like: 1,2,4,.. or just one value like 1 or 2
to retrieve ids from DB i wrote the following code:
//retrieve ids from DB
public Observable<Set<Long>> getFeedIdsFromDB() {
return Observable.create(subscriber -> {
Set<Integer> subscribedFeedIds = new HashSet<>();
for (FeedEntity feed : FeedEntity.listAll(FeedEntity.class)){
if (feed.isSubscribed()){
subscribedFeedIds.add(feed.getFeedId());
}
}});
}
in rest repository, call for response then do mapping:
public Observable<StoryList> stories(int page) {
return newsDataStore.storyEntityList(page)
.map(StoryList -> this.mNewsEntityDataMapper.transform(StoryList));
}
storyEntityList :
#Override
public Observable<StoryCollectionEntity> storyEntityList(final int page) {
return Observable.create(subscriber -> {
this.mNewsCache.getFeedIdsFromDB()
.flatMap(id ->this.restApi.storyCollection(id, page));
});
unfortunately this code is not working for me. i am not sure what is the exact problem as i see nothing happens after getFeedIdsFromDB() is called. in other word, no mapping is taken place in the last Observable.

Android Listview : Display Item details onItemClic

I have been working on a e-commerce application for a while, and now I have a ListView that displays a list of products - Each products = 1 ImageView and some TextViews-.
I set an onItemClick listener on that ListView, the event that I want to occur when I click on one of that listView products is, Start the i new Activity, 'ProductDetails' that displays more informations on the product I have clicked on.
I have already asked this question somewhere else, but I didn't get a clear answer,
They said that I have to create a new 'list\details' project, but this can't happen now, as I've working on that project for like 20 days, and can't start all over again.
You can't send a TextView or an ImageView from Activity to Activity. what you can send instead is it's contents. so for example if you want to pass the information from the TextView you will need to pass the source String that is displayed there (Or extract the String from the TextView) you do that by passing a Bundle from the calling Activity to the invoked one or simply by putting it as an Extra:
intent.putExtra("string name", value);
and in the following Activity you get this data:
Intent intent = getIntent();
bundle = intent.getExtras();
bundle.getString("string name");
Then in the following Activity create a TextView with the passed String.
Same way is handled with the ImageView by passing the it's path.
Sorry I have posted an answer but was only a link to another site so it was deleted :-(
Here I come again with what could be the solution:
In your first activity, you need to use intent.putExtra(...) and retrieve the datas in your second activity with intent.getExtra(...).
Example activity 1:
i.putExtra("title01", yourDataCollection.get(position).get(KEY_TITLE01));
Activity 2 :
this.title01 = i.getStringExtra("title01");
There is a full project here : http://www.codeproject.com/Articles/507651/Customized-Android-ListView-with-Image-and-Text
Android uses intent to allow Activitys to interact. If you want to show a details of product, probably you have a Class that describe that product and with the information it holds you fill up your ListView. If you let the class Product implements Serializable you can use Intent.putExtra(String name, Serializable obj), to pass the Product description between Activitys. In ProductDetailsActivity you can use:
getIntent(). getSerializableExtra (String name)
to retrive the serializable you put inside the Intent you created to start ProductDetailsActivity
If you pass your model class around many times using Intent, perhaps it could implement the Parcelable interface for convenience. For example:
class ProductData implements Parcelable {
protected String name;
protected String someOtherData;
public static final Parcelable.Creator<ProductData> CREATOR = new Parcelable.Creator<ProductData>() {
#Override
public ProductData createFromParcel(Parcel source) {
return new ProductData(source);
}
#Override
public ProductData[] newArray(int size) {
return new ProductData[size];
}
};
public ProductData(){
}
public ProductData(Parcel source){
readFromParcel(source);
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeString(someOtherData);
}
public void readFromParcel(Parcel source){
name = source.readString();
someOtherData = source.readString();
}
// Here goes the rest of your model code
}
Then you can easily pass the object to another Activity using an Intent:
intent.putExtra("product", productData);
and later get the data using
ProductData productData = intent.getParcelableExtra("product");

sorting custom object array on two fields

I'm sorting an array of custom objects (ListData[]) on two fields. I want it to be sorted by theme, and them by name. I thought i made a nice comparator in the custom object class and that i could use Arrays.sort(ld) to make my code working and sorting my array. But apparently im doing something wrong...
my custom object:
public class ListData implements Comparable<ListData>{
public int venueID;
public String name;
public String photoUrl;
public String tip;
public String theme;
#Override
public int compareTo(ListData ld0) {
return this.venueID- ld0.venueID;
}
public static Comparator<ListData> ListDataThemeAndNameComparator = new Comparator<ListData>() {
#Override
public int compare(ListData ld1, ListData ld2) {
String compareTheme1 = ld1.theme.toUpperCase();
String compareTheme2= ld2.theme.toUpperCase();
String compareName1 = ld1.name.toUpperCase();
String compareName2= ld2.name.toUpperCase();
//ascending
int comp = compareTheme1.compareTo(compareTheme2); // comp themes
if(comp==0){ // same theme
comp= compareName1.compareTo(compareName2); // compare names
}
return comp;
}
};
}
And in my main activity i have:
ListData ld[]= new ListData[jsonResponse.size()];
(some code filling my ListData array)
Arrays.sort(ld, ListData.ListDataThemeAndNameComparator); // compare by theme and then by name
Does anyone know what i'm doing wrong?
I edited my code But still it fails, now on a nullpointerexception on the compareTheme1 = ld1.theme.toUpperCase();. But i am sure my array is not empty, i logged it the line before sorting it and its filled with about 500 items.
Your ListData object should implements Comparable not Comparator interface.
EDIT:
To make things clear, you can sort an array by Array.sort(). To make custom sort, you can specify your comparator in Array.sort(), if you don't do that, array will be sorted in natural order which you can define by implementing Comparable interface. So you have two options how to custom sort:
by using custom comparator and specifying it in Array.sort()
by implementing Comparable interface to your items
I would suggest you to go with implementing Comparable. You save memory by not creating new comparator objects and Comparator is useful if you are comparing objects of different types which is not your case.

Categories

Resources