onClick method for Buttons in ExpandableListView - android

My ExpandableListView has some EditText boxes and Buttons(It's a sign-in/register screen).
I want to use an onClick function with a switch case to select the button that has been clicked and do something with them.
For the Buttons, I can't set the onClickListener.
All this is in the Custom Expandable List Adapter Class than I wrote.
I know there is another way to address a normal function in a Custom Expandable List Adapter but I don't know how.
Here's the relevant snippets of code WITHIN CustomExpandableAdapter class :
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
v = null;
position = 0;
position=getChildId(groupPosition, childPosition);
* - - unrelated code - *
if(position==2) //Draws Forgot Button
{
v = inflater.inflate(R.layout.forgot, parent, false);
View forgot = v.findViewById(R.id.fpb);
//What goes here to setOnClickListener?
}
return v;
}
public void onClick(View w) {
PopupWindow pw= new PopupWindow(inflater.inflate(R.layout.popup_layout, null, false),220,160, true);;
switch(w.getId())
{
case R.id.fpb:
{
pw.showAtLocation(v, Gravity.CENTER, 0, 0);
}
}
Can someone pleases help out??

I hope you have implemented OnClickListner in your adapter. If yes, then what error were you getting when using forgot.setOnClickListener(this);

Alrighty...you need to set OnClickListener not on the view, but on the specific buttons. So-you need to findViewByID(find the EditBox, Buttons or whatever) IN that View forgot and then set onClickListener ON them.
Such as...
Button b = forgot.findViewByID("myButton");
b.setOnClickListener(new OnClickListener {
etc...
Be sure to use the right OnClickListener, there are two in the API!:)

A better solution might exist, but here's what I do:
1. Interface
import android.view.View;
public interface RecyclerViewClickListener {
public void recyclerViewListClicked(View v);
}
2. In your Activity/Fragment
public class MyFragment(or Activity) Fragment implements RecyclerViewClickListener {
It will show an error that methods are not implemented. Add them
#Override public void recyclerViewListClicked(View v) {
YourObject mObject = (YourObject) v.getTag();
Log.i("My Object with >>> ", String.valueOf(mObject.getId()));
}
3. In your Adapter in the consturctor add at the end
public ExpandListViewAdapter(FragmentActivity context, etc. etc., RecyclerViewClickListener clickListener)
4. In your Adapter in getChildView()
button1.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View v) {
v.setTag(YourObject);
clickListener.recyclerViewListClicked(v);
}
});

Related

OnItemClickListener for multiple AutoCompleteTextView with switch case not working

I have multiple AutoCompleteTextView in layout, so i've implemented AdapterView.OnItemClickListener globally.
Now issue is that,
i can't compare AutoCompleteTextView using switch case inside listener,
Code
#Override
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
switch (v.getId()) {
case R.id.ac_education:
s_ac_education = testArray[position];
break;
case R.id.ac_ethincity:
s_ac_ethincity = testArray[position];
break;
case R.id.ac_languages:
s_ac_languages = testArray[position];
break;
case R.id.ac_location:
s_ac_location = testArray[position];
break;
case R.id.ac_religion:
s_ac_religion = testArray[position];
break;
case R.id.ac_travel:
s_ac_travel = testArray[position];
break;
}
}
Note: view.getId() always returning same value,
i've also tried if-else but that doesn't work also.
(I've already wasted couple of hours)
EDIT
private void init(View v) {
ac_languages = v.findViewById(R.id.ac_languages);
ac_religion = v.findViewById(R.id.ac_religion);
ac_location = v.findViewById(R.id.ac_location);
ac_travel = v.findViewById(R.id.ac_travel);
ac_ethincity = v.findViewById(R.id.ac_ethincity);
ac_education = v.findViewById(R.id.ac_education);
ptr.setACTVAdapter(getActivity(), ac_education, ac_ethincity, ac_languages, ac_location, ac_religion, ac_travel);
ac_languages.setHint(R.string.languages);
ac_religion.setOnItemClickListener(this);
ac_religion.setHint(R.string.religion);
ac_location.setOnItemClickListener(this);
ac_location.setHint(R.string.where_do_you_live);
ac_travel.setOnItemClickListener(this);
ac_travel.setHint(R.string.where_can_you_travel);
ac_ethincity.setOnItemClickListener(this);
ac_ethincity.setHint(R.string.ethincity);
ac_education.setOnItemClickListener(this);
ac_education.setHint(R.string.education);
}
setAdapter(is defined in another class)
public void setACTVAdapter(Activity activity, AutoCompleteTextView... actv) {
testArray = activity.getResources().getStringArray(R.array.testArray);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(activity, android.R.layout.simple_list_item_1, testArray);
for (AutoCompleteTextView ac : actv) {
ac.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
}
Thanks
First, create your custom onItemClickListener:
public class MyClickListener implements AdapterView.OnItemClickListener {
AutoCompleteTextView ac;
public MyClickListener(AutoCompleteTextView myAc){
ac = myAc;
}
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
switch(ac.getId()){
case R.id.autocomplete_1:
//your code
break;
case R.id.autocomplete_2:
//your code
break;
}
}
}
Use it for your AutoCompleteTextView:
autoCompleteTextView.setOnItemClickListener(new MyClickListener(autoCompleteTextView));
You should not rely on view when you're working with a list, specially when you're setting a general item click listener.
Instead get an instance to each view by their id on public View getView(int position, View convertView, ViewGroup parent) method of your AutoCompleteTextView adapter and declare a click listener to work with the position you got there, if you want to set multiple click listeners on a row individually.
For example:
public class ExampleAdapter extends BaseAdapter {
...
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView.findViewById(R.id.ac_education);
v.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Your code here
}
});
}
...
}
According to official doc for AdapterView.OnItemClickListener,
onItemClick() method have following parameters,
parent: The AdapterView where the click happened.
view: The view within the AdapterView that was clicked (this will be a
view provided by the adapter)
position: The position of the view in the adapter.
id: The row id of the item that was clicked.
So, you might want to use parent instead of view, to receive id of that parent AutoCompleteTextView.
(Check this answer to get more details.)
It's not the child that you want to track it's clicks, it's convertView.
So, if you really want to handle child clicks outside of your adapter, you can do like this:
public interface OnViewClickListener {
void onViewClick(View view, int position);
}
Then inside of your adapter:
private OnViewClickListener mListener;
public void setOnViewClickListener(final OnViewClickListener listener) {
mListener = listener;
}
And for every child you want:
child.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(final View v) {
if (mListener != null) {
mListener.onViewClick(v, position);
}
}
});
And with this you can get item clicks:
adapter.setOnViewClickListener(new OnViewClickListener() {
#Override
public void onViewClick(final View view, final int position) {
** PUT YOUR SWITCH CASE HERE! **
}
});
First setup data into Pojo class using array list. And return as string for autotextview. Match that string with arraylist's item value using for loop.
autoPartyName.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
for (CustomersModel list : customersList) {
if(list.toString().equals(adapterView.getItemAtPosition(position))){
etContactPhone.setText(list.getContact());
etContactAddress.setText(list.getAddress());
}
}
}
});

Android ListView check which column touched

I was wondering if someone could help me with something as I can't seem to find any documented way to do it, how do you get the view of a column in a list view?
So what I have is a list view, with a custom adapter which has three controls, two textviews and a imageview, what I want to happen is when you press the last column(the image) I want to be able to delete that row, I could do it using the entire row, but I want the user to have to click the imageview column in order to trigger delete.
How do I do this? what do I have to reference to within my OnClickListener in order to check the selected the imageview column?
Thank you for reading
lstNotes.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
}
});
position just references the position of the list row and doesn't allow you to specify a column index or something similar
You can't detect clicks using lstNotes.setOnItemClickListener - that's purely for item clicks. Instead inside your list view's adapter you should set click listeners for each view item.
You could then use an interface to let your activity know. Something like the following would work:
public interface OnViewClickListener{
void onTextView1Click();
void onTextView2Click();
void onImageViewClick();
}
OnViewClickListener listener;
public CustomAdapter(Context context, int layout, ArrayList<Object> list, OnViewClickListener listener) {
this.listener = listener;
super(context, layout, list);
}
...
public View getView(int position, View convertView, ViewGroup viewGroup) {
...
textView1.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View arg0) {
listener.onTextView1Click();
}});
textView2.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View arg0) {
listener.onTextView2Click();
}});
imageView.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View arg0) {
listener.onImageViewClick();
}});
}

Hide/unhide layout in list item

I have a layout for list item, which consists of two LinearLayouts. What I want to achieve is: when item is clicked, second LinearLayout should become visible/gone, depending on the current visibility.
I am experimenting with this code:
getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
view.getViewById(R.id.id_of_the_second_linear_layout).setVisibility(View.GONE);
}
});
However when item is clicked, several other linear layouts (in different items) become visible/hidden. Why?
Update:
Adapter:
public class ExpensesCursorAdapter extends SimpleCursorAdapter implements SimpleCursorAdapter.ViewBinder {
public ExpensesCursorAdapter(Context context, Cursor cursor) {
super(context, R.layout.single_expense, cursor,
new String[]{
ExpenseContract._AMOUNT,
CategoryContract._NAME,
ExpenseContract._DATE
},
new int[]{
R.id.expense_amount,
R.id.expense_category,
R.id.expense_date
},
CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
setViewBinder(this);
}
public View getView(int position, View convertView, ViewGroup viewGroup) {
View v = super.getView(position, convertView, viewGroup);
final View expandablePanel = v.findViewById(R.id.expandable_panel);
expandablePanel.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
expandablePanel.setVisibility(view.getVisibility() == View.GONE ? View.VISIBLE : View.GONE);
}
});
return v;
}
#Override
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
if (columnIndex == cursor.getColumnIndex(ExpenseContract._AMOUNT)) {
return handleAmountView((TextView) view, cursor);
}
else ...
return false;
}
private boolean handleAmountView(TextView view, Cursor cursor) {
TextView textView = (TextView) view;
Double amount = ExpenseDbHelper.getAmount(cursor);
String formattedAmount = new DecimalFormat("##.00").format(amount);
textView.setText(formattedAmount);
return true;
}
}
Each item has LinearLayout already added in XML, I want to toggle visibility flag, if possible.
You are writing your logic on wrong places. You want to listen clicks of views inside listitem. Write your logic in Adapter's getView method. In your getView logic can be like this
ll1.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
ll2.setVisibility(View.GONE);
}
});
ll2.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
ll1.setVisibility(View.GONE);
}
});
Something like this.
When you scroll through the list items, some layouts will hide and un-hide, in that case, if you are targeting just one view to be visible at a time (that is just considering one cell at a time), then you could maintain the position of the item clicked, or the id, since you are using a cursorAdapter. Else if you are considering more than one cell then maintain a list where in you store each id of the cell that has been tapped on.
Pass the list or the single position value to the adpater, and in the getview compare the id or position and then perform the visiblity code.
Hope this hint helps.

Propagate button onclick of custom arrayadapter in my activity

My activity has a ListView that has a custom ArrayAdapter.
On my ArrayAdapter i have an image, a bunch of textboxes and a button.
On the getView of the adapter i get my button and set setOnClickListener. From the click listener i can get the index of the clicked item.
Now my problem is that i want to propagate that information to my main activity, where i want to handle my button click.
I can save the index information in a static var, but i still don't know how to fire an event in my activity.
How do i do that?
I'm 6 days new to Android so, thanks
iggy
Code:
My Activity:
public class MyClass extends Activity{
...
public void onCreate(Bundle savedInstanceState) {
...
myListView = (ListView)findViewById(R.id.lvxml);
myList = new MyCustomArrayAdapter(this, myAnotherClassObject);
myListView .setAdapter(myList);
...
}
}
Now in my Adapter
public class MyCustomArrayAdapter extends ArrayAdapter<myAnotherClass> {
...
....
#Override
public View getView(int position, View convertView, ViewGroup parent) {
Button b = (Button)convertView.findViewById(R.id.myButtonInListView);
b.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
doStuff;
}
});
}
}
I need to somehow fire a buttonclick from my main activity, but without loosing the possibility to read the index clicked in the list view.
Here's an alternative and, what I think to be, more elegant solution.
Firstly, in your MyCustomArrayAdapter class, define an interface:
public interface MyCustomRowButtonListener{
void onCustomRowButtonClick(MyAnotherClass selectedItem, int position, View view);
}
Create an member variable of MyCustomRowButtonListener in your ArrayAdapter
public class MyCustomArrayAdapter{
private MyCustomRowButtonListener mRowButtonListener;
//....
}
and add a parameter for the listener in the constructor
public MyCustomArrayAdapter(Context context, MyCustomRowButtonListener listener){
mContext = context;
mRowButtonListener = listener;
}
in the getView method:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
Button b = (Button)convertView.findViewById(R.id.myButtonInListView);
b.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mRowButtonListener.onCustomRowButtonClick(getItem(position),position,b);
}
});
}
and in your activity:
myList = new MyCustomArrayAdapter(this, myAnotherClassObject,this);
Now let your activity implement MyCustomRowButtonListener
public class MyClass extends Activity implements MyCustomRowButtonListener{
...
public void onCreate(Bundle savedInstanceState) {
...
myListView = (ListView)findViewById(R.id.lvxml);
myList = new MyCustomArrayAdapter(this, myAnotherClassObject,this);
myListView .setAdapter(myList);
...
}
}
public void onCustomRowButtonClick(MyAnotherClass selectedItem, int position, View view){
Toast.makeText(this,"You have selected "+selectedItem,Toast.LENGTH_SHORT).show();
}
So I've solved it somehow. It's probably not the way to do it but it works.
I save my activity in a static var.
Button b = (Button)convertView.findViewById(R.id.myButtonInListView);
b.setTag(position);
b.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
int i = (Integer) v.getTag();
myStaticVarRep.myActivity.myMethod(i);
}
});
The index ist the position variable.
Ist has to be final to use it here
public View getView(final int position, View convertView, ViewGroup parent)
You can inform your Activity with a listener-interface.
I'm going to assume your code looks a little like this.
public class <Your Class> ... implements OnClickListener{
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
.
.
.
button.setOnClickListener(this);
}
}
To handle button click events, you need to have an onClick method that looks like this.
public void onClick(View v){
<Handle your button click in here>
}
Whenever you click your button, it'll invoke the onClick method. That is where you would manipulate your static var, fire off other methods, etc. I hope that helps.

Android: ListView elements with multiple clickable buttons

I've a ListView where every element in the list contains a TextView and two different Buttons. Something like this:
ListView
--------------------
[Text]
[Button 1][Button 2]
--------------------
[Text]
[Button 1][Button 2]
--------------------
... (and so on) ...
With this code I can create an OnItemClickListener for the whole item:
listView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> list, View view, int position, long id) {
Log.i(TAG, "onListItemClick: " + position);
}
}
});
However, I don't want the whole item to be clickable, but only the two buttons of each list element.
So my question is, how do I implement a onClickListener for these two buttons with the following parameters:
int button (which button of the element has been clicked)
int position (which is the element in the list on which the button click happened)
Update: I found a solution as described in my answer below. Now I can click/tap the button via the touch screen. However, I can't manually select it with the trackball. It always selects the whole list item and from there goes directly to the next list item ignoring the buttons, even though I set .setFocusable(true) and setClickable(true) for the buttons in getView().
I also added this code to my custom list adapter:
#Override
public boolean areAllItemsEnabled() {
return false;
}
#Override
public boolean isEnabled(int position) {
return false;
}
This causes that no list item is selectable at all any more. But it didn't help in making the nested buttons selectable.
Anyone an idea?
The solution to this is actually easier than I thought. You can simply add in your custom adapter's getView() method a setOnClickListener() for the buttons you're using.
Any data associated with the button has to be added with myButton.setTag() in the getView() and can be accessed in the onClickListener via view.getTag()
I posted a detailed solution on my blog as a tutorial.
This is sort of an appendage #znq's answer...
There are many cases where you want to know the row position for a clicked item AND you want to know which view in the row was tapped. This is going to be a lot more important in tablet UIs.
You can do this with the following custom adapter:
private static class CustomCursorAdapter extends CursorAdapter {
protected ListView mListView;
protected static class RowViewHolder {
public TextView mTitle;
public TextView mText;
}
public CustomCursorAdapter(Activity activity) {
super();
mListView = activity.getListView();
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
// do what you need to do
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View view = View.inflate(context, R.layout.row_layout, null);
RowViewHolder holder = new RowViewHolder();
holder.mTitle = (TextView) view.findViewById(R.id.Title);
holder.mText = (TextView) view.findViewById(R.id.Text);
holder.mTitle.setOnClickListener(mOnTitleClickListener);
holder.mText.setOnClickListener(mOnTextClickListener);
view.setTag(holder);
return view;
}
private OnClickListener mOnTitleClickListener = new OnClickListener() {
#Override
public void onClick(View v) {
final int position = mListView.getPositionForView((View) v.getParent());
Log.v(TAG, "Title clicked, row %d", position);
}
};
private OnClickListener mOnTextClickListener = new OnClickListener() {
#Override
public void onClick(View v) {
final int position = mListView.getPositionForView((View) v.getParent());
Log.v(TAG, "Text clicked, row %d", position);
}
};
}
For future readers:
To select manually the buttons with the trackball use:
myListView.setItemsCanFocus(true);
And to disable the focus on the whole list items:
myListView.setFocusable(false);
myListView.setFocusableInTouchMode(false);
myListView.setClickable(false);
It works fine for me, I can click on buttons with touchscreen and also alows focus an click using keypad
I don't have much experience than above users but I faced this same issue and I Solved this with below Solution
<Button
android:id="#+id/btnRemove"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_toRightOf="#+id/btnEdit"
android:layout_weight="1"
android:background="#drawable/btn"
android:text="#string/remove"
android:onClick="btnRemoveClick"
/>
btnRemoveClick Click event
public void btnRemoveClick(View v)
{
final int position = listviewItem.getPositionForView((View) v.getParent());
listItem.remove(position);
ItemAdapter.notifyDataSetChanged();
}
Probably you've found how to do it, but you can call
ListView.setItemsCanFocus(true)
and now your buttons will catch focus
I am not sure about be the best way, but works fine and all code stays in your ArrayAdapter.
package br.com.fontolan.pessoas.arrayadapter;
import java.util.List;
import android.content.Context;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import br.com.fontolan.pessoas.R;
import br.com.fontolan.pessoas.model.Telefone;
public class TelefoneArrayAdapter extends ArrayAdapter<Telefone> {
private TelefoneArrayAdapter telefoneArrayAdapter = null;
private Context context;
private EditText tipoEditText = null;
private EditText telefoneEditText = null;
private ImageView deleteImageView = null;
public TelefoneArrayAdapter(Context context, List<Telefone> values) {
super(context, R.layout.telefone_form, values);
this.telefoneArrayAdapter = this;
this.context = context;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.telefone_form, parent, false);
tipoEditText = (EditText) view.findViewById(R.id.telefone_form_tipo);
telefoneEditText = (EditText) view.findViewById(R.id.telefone_form_telefone);
deleteImageView = (ImageView) view.findViewById(R.id.telefone_form_delete_image);
final int i = position;
final Telefone telefone = this.getItem(position);
tipoEditText.setText(telefone.getTipo());
telefoneEditText.setText(telefone.getTelefone());
TextWatcher tipoTextWatcher = new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void afterTextChanged(Editable s) {
telefoneArrayAdapter.getItem(i).setTipo(s.toString());
telefoneArrayAdapter.getItem(i).setIsDirty(true);
}
};
TextWatcher telefoneTextWatcher = new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void afterTextChanged(Editable s) {
telefoneArrayAdapter.getItem(i).setTelefone(s.toString());
telefoneArrayAdapter.getItem(i).setIsDirty(true);
}
};
tipoEditText.addTextChangedListener(tipoTextWatcher);
telefoneEditText.addTextChangedListener(telefoneTextWatcher);
deleteImageView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
telefoneArrayAdapter.remove(telefone);
}
});
return view;
}
}
I Know it's late but this may help, this is an example how I write custom adapter class for different click actions
public class CustomAdapter extends BaseAdapter {
TextView title;
Button button1,button2;
public long getItemId(int position) {
return position;
}
public int getCount() {
return mAlBasicItemsnav.size(); // size of your list array
}
public Object getItem(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = getLayoutInflater().inflate(R.layout.listnavsub_layout, null, false); // use sublayout which you want to inflate in your each list item
}
title = (TextView) convertView.findViewById(R.id.textViewnav); // see you have to find id by using convertView.findViewById
title.setText(mAlBasicItemsnav.get(position));
button1=(Button) convertView.findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//your click action
// if you have different click action at different positions then
if(position==0)
{
//click action of 1st list item on button click
}
if(position==1)
{
//click action of 2st list item on button click
}
});
// similarly for button 2
button2=(Button) convertView.findViewById(R.id.button2);
button2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//your click action
});
return convertView;
}
}
Isn't the platform solution for this implementation to use a context menu that shows on a long press?
Is the question author aware of context menus? Stacking up buttons in a listview has performance implications, will clutter your UI and violate the recommended UI design for the platform.
On the flipside; context menus - by nature of not having a passive representation - are not obvious to the end user. Consider documenting the behaviour?
This guide should give you a good start.
http://www.mikeplate.com/2010/01/21/show-a-context-menu-for-long-clicks-in-an-android-listview/

Categories

Resources