Exception when showing a dialog after orientation change - android

I have an activity and a fragment inside it.inside fragment, there is a button, and on click of button a dialog shows.
Everything works, until user do a orientation change and click button after it.
IllegalStateException(cannot perform this action after onsaveinstancestate) occurs when user clicks button after orientation change. I'm using android support framework.
Anybody have any idea regarfing this?
Activity Code
public void openMoreDialog(String shareData, String link) {
DialogFragment dialog = new MoreDialog(shareData, link);
dialog.show(getSupportFragmentManager(), "MoreDialog");
}
Fragment Code
public void onAttach(Activity activity) {
super.onAttach(activity);
mControl = (ActivityControl)activity;
}
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
ImageButton moreButton = (ImageButton)v.findViewById(R.id.moreButton);
moreButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
mControl.openMoreDialog(shareData, link);
}
});
return rootView;
}
FragmentDialog code
public class MoreDialog extends DialogFragment {
private String mShareData;
private String mLink;
public MoreDialog(String shareData, String link){
mShareData = shareData;
mLink = link;
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// Get the layout inflater
LayoutInflater inflater = getActivity().getLayoutInflater();
View dialogView = inflater.inflate(R.layout.more_dialog, null);
Button openBtn = (Button)dialogView.findViewById(R.id.openBtn);
openBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
openLink(mLink);
}
});
Button shareBtn = (Button)dialogView.findViewById(R.id.shareBtn);
shareBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
shareNews(mShareData);
}
});
builder.setView(dialogView);
return builder.create();
}
private void openLink(String link){
}
private void shareNews(String data){
}
}

Helpful link & solution how to:
https://stackoverflow.com/a/17413324/619673 and btw, constructor in fragment must be empty! Documentation:
http://developer.android.com/reference/android/app/Fragment.html
public Fragment ()
Added in API level 11
Default constructor.
Every fragment must have an empty constructor, so
it can be instantiated when restoring its activity's state. It is
strongly recommended that subclasses do not have other constructors
with parameters, since these constructors will not be called when the
fragment is re-instantiated; instead, arguments can be supplied by the
caller with setArguments(Bundle) and later retrieved by the Fragment
with getArguments().
Applications should generally not implement a constructor. The first
place application code an run where the fragment is ready to be used
is in onAttach(Activity), the point where the fragment is actually
associated with its activity. Some applications may also want to
implement onInflate(Activity, AttributeSet, Bundle) to retrieve
attributes from a layout resource, though should take care here
because this happens for the fragment is attached to its activity.

Related

Unable to initialize the Context

I have an activity which uses an adapter and a fragment that displays a recycler list view. When the user taps on any row, I display an AlertDialog (created as a DialogFragment) for them to enter the data.
The callbacks from the AlertDialog is listened by the Fragment and once all the fields are captured in the Fragment, the completed object is sent back to the activity to save it in the database.
Here is a screenshot ...
Right now after I enter a name and hit continue, I get a crash because the listener from the DisplayTextEntryAlert class (i.e. DialogFragment) isn't initialized.
java.lang.NullPointerException: Attempt to invoke interface method 'void alerts.DisplayTextEntryAlert$DisplayTextEntryAlertListener.onYesButtonClicked(android.support.v4.app.DialogFragment, java.lang.String)' on a null object reference
at alerts.DisplayTextEntryAlert$1.onClick(DisplayTextEntryAlert.java:97)
at android.support.v7.app.AlertController$ButtonHandler.handleMessage(AlertController.java:161)
In my DisplayTextEntryAlert class, it crashes when mListener.onYesButtonClicked is executed.
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
....
....
....
builder.setPositiveButton(R.string.stgContinue, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
mTextEntered = editTextControl.getText().toString();
mListener.onYesButtonClicked(DisplayTextEntryAlert.this, mTextEntered);
}
});
The mListener object is initialized in the 'onAttach' method in the DisplayTextEntryAlert class
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof DisplayTextEntryAlertListener) {
mListener = (DisplayTextEntryAlertListener)context;
} else {
Log.d(this.toString(),"trackContext");
Log.d(this.toString(),context.toString());
//throw new RuntimeException(context.toString() + " must implement DisplayTextEntryAlertListener");
}
}
But when debugging, I notice that this line never gets executed.
mListener = (DisplayTextEntryAlertListener)context;
This AlertDialog is created from the Fragment class (AddFriendFragment) that is launched from the activity (AddFriendActivity)
DisplayTextEntryAlert displayTextEntryAlertFragment = DisplayTextEntryAlert.newInstance("","Enter the first name");
FragmentManager fragmentManager = ((FragmentActivity) mContext).getSupportFragmentManager();
displayTextEntryAlertFragment.show(fragmentManager, "newFriendFragment"); // give it a name for retrieving
The 'mContext' in here is created from the onCreateView method in this fragment
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_newfriend_list, container, false);
// Set the adapter
if (view instanceof RecyclerView) {
mContext = view.getContext();
....
....
....
mNewFriendAdapter = new NewFriendAdapter(mNewFriendFields, this);
recyclerView.setAdapter(mNewFriendAdapter);
}
return view;
}
Question: I'm clearly missing something here else the mListener would have been initialized in the onAttach method in the DisplayTextEntryAlert class.
Any clues ?
Here is the full source code for reference
https://gist.github.com/ArdenDev/229c69f803dce62a1e46acb0e05c7f1a
Make your AddFriendActivity implement DisplayTextEntryAlertListener
Then override that functionality in AddFriendActivity
#Override
public void onYesButtonClicked(String text)
{
// Do anything with your text
}
If you need Context for Listener, use getActivity() or getContext() instead of DisplayTextEntryAlert.this
mListener.onYesButtonClicked(getActivity());
One more thing, you don't have to check instance of when inflating your view. Because I saw there is not only RecyclerView in your layout, but also ToolBar, so the view is definately not RecyclerView
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_newfriend_list, container, false);
// Remove the instance of condition
mContext = view.getContext();
....
....
....
mNewFriendAdapter = new NewFriendAdapter(mNewFriendFields, this);
recyclerView.setAdapter(mNewFriendAdapter);
return view;
}
So, your problem that you use on onAttach(Content context) which was added in 23 API version (Android 6) and it never calls at lower API versions. You should override both onAttach(Context context) and onAttach(Activity activity)
So the trick was to use setFragment. This now works
DisplayTextEntryAlert displayTextEntryAlertFragment = DisplayTextEntryAlert.newInstance("","Enter the first name");
displayTextEntryAlertFragment.setTargetFragment(this, 0);
displayTextEntryAlertFragment.show(getActivity().getSupportFragmentManager(), "newFriendFragment");

Fragment,DialogFragment Issue

I am calling dialog fragment from FragmentA and returning some values to fragmentA. Now issue is whenever i go to another fragmentB from same fragmentA and return to it my dialog fragment values get cleared.
when i click on consultant doctor textview, a dialog opens (Pic 2). On Selecting an item (Pic 2),returns a value back to FragmentA. Pic 3 is a Fragment B which opens on same activity. But when i click on cross button on pic 3 and popBackStack , my value for consult doctor clears shown in Pic 4.
Pic 4 is an ISSUE
DialogFragment
#Override
public void onStart() {
super.onStart();
getDialog().getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
getDialog().getWindow().setGravity(Gravity.CENTER);
getDialog().setCancelable(false);
getDialog().setCanceledOnTouchOutside(false);
getDialog().closeOptionsMenu();
}
#Override public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
#Nullable #Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
View rootView = inflater.inflate(R.layout.consultant_doc_dialog, container, false);
recyclerView = (RecyclerView)rootView.findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setHasFixedSize(true);
adapter = new ConsultantDoctAdapter(getContext(),this);
adapter.getDocList().addAll(new ArrayList<DoctorList>());
recyclerView.setAdapter(adapter);
adapter.getDocList().clear();
adapter.getDocList().addAll(list);
adapter.notifyDataSetChanged();
close = (ImageButton)rootView.findViewById(R.id.bt_close);
close.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View view) {
getDialog().dismiss();
}
});
//cityEditText.setOnQueryTextListener(onQueryTextListener);
return rootView;
}
Fragment
#Nullable #Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.layout_create_leads, container, false);
ButterKnife.bind(this, view);
setRetainInstance(true);
init();
setPicker();
setSpinnerListener();
btCheckCalendar.setOnClickListener(this);
etCityId.setOnClickListener(this);
etConsultingDocId.setOnClickListener(this);
btSubmit.setOnClickListener(this);
tvClientReferral.setOnClickListener(this);
etSalesPerson.setText(sharedPref.getString(AppConstants.PREFERENCE_USER_NAME, ""));
etZone.setText(sharedPref.getString(AppConstants.USER_ZONE, ""));
etAreaCode.setText(sharedPref.getString(AppConstants.USER_AREA_CODE, ""));
setSpinner();
getConsultantDoctorList();
return view;
}
Fragment B callBack:
getActivity().getSupportFragmentManager()
.beginTransaction()
.replace(R.id.content_main, new MyCalendarFragment())
.addToBackStack("calendarFragment")
.commit();
DialogCallack:
ConsultantDocDialogFragment consultantDocDialog = new ConsultantDocDialogFragment();
consultantDocDialog.setParameter(getContext(), this, doclist);
consultantDocDialog.show(getActivity().getSupportFragmentManager(),
ConsultantDocDialogFragment.class.getSimpleName());
break;
Please help me so that i can able to save state of values got from dialog fragment.
Please find the following code it may help you-
This is Fragment Code where you can get CallBack from Dialog Fragment-
HomeFragment.java
public class HomeFragment extends Fragment implements AlertDFragment.Callback {
private static final int DIALOG_FRAGMENT = 100;
Button alertdfragbutton;
private View rootView;
public HomeFragment() {
}
public static HomeFragment newInstance() {
return new HomeFragment();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_home, container, false);
initUI(rootView);
return rootView;
}
private void initUI(View rootView) {
alertdfragbutton.setOnClickListener(new View.OnClickListener() {
public void onClick(View arg0) {
AlertDFragment alertdFragment = new AlertDFragment();
alertdFragment.setTargetFragment(HomeFragment.this, DIALOG_FRAGMENT);
// Show Alert DialogFragment
alertdFragment.show(getChildFragmentManager(), "Alert Dialog Fragment");
}
});
}
#Override
public void accept() {
Log.e("Home ", "OK");
}
#Override
public void decline() {
}
#Override
public void cancel() {
Log.e("Home ", "CANCEL");
}
}
Here is Dialog Fragment where we declare CallBack with methods-
public class AlertDFragment extends DialogFragment {
Callback callback;
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
callback = (Callback) getTargetFragment();
return new AlertDialog.Builder(getActivity())
// Set Dialog Icon
.setIcon(R.drawable.androidhappy)
// Set Dialog Title
.setTitle("Alert DialogFragment")
// Set Dialog Message
.setMessage("Alert DialogFragment Tutorial")
// Positive button
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
callback.accept();
// Do something else
//getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, getActivity().getIntent());
}
})
// Negative Button
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
callback.cancel();
// Do something else
// getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_CANCELED, getActivity().getIntent());
}
}).create();
}
public static interface Callback {
public void accept();
public void decline();
public void cancel();
}
}
A simple way to return values from DialogFragment is using setTargetFragment for calling a fragmentB creation, then return data to getTargetFragment (if not null). In fragmentA you can receive data through onActivityResult.
Another way is using SharedPreferences. You can get a new value with onResume or onHiddenChanged.
Instead of using the "Fragment Transition" why don't you just POP-UP your custom view
Just Create a global reference of
Dialogue dialogue
View popupView
and on click of whatever textview button etc.
you can just call a method like
void popup(){
popupView = LayoutInflater.from(getActivity()).inflate(R.layout.your_calenderlayout, null);
//suppose you have TextView cal_textview in popUp view i.e, your_calenderlayout
cal_textview = (TextView ) popupView.findViewById(R.id.cal_textview);
dialog = new Dialog(getContext());
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setCanceledOnTouchOutside(true);
dialog.setContentView(popupView); //and just add your popUpview
// For setting backgroung
/*dialog.getWindow().setBackgroundDrawableResource(android.R.color.Transparent);
*/
//For setting the width or height of popup
/*WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
lp.copyFrom(dialog.getWindow().getAttributes());
lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
lp.gravity = Gravity.CENTER;
dialog.getWindow().setAttributes(lp);*/
dialog.show();
}
and on dismiss of popUp or on click of the view inside popupView you set the value of variables or member inside the fragment directly
Hope this will help
You can use Shared prefs or sqlite to get your values and if you think its use less to save your temporary data in share prefs or sqlite Then Singleton model is a good option .. i believe we should follow KISS
design principle :P

Cordova WebView in a Dialog

Can I implement a Cordova WebView in a CustomDialog in android?
I want to click a button and this show me a dialog with the webview. I tried in this way but didn't work.
button = (Button) findViewById(R.id.buttonShowCustomDialog);
// add button click listener
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
// custom dialog
final Dialog dialog = new Dialog(context);
dialog.setContentView(R.layout.custom);
dialog.setTitle("Title...");
cwv = (CordovaWebView) findViewById(R.id.webview);
Config.init(this);
cwv.loadUrl("file:///android_asset/www/index.html");
dialog.show();
}
});
UPDATE2:
In fact, the extended Dialog doesn't event need to implement CordovaInterface. It just needs to override setContentView, and that's enough.
public class CordovaDialog extends Dialog {
private Context currentContext;
public CordovaDialog(Context context) {
super(context);
this.currentContext = context;
}
// we have to override this because we need to disable attaching to root when inflating (wtf cordova ??)
#Override public void setContentView(int layoutResID) {
final LayoutInflater inflater = LayoutInflater.from(this.currentContext);
View v = inflater.inflate(layoutResID, null, false);
super.setContentView(v);
};
}
I have the same issue. I have also tried to create a class which extends Dialog and implements CordovaInterface, but didn't have any luck witjh that either. It seems every time I call setContentView, Cordova just can't find the Activity associated to the Dialog, and logcat shows a warning saying that my Activity doesn't implement CordovaInterface but it does.
UPDATE:
Ok, I figured it out. So here's how I dit it. It's long but it works.
First of all, let's assume that the parent Activity, the one which is creating the dialog, is already implementing CordovaInterface. Also, let's say that your CordovaWebview is inside a layout.
Make a new class (CordovaDialog for example) which extends Dialog and implements CordovaInterface.
Make a new constructor for the CordovaDialog class which passes the context and the interface so you can set the CordovaInterface from parent activity (which should also implement CordovaInterface).
Override setContentView in the CordovaDialog so that it inflates the view without attaching to root (last params set to false).
In your main activity, create the dialog, call Config.init(), and call loadUrl for CordovaWebview.
public class CordovaDialog extends Dialog implements CordovaInterface {
CordovaInterface parentCordovaInterface;
Context currentContext;
public CordovaDialog(Context context, CordovaInterface ci) {
super(context);
this.parentCordovaInterface = ci;
this.currentContext = context;
}
#Override public void setContentView(int layoutResID) {
final LayoutInflater inflater = LayoutInflater.from(this.currentContext);
View v = inflater.inflate(layoutResID, null, false);
super.setContentView(v);
};
#Override
public Activity getActivity() {
return this.parentCordovaInterface.getActivity();
}
#Override
public ExecutorService getThreadPool() {
return this.parentCordovaInterface.getThreadPool();
}
#Override
public Object onMessage(String arg0, Object arg1) {
return this.parentCordovaInterface.onMessage(arg0, arg1);
}
#Override
public void setActivityResultCallback(CordovaPlugin plugin) {
this.parentCordovaInterface.setActivityResultCallback(plugin);
}
#Override
public void startActivityForResult(CordovaPlugin command, Intent intent, int requestCode) {
this.parentCordovaInterface.startActivityForResult(command, intent, requestCode);
}
}
And then in your the activity which implements CordovaInterface:
final CordovaDialog dialog = new CordovaDialog(this, this);
dialog.setOwnerActivity(this);
dialog.setContentView(R.layout.dialog_with_cordovawebview);
CordovaWebView cwv = (CordovaWebView) dialog.findViewById(R.id.webViewDialog);
Config.init();
cwv.loadUrl("file:///android_asset/www/index.html");
dialog.show();

Activity to SherlockFragment

I want to change my app ( extends Activity ) to Fragment ( extends SherlockFragment )
If I change it I have much errors;
public class AlarmClock extends SherlockFragment implements OnClickListener {
This is my onCreateView:
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
// sanity check -- no database, no clock
if (getContentResolver() == null) {
new AlertDialog.Builder(this)
.setTitle(getString(R.string.error))
.setMessage(getString(R.string.dberror))
.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
finish();
}
})
.setOnCancelListener(
new DialogInterface.OnCancelListener() {
public void onCancel(DialogInterface dialog) {
finish();
}
}).setIcon(android.R.drawable.ic_dialog_alert)
.create().show();
return;
}
View view = inflater.inflate(R.layout.alarm_clock, container, false);
// menu buttons
add = (ImageButton) findViewById(R.id.ibAdd);
snooze = (ImageButton) findViewById(R.id.ibSnooze);
add.setOnClickListener(this);
snooze.setOnClickListener(this);
mFactory = LayoutInflater.from(this);
mPrefs = getSharedPreferences(PREFERENCES, 0);
mCursor = Alarms.getAlarmsCursor(getContentResolver());
mAlarmsList = (ListView) findViewById(R.id.alarms_list);
mAlarmsList.setAdapter(new AlarmTimeAdapter(this, mCursor));
mAlarmsList.setVerticalScrollBarEnabled(true);
mAlarmsList.setItemsCanFocus(true);
mClockLayout = (ViewGroup) findViewById(R.id.clock);
mQuickAlarm = findViewById(R.id.ibSnooze);
mQuickAlarm.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
showQuickAlarmDialog();
}
});
setVolumeControlStream(android.media.AudioManager.STREAM_ALARM);
setQuickAlarmVisibility(mPrefs.getBoolean(PREF_SHOW_QUICK_ALARM, true));
return view;
}
There are a lot of errors because there is no Activity.
If is Activity it works.
I use "extends SherlockFragment" because I want to add it to the table.
How fix this problem ? Please help me.
If am right, Fragments must definitely be used in an Activity.
So instead of using this use getActivity(); to get the Activity(which uses this fragment) Context.
something like:
getActivity.finish();
and in case of findViewById(//some Id);
use it like this:
inflatedView.findViewById(//Id);
A Fragment is not a Context (unlike Activity or Application). So quite a few methods are not available to it.
It however has access to the context it is attached to. Usually, you can call getActivity() within the fragment to get it. You should check if the Fragment is part of the activity by using the isAdded() method.
You should do some reading about Fragments, porting existing activities to use Fragments, ... tutorials are available using Google.

Android: Can i show multiple Dialogs one over another? Is there something like Dialog Z-Level?

Is it possible to show multiple Dialogs one over another? Is there something like Dialog Z-Level?
I am using DialogFragment where user chooses elements, when he comfirms his choice, it is saved to database and sent on server. if the save action fails I would like to inform user with ... another dialog is it possible? And will it not clear off my first dialog?
Thanks in advance.
Indeed, it's possible to show multiple dialog Fragments one inside another one. The z-order depends on the order they are created.
In the code below there is an example of a FragmentActivity with the behavior that you require.
public class MyActivity extends FragmentActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
//...
}
public void onSave(View view) {
Intent intent = getIntent();
this.setResult(RESULT_OK, intent);
finish();
}
public void onCancel(View view) {
finish();
}
public void SelectWeekDay(View view) {
DialogFragment selectWeekDayFragment = new SelectWeekDayFragment();
selectWeekDayFragment.show(getSupportFragmentManager(), "WeekDayDialog");
}
public class SelectWeekDayFragment extends DialogFragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.week_day_dialog, container, true);
Button saveButton = (Button) view.findViewById(R.id.button_save);
saveButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
CheckBox checkboxMonday = (CheckBox) getDialog().findViewById(R.id.checkBox_monday);
if (!checkboxMonday.isChecked()) {
DialogFragment saveErrorFragment = new SaveErrorFragment();
saveErrorFragment.show(getSupportFragmentManager(), "SaveErrorFragment");
}
else {
SaveToDb(); //Perform actions to store on db or what you wish
dismiss();
}
}
});
Button cancelButton = (Button) view.findViewById(R.id.button_cancel);
cancelButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
dismiss();
}
});
return view;
}
}
public class SaveErrorFragment extends DialogFragment {
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity())
.setMessage("You must select Monday").setPositiveButton("Ok", null).create();
}
}
}
My advice is to use a custom layout with a ViewFlipper inside your dialog so you can easily switch between a progress-bar or whatever different layouts you want to show. If you want to show multiple Dialogs my guess is that the z-order depends on the order they were created the latest beeing shown on top.
You usually can, however, just be a little careful. Use the dialog's lifecycle to your advantage to avoid side-effects. For example: you can do a check on a function like onStop() to see if the child dialog is open, and if so, close it.
Ideally, cutting down on the amount of layers of dialogs you have is ideal, as long as it's sane (for example: doing it ends up being hundreds of lines of code more)

Categories

Resources