I can't get object from one Activity to another (android) - android

I want to pass my object oManagement from activity X to Y.
In X:
oManagement = new OrdersManagement();
myIntent = new Intent(this, Y.class);
Then I fill my oManagement object with separate thread.
Then I am waiting when oManagement filled and start another activity:
while (oManagement.allOrders.size() == 0) {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (oManagement.allOrders.size() > 0) {
myIntent.putExtra("OrdersManagementObject", oManagement);
this.startActivity(myIntent);
}
}
And in my Y Activity:
Bundle b = this.getIntent().getExtras();
if (b != null) {
oManagement = b.getParcelable("OrdersManagementObject");
}
And now surprise -> oManagement is not null, but oManagement.allOrders.size() is 0! :( So i pass my object, but ArrayList inside by object is 0 (in moment of passing was 1 element inside that Array).
How to fix that?
EDIT:
OrdersManagement class:
public class OrdersManagement implements Parcelable {
public ArrayList<QuantorsOrder> allOrders = new ArrayList<QuantorsOrder>();
public OrdersManagement() {
}
public OrdersManagement(Parcel in) {
}
public static final Creator<OrdersManagement> CREATOR = new Creator<OrdersManagement>() {
#Override
public OrdersManagement createFromParcel(Parcel in) {
return new OrdersManagement(in);
}
#Override
public OrdersManagement[] newArray(int size) {
return new OrdersManagement[size];
}
};
public void updateListOfOrders(JSONArray jsonArray) throws JSONException {
if (jsonArray != null && jsonArray.length() > 0)
{
JSONObject json;
for (int i = 0; i < jsonArray.length(); i++)
{
json = (JSONObject)jsonArray.get(i);
if(!IsOrderAlreadyExists(json))
addOrder(json);
}
}
}
private boolean IsOrderAlreadyExists(JSONObject json) throws JSONException
{
if (allOrders.isEmpty())
return false;
else
{
for(QuantorsOrder order : allOrders)
{
if (String.valueOf(order.getId()).equals(json.get("id").toString()))
{
return true;
}
}
return false;
}
}
private void addOrder(JSONObject json) throws JSONException {
allOrders.add(new QuantorsOrder(json));
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
}
}

Your QuantorsOrdershould also implement Parcelable.
And in writeToParcel() method you should write the list and read it in constructor like this:
public void writeToParcel(Parcel out, int flags) {
out.writeTypedList(allOrders);
}
private OrdersManagement (Parcel in) {
in.readTypedList(allOrders, QuantorsOrder.CREATOR);
}
You should also implement the logic of writing and reading field in your QuantorsOrder class.
Here is example on simple class:
public class MyParcelable implements Parcelable {
private int mData;
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mData);
}
public static final Parcelable.Creator<MyParcelable> CREATOR
= new Parcelable.Creator<MyParcelable>() {
public MyParcelable createFromParcel(Parcel in) {
return new MyParcelable(in);
}
public MyParcelable[] newArray(int size) {
return new MyParcelable[size];
}
};
public MyParcelable(Parcel in) {
mData = in.readInt();
}
}
The main thing here is happening in writeToParcel(...) method and in constructor public MyParcelable(Parcel in).

Related

In fragment, restore an object that contains a list of another object with Parcelable implementation

I have a Car class implements Parcelable:
public class Car implements Parcelable {
private long id;
private String model;
public Car(long id, String model) {
this.id = id;
this.model = model;
}
public String getModel() {
return model;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(this.id);
dest.writeString(this.model);
}
private Car(Parcel in) {
this.id = in.readLong();
this.model = in.readString();
}
public static final Creator<Car> CREATOR = new Creator<Car>() {
#Override
public Car createFromParcel(Parcel source) {
return new Car(source);
}
#Override
public Car[] newArray(int size) {
return new Car[size];
}
};
}
Then, I have a Store class which holds a list of cars, it also implements Parcelable:
public class Store implements Parcelable {
private List<Car> carList = new ArrayList<>();
public List<Car> getCarList() {
return carList;
}
public void setCarList(List<Car> cars) {
carList = cars;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeTypedList(this.carList);
}
private Store(Parcel in) {
in.readTypedList(carList, Car.CREATOR);
}
public static final Creator<Store> CREATOR = new Creator<Store>() {
#Override
public Store createFromParcel(Parcel source) {
return new Store(source);
}
#Override
public Store[] newArray(int size) {
return new Store[size];
}
};
}
In a Fragment, I try to restore the Store instance named myStore, including the list of cars in it:
class MyFragment extends Fragment {
private Store myStore;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null && savedInstanceState.containsKey(MY_STORE)) {
myStore = savedInstanceState.getParcelable(MY_STORE);
// PROBLEM IS HERE:
// when runtime hit here, the restored myStore contains one car but has id with weird long value and null in model field. WHY?
} else {
// initialize myStore with car list (only one car)
Car myCar = new Car(1, "BMW X1");
List<Car> cars = new ArrayList<>();
cars.add(myCar);
myStore = new Store();
myStore.setCarList(cars);
}
}
#Override
public void onSaveInstanceState(#NonNull Bundle outState) {
if(myStore != null) {
outState.putParcelable(MY_STORE, myStore);
}
super.onSaveInstanceState(outState);
}
}
Problem occur when in MyFragment, code tries to restore myStore in onCreate(). At runtime , the restored myStore does contain one car in a list, but the car in that list has id with weird long value (e.g. 28429333427126393) and null in model field. WHY? What did I do wrong? How to restore an object contains a list of another object?
This problem usually occurs when your carList instance was null so you wrote a null object in parcelable and try to read from it.
One of the options is that you initialize your list when it's null and then write it in parcelable. Using this case your list will always have a reference after restoration which might not be what you're looking for.
The other approach is to write a byte value before writing your list into parcelable indicating that your list was null or you should expect some value from it. For this case I'm using this helper class which solved my own problem. Based on my needs, I have made some improvements. Here is my version if you'd prefer to use it:
public class ParcelableUtils {
public static void write(Parcel dest, String string) {
dest.writeByte((byte) (string == null ? 0 : 1));
if (string != null) {
dest.writeString(string);
}
}
public static String readString(Parcel source) {
if (source.readByte() == 1) {
return source.readString();
}
return null;
}
public static void write(Parcel dest, Parcelable parcelable, int flags) {
dest.writeByte((byte) (parcelable == null ? 0 : 1));
if (parcelable != null) {
dest.writeParcelable(parcelable, flags);
}
}
public static <T extends Parcelable> T readParcelable(Parcel source) {
if (source.readByte() == 1) {
return source.readParcelable(null);
}
return null;
}
public static <T extends Parcelable> T readParcelable(Parcel source, Class objectClass) {
if (source.readByte() == 1) {
return source.readParcelable(objectClass.getClassLoader());
}
return null;
}
public static void write(Parcel dest, Map<String, String> strings) {
if (strings == null) {
dest.writeInt(-1);
}
{
dest.writeInt(strings.keySet().size());
for (String key : strings.keySet()) {
dest.writeString(key);
dest.writeString(strings.get(key));
}
}
}
public static Map<String, String> readStringMap(Parcel source) {
int numKeys = source.readInt();
if (numKeys == -1) {
return null;
}
HashMap<String, String> map = new HashMap<String, String>();
for (int i = 0; i < numKeys; i++) {
String key = source.readString();
String value = source.readString();
map.put(key, value);
}
return map;
}
public static <T extends Parcelable> void write(Parcel dest, Map<String, T> objects, int flags) {
if (objects == null) {
dest.writeInt(-1);
} else {
dest.writeInt(objects.keySet().size());
for (String key : objects.keySet()) {
dest.writeString(key);
dest.writeParcelable(objects.get(key), flags);
}
}
}
public static <T extends Parcelable> Map<String, T> readParcelableMap(Parcel source) {
int numKeys = source.readInt();
if (numKeys == -1) {
return null;
}
HashMap<String, T> map = new HashMap<String, T>();
for (int i = 0; i < numKeys; i++) {
String key = source.readString();
T value = source.readParcelable(null);
map.put(key, value);
}
return map;
}
public static void write(Parcel dest, URL url) {
dest.writeString(url.toExternalForm());
}
public static URL readURL(Parcel source) {
try {
return new URL(source.readString());
} catch (MalformedURLException e) {
e.printStackTrace();
}
return null;
}
public static void write(Parcel dest, Date date) {
dest.writeByte((byte) (date == null ? 0 : 1));
if (date != null) {
dest.writeLong(date.getTime());
}
}
public static Date readDate(Parcel source) {
if (source.readByte() == 1) {
return new Date(source.readLong());
}
return null;
}
public static <T extends java.lang.Enum<T>> void write(Parcel dest, java.lang.Enum<T> enu) {
if (enu == null) {
dest.writeString("");
} else {
dest.writeString(enu.name());
}
}
public static <T extends java.lang.Enum<T>> T readEnum(Parcel dest, Class<T> clazz) {
String name = dest.readString();
if ("".equals(name)) {
return null;
}
return java.lang.Enum.valueOf(clazz, name);
}
public static void write(Parcel dest, boolean bool) {
dest.writeByte((byte) (bool ? 1 : 0));
}
public static boolean readBoolean(Parcel source) {
return source.readByte() == 1;
}
public static <T extends Parcelable> void write(Parcel dest,
List<T> fields, int flags) {
if (fields == null) {
dest.writeInt(-1);
} else {
dest.writeInt(fields.size());
for (T field : fields) {
dest.writeParcelable(field, flags);
}
}
}
#SuppressWarnings("unchecked")
public static <T extends Parcelable> List<T> readParcelableList(
Parcel source) {
int size = source.readInt();
if (size == -1) {
return null;
}
ArrayList<T> list = new ArrayList<T>();
for (int i = 0; i < size; i++) {
list.add((T) source.readParcelable(null));
}
return list;
}
}
Hope it helps.

Parcable of generic array

I generate parcelable implementation with plugin and that is the code i got.
I have a an error compilation on Metrics(Parcel in).
It probably because the generic array. How can i fix it?
The generic T can can contain String, Number or boolean only.
public class Metrics<T extends Parcelable> implements Parcelable {
private T[] yData;
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeTypedArray(this.yData, flags);
}
public Metrics() {
}
protected Metrics(Parcel in) {
this.yData = in.createTypedArray(T.CREATOR);
}
public static final Parcelable.Creator<Metrics> CREATOR = new Parcelable.Creator<Metrics>() {
#Override
public Metrics createFromParcel(Parcel source) {
return new Metrics(source);
}
#Override
public Metrics[] newArray(int size) {
return new Metrics[size];
}
};
}
You can use a List instead of an array :
public class Metrics<T extends Parcelable> implements Parcelable {
private List<T> yData;
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
if (yData == null || yData.size() == 0)
dest.writeInt(0);
else {
dest.writeInt(yData.size());
final Class<?> objectsType = yData.get(0).getClass();
dest.writeSerializable(objectsType);
dest.writeList(yData);
}
}
public Metrics() {
}
protected Metrics(Parcel in) {
int size = in.readInt();
if (size == 0) {
yData = null;
} else {
Class<?> type = (Class<?>) in.readSerializable();
yData = new ArrayList<>(size);
in.readList(yData, type.getClassLoader());
}
}
public static final Parcelable.Creator<Metrics> CREATOR = new Parcelable.Creator<Metrics>() {
#Override
public Metrics createFromParcel(Parcel source) {
return new Metrics(source);
}
#Override
public Metrics[] newArray(int size) {
return new Metrics[size];
}
};
}

Sending Nested Parcelable with Intent

I am trying to send a Parcelable object which also contains another Parcelable object with Intent. However, I am getting NullPointer Exception. Could you please tell me where I am doing wrong?
A.java
public class A implements Parcelable {
private ArrayList<B> var;
public A()
{
this.var = new ArrayList<B>();
}
public void addToB(B b)
{
var.add(b);
}
public ArrayList<B> getB() {
return var;
}
public void setB(ArrayList<B> b) {
this.var = b;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeTypedList(this.var);
}
private A (Parcel in){
in.readTypedList(this.var, B.CREATOR);
}
public static final Parcelable.Creator<A> CREATOR = new Parcelable.Creator<A>() {
public A createFromParcel(Parcel in) {
return new A(in);
}
public A[] newArray(int size) {
return new A[size];
}
};
}
B.java
public class B implements Parcelable {
private String type;
public B(String type)
{
this.type=type;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel arg0, int arg1) {
arg0.writeString(this.type);
}
private B(Parcel in) {
this.type=in.readString();
}
public static final Parcelable.Creator<B> CREATOR = new Parcelable.Creator<B>() {
public B createFromParcel(Parcel in) {
return new B(in);
}
public B[] newArray(int size) {
return new B[size];
}
};
}
I send the A object with Intent like this:
Intent i = new Intent(Bla.this, Blabla.class);
i.putExtra("info", objectA);
startActivity(i);
I receive the parcelable like this:
Intent i = getIntent();
ObjectA ci = (ObjectA)i.getParcelableExtra("info");
You have to instanciate your ArrayList< B > in :
private A (Parcel in){
var = new ArrayList<B>();
in.readTypedList(this.var, B.CREATOR);
}
Say if it works :)
You are not assigning data to var variable in constructor.
change it like
private A (Parcel in){
var=(ArrayList)in.readTypedList(this.var, B.CREATOR);
}
Edit::
Change your code like this and try
#Override
public void writeToParcel(Parcel dest, int flags) {
B[] data = new B[var.size()];
for (int i = 0; i < data.length; i++) {
data[i] = var.get(i);
}
dest.writeParcelableArray(data, flags);
}
public A(Parcel in) {
Parcelable[] parcelables = in.readParcelableArray(Thread
.currentThread().getContextClassLoader());
ArrayList<B> list = new ArrayList<B>();
for (Parcelable parcelable : parcelables) {
list.add((B) parcelable);
}
var = list;
}
you have problem with parsing the arraylist... do these changes..
private A (Parcel in){
in.readTypedList(this.var, B.CREATOR);
}
to
this.var=in.readArrayList(B.class.getClassLoader());
and
public void writeToParcel(Parcel dest, int flags) {
dest.writeTypedList(this.var);
}
to
dest.writeList(this.var);

Sending list of custom objects as an intent to another activity

I have a class Tag Model and i need to send List< TagModel> to another activity.
If i implement parcelable then I have another custom object array list in its items.
How to solve this problem?
public class TagModel {
int listing_count;
ArrayList<ListingsModel> listingsModel; //how to handle this list
String foodTypeName;
public int getListing_count() {
return listing_count;
}
public void setListing_count(int listing_count) {
this.listing_count = listing_count;
}
public String getFoodTypeName() {
return foodTypeName;
}
public void setFoodTypeName(String foodTypeName) {
this.foodTypeName = foodTypeName;
}
public ArrayList<ListingsModel> getListings() {
return listingsModel;
}
public void setListings(ArrayList<ListingsModel> listingsModel) {
this.listingsModel = listingsModel;
}
}
You 2 objects have to be Parceable to send.
I'm assuming your object "ListingsModel" is already parceable.
TagModel Parceable
public class TagModel implements Parcelable {
int listing_count;
ArrayList<ListingsModel> listingsModel; //how to handle this list
String foodTypeName;
public int getListing_count() {
return listing_count;
}
public void setListing_count(int listing_count) {
this.listing_count = listing_count;
}
public String getFoodTypeName() {
return foodTypeName;
}
public void setFoodTypeName(String foodTypeName) {
this.foodTypeName = foodTypeName;
}
public ArrayList<ListingsModel> getListings() {
return listingsModel;
}
public void setListings(ArrayList<ListingsModel> listingsModel) {
this.listingsModel = listingsModel;
}
protected TagModel(Parcel in) {
listing_count = in.readInt();
if (in.readByte() == 0x01) {
listingsModel = new ArrayList<ListingsModel>();
in.readList(listingsModel, ListingsModel.class.getClassLoader());
} else {
listingsModel = null;
}
foodTypeName = in.readString();
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(listing_count);
if (listingsModel == null) {
dest.writeByte((byte) (0x00));
} else {
dest.writeByte((byte) (0x01));
dest.writeList(listingsModel);
}
dest.writeString(foodTypeName);
}
#SuppressWarnings("unused")
public static final Parcelable.Creator<TagModel> CREATOR = new Parcelable.Creator<TagModel>() {
#Override
public TagModel createFromParcel(Parcel in) {
return new TagModel(in);
}
#Override
public TagModel[] newArray(int size) {
return new TagModel[size];
}
};
}
For parcelable object I recommend using this great tool parcelabler

ClassCastException when trying to use ParcelableArrayList on custom object array

I’m trying to implement Parcelable instead of Serializable as it’s supposed to be more efficient.
From my MainActivity I want to pass the object of generic type ArrayList<LogChartData> to the same activity, so that I get it on orientation change.
If I get past this one error I’m sure I’ll run in to a lot more of them, but right now when I try to run my app I’m getting,
Caused by: java.lang.ClassCastException: java.lang.String cannot be cast to java.util.Date
at com.testapp.util.LogChartData.<init>(LogChartData.java:60)
at com.testapp.util.LogChartData.<init>(LogChartData.java:59)
at com.testapp.util.LogChartData$1.createFromParcel(LogChartData.java:51)
at com.testapp.util.LogChartData$1.createFromParcel(LogChartData.java:1)
at android.os.Parcel.readParcelable(Parcel.java:2103)
at android.os.Parcel.readValue(Parcel.java:1965)
MainActivity.java--
ArrayList<LogChartData> logss = new ArrayList<LogChartData>();
#Override
public void onSaveInstanceState(final Bundle outState) {
outState.putParcelableArrayList("logss", logss);
outState.putParcelableArrayList("alert", alert);
outState.putBoolean("isRotate", true);
super.onSaveInstanceState(outState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
if (savedInstanceState != null) {
try
{
logss = savedInstanceState.getParcelableArrayList("logss");
alert = savedInstanceState.getParcelableArrayList("alert");
isRotate = savedInstanceState.getBoolean("isRotate");
}
catch(ClassCastException e)
{
e.printStackTrace();
}
if(!isRotate)
someFunctionCall();
// More code here..
}
}
LogCharData.java --
public class LogChartData implements Parcelable {
public Date chartDate = null;
public ArrayList<LogEntries> logs = new ArrayList<LogEntries>();
public LogChartData(Date chartDate, ArrayList<LogEntries> logs) {
super();
this.chartDate = chartDate;
this.logs = logs;
}
public LogChartData() {
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
}
public static final Parcelable.Creator<LogChartData> CREATOR =
new Parcelable.Creator<LogChartData>() {
public LogChartData createFromParcel(Parcel in) {
return new LogChartData(in);
}
public LogChartData[] newArray(int size) {
return new LogChartData[size];
}
};
private LogChartData(Parcel in) {
this.chartDate =
(Date)in.readValue(LogChartData.class.getClassLoader()); //Error Here..
in.readList(logs, LogChartData.class.getClassLoader());
}
public Date getChartDate() {
return chartDate;
}
public void setChartDate(Date chartDate) {
this.chartDate = chartDate;
}
public ArrayList<LogEntries> getLogs() {
return logs;
}
public void setLogs(ArrayList<LogEntries> logs) {
this.logs = logs;
}
}
You cannot Directly cast the String variable directly to the Date Object like this
(Date)in.readValue(LogChartData.class.getClassLoader());
You need to use SimpleDateFormat and parse the String from Specified DateFormat and then Stored in the Date Object
Suppose if your Date as the format of "yyyy-MM-dd"
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
this.chartDate= format.parse(in.readValue(LogChartData.class.getClassLoader()));
you have to use writeToParcel(Parcel dest, int flags) method. do something like this
public LogChartData(Parcel source) {
Parcelable[] parcelables = source.readParcelableArray(Thread
.currentThread().getContextClassLoader());
for (Parcelable parcelable : parcelables) {
logs.add((LogEntries) parcelable);
}
}
#Override
public void writeToParcel(Parcel dest, int flags) {
LogEntries[] data = new LogEntries[getLogs().size()];
for (int i = 0; i < data.length; i++) {
data[i] = getLogs().get(i);
}
dest.writeParcelableArray(data, flags);
}
I don't know about Date object. it is Parcelable or not . I think you have to convert it into string using SimpleDateFormat .
LogEntries should implement Parcelable interface as well
Suppose:
public class LogEntries implements Parcelable {
private String log;
public LogEntries() {}
public LogEntries(String log) {
this.log = log;
}
public String getLog() {
return log;
}
public void setLog(String log) {
this.log = log;
}
/*--- INIT parcelable code ---*/
#Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
public void readFromParcel(Parcel source) {
this.log = (String)source.readValue(String.class.getClassLoader());
}
/* Parceling constructor */
public LogEntries(Parcel source) {
this();
readFromParcel(source);
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeValue(log);
}
public static final Parcelable.Creator<LogEntries> CREATOR = new Parcelable.Creator<LogEntries>() {
#Override
public LogEntries createFromParcel(Parcel source) {
return new LogEntries(source);
}
#Override
public LogEntries[] newArray(int size) {
return new LogEntries[size];
}
};
/*--- END parcelable code ---*/
}
Then:
public class LogChartData implements Parcelable {
public Date chartDate = null;
public ArrayList<LogEntries> logs = new ArrayList<LogEntries>();
public LogChartData(Date chartDate, ArrayList<LogEntries> logs) {
super();
this.chartDate = chartDate;
this.logs = logs;
}
public LogChartData() {}
public Date getChartDate() {
return chartDate;
}
public void setChartDate(Date chartDate) {
this.chartDate = chartDate;
}
public ArrayList<LogEntries> getLogs() {
return logs;
}
public void setLogs(ArrayList<LogEntries> logs) {
this.logs = logs;
}
/*--- INIT parcelable code ---*/
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
if(chartDate!=null) {
dest.writeLong(chartDate.getTime());
}
else {
dest.writeLong(0);
}
if(logs!=null) {
dest.writeInt(logs.size()); // specify the number of logs
dest.writeTypedList(logs);
}
else {
dest.writeInt(-1); // specify there are no logs
}
}
public LogChartData(Parcel source) {
this();
readFromParcel(source);
}
#Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
public void readFromParcel(Parcel source) {
long timeMillis = source.readLong();
if(timeMillis!=0) {
this.chartDate = new Date(timeMillis);
}
else {
this.chartDate = null;
}
int logsSize = source.readInt();
if(logsSize>-1) {
this.logs = new ArrayList<LogEntries>(logsSize);
if(logsSize>0) {
source.readTypedList(logs, LogEntries.CREATOR);
}
}
else {
this.logs = null;
}
}
public static final Parcelable.Creator<LogChartData> CREATOR = new Parcelable.Creator<LogChartData>() {
#Override
public LogChartData createFromParcel(Parcel source) {
return new LogChartData(source);
}
#Override
public LogChartData[] newArray(int size) {
return new LogChartData[size];
}
};
/*--- END parcelable code ---*/
}
where you can use the long value from Date and retrieve it accordingly

Categories

Resources