Multiple setAdapter in ListView - java

Here's the code with the problem:
//declarations:
private ArrayAdapter<String> listAdapter;
private ArrayAdapter<String> listAdapter2;
String [] nomi=null;
String[] famiglia=null;
private ListView mainListView;
// other code bla bla bla...
mainListView = (ListView) findViewById(R.id.listView);
// other code bla bla bla...
nomi = new String[res.size()];
for (int i = 0; i < res.size(); i++) {
nomi[i] = res.get(i).getNomignolo();
}
famiglia= new String[res.size()];
for(int i=0; i<res.size();i++){
famiglia[i] = res.get(i).getFamiglia();
}
ArrayList<String> listaNomi = new ArrayList<String>();
listaNomi.addAll(Arrays.asList(nomi));
ArrayList<String> listaFamiglie = new ArrayList<String>();
listaFamiglie.addAll(Arrays.asList(famiglia));
listAdapter = new ArrayAdapter<String>(HomeActivity.this, R.layout.row, R.id.button3, listaNomi);
listAdapter2 = new ArrayAdapter<String>(HomeActivity.this, R.layout.row, R.id.button6, listaFamiglie);
mainListView.setAdapter(listAdapter);
mainListView.setAdapter(listAdapter2);
It works, but only in part, because when i start the app i can find only the result of the second setAdapter method. How can i achieve also the result of all the setAdapter methods? Thanks.

try to add objects of second list in first on
nomi = new String[res.size()];
for (int i = 0; i < res.size(); i++) {
nomi[i] = res.get(i).getNomignolo() + " " +res.get(i).getFamiglia();
}
//list of object with name and family
create adapter
listAdapter = new ArrayAdapter<String>(HomeActivity.this, R.layout.row, R.id.button3, listaNomi);
mainListView.setAdapter(listAdapter);
you cannot set multiple adapter for one listview.
How to create custom array adapter:
public class CustomAdapter extends ArrayAdapter<Person>{
private final Activity context;
private ArrayList<Person> Items;
public CustomAdapter (Activity context, int layout,ArrayList<Person> persons) {
super(context, layout, carriers);
// TODO Auto-generated constructor stub
this.context = context;
this.Items = persons;
}
static class ViewHolder {
public Button name;
public Button family;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
// Recycle existing view if passed as parameter
// This will save memory and time on Android
// This only works if the base layout for all classes are the same
View rowView = convertView;
if (rowView == null) {
LayoutInflater inflater = context.getLayoutInflater();
rowView = inflater.inflate(R.layout.item, null, true);
holder = new ViewHolder();
holder.name= (TextView) rowView.findViewById(R.id.name);
holder.family= (TextView) rowView.findViewById(R.id.family);
rowView.setTag(holder);
} else {
holder = (ViewHolder) rowView.getTag();
}
holder.name.setText(Items.get(position).getName());
holder.family.setText(Items.get(position).getFamily());
return rowView;
}
}
and create PErson Object:
public class PErson{ public String name; public String family;
public Person();
public String getName(){ return name;}
public String getFamily(){return family;}
}
and this is item xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<Button
android:id="#+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="wkjdhk"
android:textColor="#color/green"
android:textSize="12sp" />
<Button
android:id="#+id/family"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="wkjdhk"
android:textColor="#color/green"
android:textSize="12sp" />
</RelativeLayout>
this is how to call from activity:
adapter = new MyCustomAdapter(CarrierSummaryActivity.this,nomi,R.layout.item);
mainListView.setAdapter(adapter);

Can you please do like this way ?
First List:
ArrayList<String> a = new ArrayList<String>();
a.add("a");
a.add("b");
a.add("c");
Second List:
ArrayList<String> b = new ArrayList<String>();
b.add("d");
b.add("e");
b.add("f");
Add a to b:
b.addAll(a);
Merge both list:
ArrayList<String> union = new ArrayList<String>(a);
union.addAll(b);
Set Adapter on ListView:
listAdapter = new ArrayAdapter<String>(HomeActivity.this, R.layout.row, R.id.button3, union);
mainListView.setAdapter(listAdapter);
Hope this will help you.

This is not possible, whenever you are setting second adapter to listview, your first adapters data get replaced with second.
LISTVIEW CAN DISPLAY SINGLE ADAPTER DATA AT SINGLE TIME.
If you want to display data from multiple sources to ListView then first merge all all data and then use single adapter. Do something like below
dataList1 (From source A)
datalist2 (From source B)
datalist3 = dataList1 + dataList2;
setDapter(dataList3)

You should implement your custom adapter so it can draw the list just like you want.
class MyAdapter extends BaseAdapter
{
#Override
public View getView(int position, View convertView, ViewGroup parent) {
//Here you can return a View with any data in any way
}
}
Hope this helps.

Related

How to add different types of view to a ListView

I want to add Text and Images to a TextView with a ArrayApapter.
If this ist the definition of the ArrayAdapter
new ArrayAdapter<View>(getContext(),R.id.element_manual_list, viewsOfPage);
I could use for element_manual_list a TextView or a ImageView but I want both. How can I implement this? Or is there any more easy way to write a userManual Text with images into a Scrollable View?
Something that is HTML styleable perhaps?
You need to create your own adapter class:
public class MyAdapter extends ArrayAdapter<User> {
public MyAdapter(Context context, ArrayList<User> users) {
super(context, 0, users);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
User user = getItem(position);
// Check if an existing view is being reused, otherwise inflate the view
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_user, parent, false);
}
// Lookup view for data population
TextView tvName = (TextView) convertView.findViewById(R.id.tvName);
ImageView ivPhoto = (ImageView) convertView.findViewById(R.id.ivPhoto);
// Populate the data into the template view using the data object
tvName.setText(user.name);
ivPhoto.setImageResource(user.photo);
// Return the completed view to render on screen
return convertView;
}
}
Then XML for your row:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="#+id/ivPhoto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#android:drawable/sym_def_app_icon" />
<TextView
android:id="#+id/tvName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Name" />
Then your custom for example User class:
public class User {
public String name;
public int photo;
public User(String name, int photo) {
this.name = name;
this.photo = photo;
}
}
In your main clas put this:
// Construct the data source
ArrayList<User> arrayOfUsers = new ArrayList<User>();
// Create the adapter to convert the array to views
MyAdapter adapter = new MyAdapter(this, arrayOfUsers);
// Attach the adapter to a ListView
ListView listView = (ListView) findViewById(R.id.lvItems);
listView.setAdapter(adapter);
// Add item to adapter
User newUser = new User("Nathan", R.drawable.logo);
adapter.add(newUser);

Adding a second textview to customAdapter ListView

So I have a listview with one textview and I want to add a second textview to it. But I cannot figure out how to modify the adapter and then call it in my main activity, even after looking through many similiar questions on stackoverflow.
Heres how my CustomAdapter.java is now
class CustomAdapter extends ArrayAdapter<CharSequence>{
public CustomAdapter(Context context, CharSequence[] routes) {
super(context, R.layout.custom_row ,routes);
}
#NonNull
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater routeInflater = LayoutInflater.from(getContext());
View customView = convertView;
if(customView == null){customView = routeInflater.inflate(R.layout.custom_row, parent, false);}
CharSequence singleRoute = getItem(position);
TextView routeText = (TextView) customView.findViewById(R.id.routeText);
routeText.setText(singleRoute);
///// Textview I want to add
CharSequence routeNum = getItem(position);
TextView routeNumText = (TextView) customView.findViewById(R.id.numbersTextView);
routeNumText.setText(routeNum);
/////
return customView;
and heres my MainActivity.java
///// fill listview numbers I want to add
final String[] routeListviewNumbers = getResources().getStringArray(R.array.routeNumbers);
//fill list view with xml array of routes
final String[] routeListViewItems = getResources().getStringArray(R.array.routeList);
//custom adapter for list view
ListAdapter routeAdapter = new CustomAdapter(this, routeListViewItems);
final ListView routeListView = (ListView) findViewById(R.id.routeListView);
routeListView.setAdapter(routeAdapter);
any help in how to modify the adapter and then call it in the main activity would be really appreciated. Thank you!
I would recommend creating a custom Route class since this adapter is meant to handle one array. Create some member variables for the route number and route items in the new class with getter and setter methods. Then you should be able to create a new array list of Route objects in your main activity and iterate through the existing string arrays while appending them (as new Route objects) to the new array.
You will have to change the adapter to accept a Route object instead of CharSequence. Hope this points you in the right direction.
I would recommend a custom adapter for you.
Exm..
Create Route Class
class Route{
public int number;
public String text;
}
Base Adapter...
public class myBaseAdapter extends BaseAdapter {
private LayoutInflater mInflater;
private List<Route> mRouteList;
public BilgiAdapter(Activity activity,List<Route> routeList){
mInflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mRouteList = routeList;
}
#Override
public int getCount() {
return mRouteList.size();
}
#Override
public Object getItem(int position) {
return mRouteList.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View myView;
myView = mInflater.inflate(R.layout.line_layout,null);
Route r = mRouteList.get(position);
TextView txtRouteNumber = (TextView)myView.findViewById(R.id.textRouteNumber);
TextView txtRouteText = (TextView)myView.findViewById(R.id.textRouteText);
txtRouteNumber.setText(String.ValueOf(r.number));
txtRouteText.setText(String.ValueOf(r.text));
return myView;
}
}
MainActivity vs..
ListView lstRoute;
myBaseAdapter adapter;
List<Route> list;
...
..
..
..
..
OnCreate(..)
..
list = new ArrayList<Route>();
//add routes in list
myBaseAdapter = new myBaseAdapter(MainActivity.this,list);
lstRoute = (ListView)findViewById(R.id.listViewRoute);
lstRoute.setAdapter(myBaseAdapter);
...
..
..
line_layout.xml(Layout file)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/textRouteNumber"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/textRouteText"/>
</LinearLayout>

Android - Load string array values to custom list view adapter

I have created a custom list view adapter and items for the list view is stored at String.xml. When I run the app I only shows a blank activity.
Please help me to fix this issue.
This is my custom adapter java class.
public class UserAdapter extends ArrayAdapter<User> {
public UserAdapter(Context context, ArrayList<User> users, String[] number) {
super(context, 0, users);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
User user = getItem(position);
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.listview_singlerow, parent, false);
}
TextView tvName = (TextView) convertView.findViewById(R.id.org_name);
TextView tvNum = (TextView) convertView.findViewById(R.id.cn_num);
// Populate the data into the template view using the data object
tvName.setText(user.company_name);
tvNum.setText(user.contact_num);
return convertView;
}}
List View model class.
public class User {
public String company_name;
public Integer contact_num;
public User(String company_name, Integer contact_num) {
this.company_name = company_name;
this.contact_num = contact_num;
}}
Main Activity class where I attach adapter to the view.
public class activity_one extends Activity {
String[] number;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity1);
number = getResources().getStringArray(R.array.dummy_data);
ArrayList<User> arrayOfUsers = new ArrayList<User>();
UserAdapter adapter = new UserAdapter(this, arrayOfUsers,number);
ListView listView = (ListView) findViewById(R.id.listView2);
listView.setAdapter(adapter);
}}
Since i am not 100% sure on what you want to do, ill show you a piece of my code and you can deduce from that what you need.
My DrawerListAdapter is an custom ListView adapter, and every item on that list consists of 3 objects - a String name, String imageurl, String description.
public class DrawerListAdapter extends ArrayAdapter<String> {
private final static int settingsCount = 4; // can ignore this
private Activity context;
// Next 3 lines: All data required for this adapter
private static ArrayList<String> itemname = null;
private static ArrayList<String> imgid;
private static ArrayList<String> itemDescription;
public DrawerListAdapter(Activity context, ArrayList<String> itemname, ArrayList<String> itemDescription, ArrayList<String> imgid) {
// R.layout.drawer_list_item is a layout that represents ONE item
// (not 100% sure, but i think the super class inflates this view itemname.size() times)
super(context, R.layout.drawer_list_item, itemname);
this.context=context;
// saving the required data
DrawerListAdapter.itemDescription = itemDescription;
DrawerListAdapter.itemname=itemname;
DrawerListAdapter.imgid=imgid;
}
#Override
public View getView(int position,View view,ViewGroup parent) {
LayoutInflater inflater=context.getLayoutInflater();
View rowView=inflater.inflate(R.layout.drawer_list_item, null,true);
// the views that are present in R.layout.drawer_list_item
TextView txtTitle = (TextView) rowView.findViewById(R.id.tvName);
ImageView imageView = (ImageView) rowView.findViewById(R.id.icon);
TextView extratxt = (TextView) rowView.findViewById(R.id.tvDescription);
txtTitle.setText(itemname.get(position));
extratxt.setText(itemDescription.get(position));
// setting the image here of the list item here
if (position < settingsCount) {
imageView.setImageResource(Integer.parseInt(imgid.get(position)));
} else {
Glide.with(context).load(imgid.get(position)).into(imageView);
}
return rowView;
};
// need to override the getCount. this function just returns the amount of items in your list
#Override
public int getCount(){
if (itemname != null)
return itemname.size();
return 0;
}
This is initialized by:
private void init() {
mDrawerList = (ListView) findViewById(R.id.left_drawer);
if (adapter == null) {
ArrayList<String> itemname = new ArrayList<String>();
itemname.add("item1");
itemname.add("item2");
itemname.add("item3");
itemname.add("item4");
ArrayList<String> imgid = new ArrayList<String>();
imgid.add(R.drawable.pic1 + "");
imgid.add(R.drawable.pic2 + "");
imgid.add(R.drawable.pic4 + "");
imgid.add(R.drawable.pic3 + "");
ArrayList<String> itemDescription = new ArrayList<String>();
itemDescription.add("desc1");
itemDescription.add("desc2");
itemDescription.add("desc3");
itemDescription.add("desc4");
adapter=new DrawerListAdapter(this, itemname, itemDescription, imgid);
setOnItemClickListener(); // sets onClick for each item on the list
}
mDrawerList.setAdapter(adapter);
}
and the drawer_list_item.xml just in case you are intrested:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<ImageView
android:id="#+id/icon"
android:layout_width="60dp"
android:layout_height="60dp"
android:padding="5dp" />
<LinearLayout android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/tvName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="5dp"
android:padding="2dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="#429bd9" />
<TextView
android:id="#+id/tvDescription"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:maxLines="2"
android:textColor="#79e5a4" />
</LinearLayout>

Android Studio itemsAdapter and custom ListView not working

I am in Android Studio trying to implement a custom ListView. I have created an xml file called "custom_layout_rachel.xml" and have put it in my "layout" folder. This file contains the code for how I want my ListView to look.
I am trying to make the list view in my page called "activity_urgent__important.xml" to look like the one in "custom_layout_rachel.xml". In this file, I have the following code:
<ListView
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:id="#+id/lvItems"
tools:listitem="#layout/custom_layout_rachel"
/>
In Android Studio, the custom layout is showing up, but when I run the app on my emulator, it is not there.
The java code for this activity, looks like:
lvItems = (ListView) findViewById(R.id.lvItems);
items = new ArrayList<String>();
itemsAdapter = new ArrayAdapter<String>(this, android.R.layout.custom_layout_rachel, items);
lvItems.setAdapter(itemsAdapter);
on the third line is where my error is.
Does anyone know why I can't do this or why I am getting an error?
Thank you!!!
New:
lvItems = (ListView) findViewById(R.id.lvItems);
items = new ArrayList<String>();
readItems();
itemsAdapter = new CustomListAdapter(this, items);
lvItems.setAdapter(itemsAdapter);
Getting an error on "Custom List Adapter(this, items)
I do not have adapter code, but I did start the following, I could implement it if it would work:
public class CustomListAdapter extends ArrayAdapter {
private Context mContext;
private int id;
private List<String> items ;
public CustomListAdapter(Context context, int textViewResourceId , List<String> list )
{
super(context, textViewResourceId, list);
mContext = context;
id = textViewResourceId;
items = list ;
}
public CustomListAdapter(Context context , List<String> list) {
super(context, items);
}
#Override
public View getView(int position, View v, ViewGroup parent)
{
View mView = v ;
if(mView == null){
LayoutInflater vi = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mView = vi.inflate(id, null);
}
TextView text = (TextView) mView.findViewById(R.id.textView);
if(items.get(position) != null )
{
text.setTextColor(Color.WHITE);
text.setText(items.get(position));
text.setBackgroundColor(Color.RED);
int color = Color.argb( 200, 255, 64, 64 );
text.setBackgroundColor( color );
}
return mView;
}
In your second CustomListAdapter constructor, initialize mContext and items since mContext will be used in inflating the view in getView() method.
public CustomListAdapter(Context context , List<String> list) {
super(context, items);
mContext = context;
items = list ;
}
If you are creating your own adpater extending String type. You don't have to pass android.R.layout.custom_layout_rachel in your third line of code.
You will be inflating your custom_layout for listview inside getView method ,within adapter.
Simply pass the context and values needed to be populated in Listview.
new ArrayAdapter<String>(this,items);
Change your adapter constructor to the same.
if it doesn't work please post the adapter code.
Update your code like this.
public class CustomListAdapter extends ArrayAdapter<String> {
private Context mContext;
private int id;
private List<String> items ;
private LayoutInflater inflater;
public CustomListAdapter(Context context,List<String> list )
{ super(context,list);
this.mContext = context;
this.items = list ;
this.inflater=LayoutInflater.from(context)
}
public int getCount()
{
items.length;
(or)
items.size();
}
#Override
public View getView(int position, View v, ViewGroup parent)
{
if(v== null){
v = this.inflater.inflate(R.layout.custom_layout_rachel, null);
}
TextView text = (TextView) v.findViewById(R.id.textView);
if(items.get(position) != null )
{
text.setTextColor(Color.WHITE);
text.setText(items.get(position));
text.setBackgroundColor(Color.RED);
int color = Color.argb( 200, 255, 64, 64 );
text.setBackgroundColor( color );
}
return v;
}
changes in your code
lvItems = (ListView) findViewById(R.id.lvItems);
items = new ArrayList<String>();
readItems();
itemsAdapter = new CustomListAdapter<String>(this, items);
lvItems.setAdapter(itemsAdapter);

How can I use a view from different class?

I've searched a lot this but I really did not find what I need exactly.
What I want is :
I have a listview and load it on my main class. I load it like this:
ArrayAdapter<String> adapter = new ArrayAdapter<String>(Main.this,
R.layout.myrow,R.id.text );
if (friends != null) {
for (ParseObject friend : friends) {
adapter.add((String) friend.get("name"));
}
}
setListAdapter(adapter);
and myrow layout xml is here:
< RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center" >
<ProgressBar
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/my_prog"
></ProgressBar>
<TextView
android:id="#+id/text"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="25sp" >
</TextView>
</RelativeLayout >
You can see I have progress bar on textview so I need to reach it and set it's visible. I mean sometimes I set it invisible sometimes visible
What I did is:
On my MainActivity on create method the code is here :
ProgressBar my_prog;
my_prog=(ProgressBar)findViewById(R.id.my_prog);
my_prog.setVisibility(View.INVISIBLE);
The error is:
07-16 13:35:39.219: E/AndroidRuntime(29096): java.lang.RuntimeException: Unable to start activity ComponentInfo{....MainActivity}: java.lang.NullPointerException
I think I cant reach my_prog on my main activity but I need it.
How can I do that.
thanks in adnvace...
You need to implement a custom Adapter to do this. I have written this ExampleAdapter to show you how it works, I commented all the important parts:
public class ExampleAdapter extends BaseAdapter {
private final LayoutInflater inflater;
private final List<ParseObject> objects;
private final boolean[] activated;
public ExampleAdapter(Context context, List<ParseObject> objects) {
this.inflater = LayoutInflater.from(context);
this.objects = objects;
this.activated = new boolean[objects.size()];
}
public void showProgressBar(int position, boolean visible) {
this.activated[position] = visible;
notifyDataSetChanged();
}
#Override
public int getCount() {
return this.objects.size();
}
#Override
public ParseObject getItem(int position) {
return this.objects.get(position);
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// If the convertView is null, we need to inflate a new one
if(convertView == null) {
// We inflate the view with your layout
convertView = inflater.inflate(R.layout.myrow, parent, false);
// Here we create a view holder object which keeps a
// reference to the Views in this row so we have to
// perform the expensive findViewById() only once
ViewHolder holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.text);
holder.my_prog = (ProgressBar) convertView.findViewById(R.id.my_prog);
// The view holder is set as tag to the view so we can access it later
convertView.setTag(holder);
}
// We retrieve the ParseObject for the current position
ParseObject parseObject = getItem(position);
// And get the view holder from the View
ViewHolder holder = (ViewHolder) convertView.getTag();
// We read the data we need from the ParseObject
String name = parseObject.get("name");
// And here is the visibility logic
int progressBarVisibility = this.activated[position] ? View.VISIBLE : View.GONE;
int textViewVisibility = this.activated[position] ? View.GONE : View.VISIBLE;
// Now we set the data to the Views through the view holder
holder.text.setText(name);
holder.text.setVisibility(textViewVisibility);
holder.my_prog.setVisibility(progressBarVisibility);
return convertView;
}
// This is our view holder class. It keeps a reference to the Views
// inside each row so we have to perform the expensive
// findViewById() only once
private class ViewHolder {
public TextView text;
public ProgressBar my_prog;
}
}
You can use the ExampleAdapter like this:
if(friends != null) {
ExampleAdapter adapter = new ExampleAdapter(Main.this, friends);
setListAdapter(adapter);
}
If you want to show a ProgressBar at a specific position you just do this:
// Shows the ProgressBar in the first row
adapter.showProgressBar(0, true)

Categories

Resources