Android databinding: Layout to the view - android

I am programming an android-App and I am using the MVVM-Pattern. I have problems seeing the data in the layout in my view. I am new to android programming, so maybe i am thinking the wrong way. In C# i had no problems creating an MVVMC-App. To be honest, i do not understand how to show the contents in the layouts in the activity :-(
This is my code in the view:
#Override
protected void onCreate(Bundle savedInstanceState) {
//This is the code which shows the white window with my data
super.onCreate(savedInstanceState);
ContentSelectItemBinding binding = DataBindingUtil.setContentView(this, R.layout.content_select_item);
this.aSelectItemViewModel = new SelectItemViewModel();
binding.setItem(this.aSelectItemViewModel.item);
//when i add this, it will just show the empty view
setContentView(R.layout.activity_select_item);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
}
And thats how my easy SelectItemViewModel looks like
public TestItem item;
public SelectItemViewModel() {
item = new TestItem("Test", "User");
}
public List<TestItem> getItemList() {
return this.aTestItemList;
}
And here are my XML-Files:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="private.testapp.view.SelectItemActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="#style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<include layout="#layout/content_select_item" />
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="#dimen/fab_margin"
app:srcCompat="#android:drawable/ic_dialog_email" />
</android.support.design.widget.CoordinatorLayout>
This is my layout file
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="item" type="private.testapp.model.TestItem"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:layout_width="wrap_content"
android:textSize="128sp"
android:layout_height="wrap_content"
android:text="#{item.name}"/>
<TextView android:layout_width="wrap_content"
android:textSize="128sp"
android:layout_height="wrap_content"
android:text="#{item.description}"/>
</LinearLayout>
</layout>
All i want to do first is just display the testitem... Since it displays it when i just show the layout, i am sure that the binding and my viewmodel works.
I don`t understand how the data in the layout will be shown in my view.

Option 1- forward variable from activity xml to included xml:
First,
ContentSelectItemBinding binding = DataBindingUtil.setContentView(this,R.layout.content_select_item);
this.aSelectItemViewModel = new SelectItemViewModel();
binding.setItem(this.aSelectItemViewModel.item);
//when i add this, it will just show the empty view
setContentView(R.layout.activity_select_item);
You're setting your activity content twice which is wrong, replace it with just:
DataBindingUtil.setContentView(this,R.layout.activity_select_item);
this.aSelectItemViewModel = new SelectItemViewModel();
binding.setItem(this.aSelectItemViewModel.item);
To set your activity xml as your layout.
Next, your activity_select_item xml should also be wrapped with a <layout> tag and a <data> tag inside to denote this is a data binding layout:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="item" type="private.testapp.model.TestItem"/>
</data>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="private.testapp.view.SelectItemActivity">
.
.
.
</layout>
Finally, forward the variable to your included layout inside the <include> tag:
.
.
.
<include
layout="#layout/content_select_item"
app:item="#{item}"/>
.
.
.
</android.support.design.widget.CoordinatorLayout>
Option 2- find included view and bind it
First, mark included view with id:
<include
id="#+id/content"
layout="#layout/content_select_item" />
Next, find it in code and bind it:
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_select_item);
View content= findViewById(R.id.content);
ViewDataBinding binding = DataBindingUtil.bind(content);
Finally, set the variable on the generic binding using generated id inside BR class (like R class but for data binding variables):
this.aSelectItemViewModel = new SelectItemViewModel();
binding.setVariable(BR.item, aSelectItemViewModel.item);

Related

How to pass observable fields through include tag?

I have a layout for the toolbar spinner:
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="selectedItem"
type="int" />
</data>
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:popupTheme="#style/Theme.Paper.Toolbar.Popup"
app:theme="#style/Theme.Paper.Toolbar">
<Spinner
android:id="#+id/toolbar_spinner"
android:layout_width="wrap_content"
android:layout_height="match_parent"
app:theme="#style/Theme.Paper.Toolbar.Spinner"
app:popupTheme="#style/Theme.Paper.Toolbar.Spinner.Popup"
app:selectedItem="#{selectedItem}"/>
</androidx.appcompat.widget.Toolbar>
</layout>
I include that layout in fragment layouts like below:
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="model"
type="com.contedevel.timetable.models.WeekViewModel" />
</data>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.contedevel.timetable.activities.MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/appBar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<include
android:id="#+id/toolbar_layout"
layout="#layout/toolbar_spinner"
app:selectedItem="#{model.selectedWeekPosition}"/>
</com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
Here is my ViewModel:
class WeekViewModel(application: Application) : AndroidViewModel(application) {
val selectedWeekPosition = ObservableInt(0)
}
But selectedItem changes once on the start of activity only. When I try to change it in onOptionsItemSelected a nothing happens:
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
if (R.id.action_now == item?.itemId) {
week = WeekUtils.getCurrentWeek(timetable.entity)
viewModel.selectedWeekPosition.set(week - 1)
return true
}
return super.onOptionsItemSelected(item)
}
I suppose the value doesn't change due to the first layout (layout data binder) unwraps it to Int and the second layout binder gets Int instead of ObservableInt. But I still don't know how to fix it not to pass ObservableInt in the toolbar spinner binder directly.
Does anyone know how to fix it? Spinner binding adapter are below:
object SpinnerBindings {
#BindingAdapter("selectedItem")
#JvmStatic fun Spinner.setSelectedItem(position: Int) {
setSelection(position)
}
}
Pass your viewmodel to toolbar spinner layout.
Step 1: Add bind attribute to layout tag that contains include(fragment layouts) :
xmlns:bind="http://schemas.android.com/apk/res-auto"
Step 2: Change your data item in toolbar spinner layout:
<data>
<variable
name="vmodel"
type="com.contedevel.timetable.models.WeekViewModel" />
</data>
Step 3: Change your include tag like below:
<include
android:id="#+id/toolbar_layout"
layout="#layout/toolbar_spinner"
bind:vmodel="#{model}"/>
Step 4 : You have access to your viewModel in toolbar spinner layout, So:
<Spinner
android:id="#+id/toolbar_spinner"
android:layout_width="wrap_content"
android:layout_height="match_parent"
app:theme="#style/Theme.Paper.Toolbar.Spinner"
app:popupTheme="#style/Theme.Paper.Toolbar.Spinner.Popup"
app:selectedItem="#{vmodel.selectedWeekPosition}"/>

RecycleView item transition to fragment

I am trying to create image move transition from recycle view to fragment. But the problem is that after the transition image is displayed in the new the move animation doesn't happen it just fades in with the rest of the content. What could I be doing wrong. I was following this example:
http://mikescamell.com/shared-element-transitions-part-4-recyclerview/
I have recycle view item layout with image view:
<?xml ...?>
<layout ...>
<data>
<variable name="model" type="..."/>
</data>
<LinearLayout ...>
<FrameLayout ...>
<ImageView android:id="#+id/image" .../>
</FrameLayout>
<LinearLayout ...>
...
</LinearLayout>
</LinearLayout>
</layout>
And then I have a fragment layout to which I want to move the image from recycle view.
<FrameLayout ...>
<TextView .../>
<ImageView android:id="#+id/logo" >
</FrameLayout>
Here's recycle my view onBindViewHolder method
#Override
public void onBindViewHolder(RecycleViewAdapter.ViewHolder holder, int position) {
final T object = getItem(position);
holder.getBinding().setVariable(BR.model, object);
holder.getBinding().executePendingBindings();
ViewCompat.setTransitionName(holder.getBaseView().findViewById(R.id.logo), "wb_logo");
holder.getBaseView().setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
onItemClickListener.onItemClick(view, object);
}
});
}
Here's recycle view item click handler in my main activity
#Override
public void onFragmentItemClick(View view, Object object) {
PreviewFragment fragment = PreviewFragment.newInstance(Object.imageId, "");
getFragmentManager()
.beginTransaction()
.addSharedElement(view.getRootView().findViewById(R.id.logo), "wb_logo")
.addToBackStack(null)
.replace(R.id.fragment_container, fragment)
.commit();
}
In my fragment class in which I wan't to animate image I have following setup:
#Override
public void onCreate(Bundle savedInstanceState) {
...
setSharedElementEnterTransition(
TransitionInflater.from(getActivity()).inflateTransition(android.R.transition.move));
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ImageView imageView = view.findViewById(R.id.logo);
imageView.setTransitionName("wb_logo");
imageView.setImageResource(...);
}
I tried statically adding transitionName attribute to image views but the result was same.
Edit
Maybe there is something wrong with my general layout. My fragment container is in a DrawerLayout
<android.support.v4.widget.DrawerLayout ...>
<android.support.design.widget.CoordinatorLayout ...>
<android.support.design.widget.AppBarLayout ...>
<android.support.v7.widget.Toolbar ... />
</android.support.design.widget.AppBarLayout>
<FrameLayout android:id="#+id/fragment_container" .../>
</android.support.design.widget.CoordinatorLayout>
<android.support.design.widget.NavigationView .../>
</android.support.v4.widget.DrawerLayout>
Or fragment class I am using I am using
android.app.Fragment
instead of
android.support.v4.app.Fragment
within
android.support.v7.app.AppCompatActivity
I figured out what was my problem: I had multiple items in recycle view which all had ImageView inside them with android:transitionName="wb_logo".
I somehow missed the point that each shared item in recycle view must have a unique transitionName. when I modified transitionName to include id suffix it started working as intended.
android:transitionName="wb_logo_1"
android:transitionName="wb_logo_2"
Try setting the transition name in xml rather than JAVA side. I think it might not be working because you are setting it when the view is already created.
<ImageView
android:id="#+id/ivItem"
android:transitionName="wb_logo"
android:layout_width="match_parent"
android:background="#android:color/holo_blue_bright"
android:scaleType="centerCrop"
android:layout_height="wrap_content" />
The above would be in your item in your recycler view.
<ImageView
android:id="#+id/wb_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:scaleType="centerCrop"
android:transitionName="wb_logo" />
The above would then be in your fragment
Your start fragment code is correct

Android data binding not shown

I am trying to use data binding in my app, but nothing is shown.
This is what I did:
I created an New Project with an Empty Activity.
I added
dataBinding {
enabled = true
}
to my build.gradle
This is my activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="game" type="my.name.bar.Bar"/>
</data>
<android.support.constraint.ConstraintLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="my.name.bar.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{game.GetFoo()}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
</layout>
And this is my Bar class
public class Bar {
private String str;
public Bar() {
str = "Foo";
}
public String GetFoo() {
return str;
}
}
But I am only getting a white screen. If I replace the binding with just "Hello World!" then everything works fine. What am I doing wrong?
(promoted from the comment)
You must bind the value. In your case, it is something like this:
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.setGame(new Bar());

databinding button onclick not working

activity_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="android.view.View" />
<variable
name="callback"
type="com.buscom.ActionCallBack" />
</data>
<LinearLayout xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/ll_oml"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/grey_50"
android:orientation="vertical">
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="#{(v) -> callback.onClick(v)}"
android:text="Menu" />
</android.support.design.widget.CoordinatorLayout>
</LinearLayout>
</layout>
ActionCallBack.java
This is the interface I implement in MainActivity
public interface ActionCallback {
void onClick(View view);
}
MainActivity.java
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
actionCallBack = new ActionCallBack() {
#Override
public void onClick(View view) {
System.out.println("Call onclick method *****");
}
}
}
When i click on button onClick() method is not evoked, noting is shown in output or no action performed. But is works in the traditional way with onClickListener
I think there is a mistake in your Activity declaration. Anyhow, you are still not setting your callback, as such:
binding.setCallback(this);
or
binding.setCallback(actionCallback);
I solved this issue using this:
binding.setHandlers(this);
I was working in a fragment.

ViewDataBinding getVariable?

The 2 main questions are :
Why ViewDataBinding doesn't have a method like getVariable("variableName") that will look up for a variable and returns it or null if no variable with this name exists.
Is their any way/workaround to achieve this kind of behavior?
So to be more explicit : if I don't know the type of my ViewDataBinding, is their a way to get its variable or I must know its type?
Here is how it is working actually :
I havea layout called my_layout.xml :
<layout>
<data>
<variable
android:name="myVaribale"
android:type="String"/>
</variable>
</data>
<RelativeLayout
android:id="#+id/root_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="#{myVariable}"/>
</RelativeLayout>
<layout>
I inflate an instance of its ViewDataBinding :
MyLayoutBinding binding = (MyLayoutBinding) DataBindingUtil.inflate(inflater, R.layout.my_layout, root, false);
I can get its variable by calling the appropriate method :
String myVariable = binding.getMyVariable();
So this was how to get a variable when we know the type of the ViewDataBinding.
My problem is here :
Let's imagine I have 3 layouts called my_layout_1, my_layout_2, my_layout_3 and those 3 layouts are surrounded by a <layout> tag.
Those 3 layouts has also the same variable (myVariable).
So here are the layouts :
my_layout_1
<layout>
<data>
<variable
android:name="myVariable"
android:type="String"/>
</variable>
</data>
<RelativeLayout
android:id="#+id/root_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="#{myVariable}"
style="#style/my_style_1"/>
</RelativeLayout>
<layout>
my_layout_2
<layout>
<data>
<variable
android:name="myVariable"
android:type="String"/>
</variable>
</data>
<RelativeLayout
android:id="#+id/root_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="#{myVariable}"
style="#style/my_style_2"/>
</RelativeLayout>
<layout>
my_layout_3
<layout>
<data>
<variable
android:name="myVariable"
android:type="String"/>
</variable>
</data>
<RelativeLayout
android:id="#+id/root_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="#{myVariable}"
style="#style/my_style_3"/>
</RelativeLayout>
<layout>
So the different layout represent a text with a different style (I know it can be achieved by others way but the goal is just to explain why I need this function getVariable(variableName).
Imagine now that I got a layout called my_text_container which will randomly contain one of the 3 layouts.
my_text_container
<layout>
<FrameLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<layout>
So I'll add a text by doing something like this (again, it's just for demonstration purpose) :
MyTextContainerBinding textContainerBinding = (MyTextContainerBinding) DataBindingUtil.inflate(inflater, R.layout.my_text_container, root, false);
int textLayoutRes;
int randomNumber = generateNumberBetween0and2();
switch(randomNumber) {
case 0: textLayoutRes = R.layout.my_layout_1;
break;
case 1: textLayoutRes = R.layout.my_layout_2;
break;
case 2: textLayoutRes = R.layout.my_layout_3;
break;
}
textContainerBinding.getRoot().addView(DataBindingUtil.inflate(inflater, textLayoutRes, root, false));
And finally I want to retrieve myVariable but I don't know if it's a layout 1, 2 or 3. Basically what could be done now is to use instanceof operator to check whether its a type MyLayoutBinding1, MyLayoutBinding2 or MylayoutBinding3. But as I said this use-case is only for demonstration purpose and I could have 999 different layouts.
So what I would like to do is :
ViewDataBinding myLayout = DataBindingUtil.getBinding(textContainerBinding.getRoot().getChildAt(0));
String myVariable = myLayout.getVariable("myVariable");
if(myVariable != null)
Log.d(TAG, "myLayout was MylayoutBinding1 or myLayoutBinding2 or myLayoutBinding3");
else
Log.d(TAG, "mylayout was not one of the 3 layouts");
Sorry for this (very) long post but thank's for reading it and for any future answers !
I am not sure if I get it but what I do is I declare a variable in the layout like this:
<data>
<variable
name="variableName"
type="bla.bla.VariableType"/>
</data>
Then first you get your layout binding in onCreate:
binding = DataBindingUtil.inflate(inflater, R.layout.your_layout, container, false);
Then you should send a variable to the layout in code like:
binding.setVariableName(yourVariable);
Then you can retrieve it like:
binding.getVariableName();
why ViewDataBinding Doesn't have getMethodVariable?
consider the normal scenario without binding. you have a text container layout. how will you include another layout inside it in a normal activity.
my_text_container.xml:(Normal activity)
You inflate this layout to your java file using R.layout.my_text_container, you won't inflate every layout to a different view to access textview inside it. just one inflate and go with it.
So the same procedure is followed in DataBinding as well, when you use include tag ,the respective fields will be inside MyTextContainerLayoutBinding.
you don't have to inflate it again and again with activity name like MyLayout1Binding, MyLayout2Binding. that's why getting variable name become unnecessary over here.
I think adding variability to Binding , will be more helpful in a place where you just want to display data. Data from one model across multiple included layouts,more helpful with Listviews,Recyclerviews(where layout use data from models).
you can't provide different type for different layouts ,if you are going include one layout to another.(Remember that this binds both layout to single name (parent_activity in your case container)).
Eg:
UserProfileModel:
public class UserProfileModel {
String firstname;
String lastname;
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
}
my_layout_1.xml;
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable name="user"
type="com.hourglass.lingaraj.bindingusingvariables.UserProfileModel" />
</data>
<LinearLayout
android:orientation="vertical"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:visibility="visible"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{user.firstname}"
style="#style/mystyle_1"/>
<include layout ="#layout/my_layout_2"
app:user="#{user}" />
</LinearLayout>
</layout>
my_layout_2.xml:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user"
type="com.hourglass.lingaraj.bindingusingvariables.UserProfileModel"/>
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:visibility="visible"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{user.lastname}"
style="#style/mystyle_2"/>
</RelativeLayout>
</layout>
MainActivity.java:
public class MainActivity extends AppCompatActivity {
MyLayout1Binding layout_binding;
UserProfileModel data_model;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_layout_1);
data_model = new UserProfileModel();
data_model.setFirstname("Lingaraj");
data_model.setLastname("Sankaravelu");
layout_binding = DataBindingUtil.inflate(getLayoutInflater(),R.layout.my_layout_1,null,false);
layout_binding = DataBindingUtil.setContentView(this,R.layout.my_layout_1);
layout_binding.setUser(data_model);
}
}
As you see we are using only one model to populate data across included layout. So there is no need get Variable name to verify which model is loaded as it's Binded to single layout.
There is no workaround to achieve this, us of I have tried.
So my answer will be No you can't get a variable name from binding
References.
No More findViewById
Android Data Binding: That Thing
Android Data Binding: Adding some variability
Android Data Binding: Express Yourself

Categories

Resources