ListView with SimpleCursorAdapter does not refresh after changes - android

I created a kind of contacts application. I am using a ListView and a SimpleCursorAdapter, and the contacts are stored in a SQLite database.
In the main screen, there are two buttons: "Add contacts" and "View contacts". When I view the contacts and I click on one of them, I can edit or delete that contact.
After I do any of these changes, the contacts list displayed should refresh according to the changes made, but it doesn't.
I have read and tried plenty of possible solutions, but every case that I find is different, and apparently some solutions only work in some cases, so I'm very confused.
In the main activity, I call this method:
public void viewContacts(View view){
Intent intent = new Intent(this, Contacts.class);
startActivity(intent);
}
This is the Contacts class:
public class Contacts extends ExampleActivity {
ContactsOpenHelper dbHelper;
SQLiteDatabase db;
SimpleCursorAdapter adapter;
ListView listView;
Cursor c;
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.contactslayout);
listView = (ListView) findViewById(R.id.listView);
dbHelper = new ContactsOpenHelper(this);
db = dbHelper.getReadableDatabase();
String[] projection = {Contract.Entry.NAME,Contract.Entry.NUMBER,_ID};
c = db.query(Contract.Entry.TABLE_NAME, projection, null, null, null, null, null);
String[] fromColumns = {Contract.Entry.NAME,Contract.Entry.NUMBER};
int[] toViews = {R.id.textViewName, R.id.textViewNumber};
adapter = new SimpleCursorAdapter(this,R.layout.contactslayoutentry,c,fromColumns,toViews,0);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
ContactDialog cd = new ContactDialog(db, id, adapter);
cd.show(getFragmentManager(), "Contactos");
}
});
}
}
The ContactDialog class:
public class ContactDialog extends DialogFragment {
SQLiteDatabase db;
long id;
SimpleCursorAdapter adapter;
public ContactDialog(SQLiteDatabase db, long id, SimpleCursorAdapter adapter) {
this.db = db;
this.id = id;
this.adapter = adapter;
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("Choose an action")
.setPositiveButton("Edit", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
MyDialogFragment mdf = new MyDialogFragment(getActivity().getApplicationContext(), db, true, id);
mdf.show(getFragmentManager(), "TAG");
}
})
.setNegativeButton("Delete", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
String selection = Contract.Entry._ID + " LIKE ?";
String[] selectionArgs = {String.valueOf(id)};
db.delete(Contract.Entry.TABLE_NAME, selection, selectionArgs);
adapter.notifyDataSetChanged();
}
});
return builder.create();
}
}
In the MyDialogFragment class I run the line
db.update(Contract.Entry.TABLE_NAME, myValues, Contract.Entry._ID + " LIKE " + id,null);
I don't know if I need the notifySetDataChanged() method, when to execute it, or whether I need another solution.
I would really appreciate some help.
Thank you.
EDIT:
I forgot to point out that the elements are correctly edited or deleted in the database. When I do some changes, I go back to the main screen and click on "View contacts" again, and the list is updated. I guess this is important to know.

Firstly,I suggest you use CursorLoader and LoaderManager,it will be better than your current solution.
If you still want to use this solution,please add cursor.requery() after update your data,like this:
db.update(Contract.Entry.TABLE_NAME, myValues, Contract.Entry._ID + " LIKE " + id,null);
adapter.getCursor().requery();

Related

How to add different variables to each listview item

I used custom adapter for viewing listview. I tried to define my variables in adapter class but it didnt work. I find a temporary solution using invisible textview for this but i need to solve this problem. How can I overcome this?
Edit 1>
I have a column named date and this column contains datas like 20180723182036 and i grab this data to a list. I want to place this to listview as a variable and i will use this variable later.
My temporary solution is:
I have a code like that in database:
public ArrayList<SimpleNoteItem> getAllData() {
openDatabase();
ArrayList<SimpleNoteItem> newList = new ArrayList<>();
String[] columns = {KEY_ROWTITLE, KEY_ROWCONTENT, KEY_ROWDATE, KEY_ROWSTRDATE};
Cursor cs = db.query(DB_TABLE_NORMAL, columns, null, null, null, null, null);
while(cs.moveToNext()){
SimpleNoteItem allData = new SimpleNoteItem(cs.getString(0), cs.getString(1), cs.getLong(2), cs.getString(3));
newList.add(allData);
}
Collections.reverse(newList);
Log.d(LOGDB, "GETALLDATA STRUCTURE WORKS WELL!");
return newList;
}
Listview longclick constructor is this:
simpleNotesDisplay.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, final long id) {
Log.d(TAG, "LONG CLICKED " + position + " ITEM!");
//THIS AREA WILL BE EDITED FOR MORE ANDROID VERSION SUPPORT
AlertDialog.Builder builder;
final TextView getInvisibleDate = (TextView) view.findViewById(R.id.tv_date_invisible);
builder = new AlertDialog.Builder(NotesActivity.this, android.R.style.Theme_Material_Dialog_Alert);
builder.setTitle("Delete?")
.setMessage("Do you want to send this note to hell?")
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
db.deleteSimpleNote(getInvisibleDate.getText().toString());
Log.d(TAG, "DELETING SUCCESSFUL ON ID = " + id + "!!!");
NotesActivity.this.recreate();
}
})
.setNegativeButton("No", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
//empty
}
})
.show();
return true;
}
});
}
And at least my invisible date:
<TextView
android:id="#+id/tv_date_invisible"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="TextView"
android:visibility="invisible"
tools:layout_editor_absoluteX="351dp"
tools:layout_editor_absoluteY="65dp" />
i hope it can help!
you should better to explain more about what you want but:
1)if you want to show some data in listview you most have a list of objects that each one has its data.
2)if you want to show different data in each row of listview then you most fill different data in each object of your list
3) if you want to show different view of each listviews row you most at first define different xml layouts and in your adapter where you define infelater you can use if else statment to decide which layout shows to witch row. below links will help you
Android ListView with different layouts for each row
https://www.javacodegeeks.com/2014/08/android-listview-with-multiple-row-layout.html
I found a different solution for that>>
I grab all data from database with this code:
public ArrayList<SimpleNoteItem> getAllData() {
openDatabase();
ArrayList<SimpleNoteItem> newList = new ArrayList<>();
String[] columns = {KEY_ROWTITLE, KEY_ROWCONTENT, KEY_ROWDATE, KEY_ROWSTRDATE};
Cursor cs = db.query(DB_TABLE_NORMAL, columns, null, null, null, null, null);
while(cs.moveToNext()){
SimpleNoteItem allData = new SimpleNoteItem(cs.getString(0), cs.getString(1), cs.getLong(2), cs.getString(3));
newList.add(allData);
}
Collections.reverse(newList);
Log.d(LOGDB, "GETALLDATA STRUCTURE WORKS WELL!");
return newList;
}
And in another activity i grab this list with this and then initialized it:
private ArrayList<SimpleNoteItem> getList = new ArrayList<>();
getList = db.getAllData();
initialize should be in onCreate method.
And then i used that data:
simpleNotesDisplay.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, final int position, final long id) {
Log.d(TAG, "LONG CLICKED " + position + " ITEM!");
//THIS AREA WILL BE EDITED FOR MORE ANDROID VERSION SUPPORT
AlertDialog.Builder builder;
builder = new AlertDialog.Builder(NotesActivity.this, android.R.style.Theme_Material_Dialog_Alert);
builder.setTitle("Delete?")
.setMessage("Do you want to send this note to hell?")
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
db.deleteSimpleNote(String.valueOf(getList.get(position).getSimpleNoteDate()));
Log.d(TAG, "DELETING SUCCESSFUL ON ID = " + id + "!!!");
NotesActivity.this.recreate();
}
})
.setNegativeButton("No", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
//empty
}
})
.show();
return true;
}
});

Android SQLite Using two spinners as view filters

MAIN QUESTION:
Wondering if there's any conflict with the two spinners when I try display the results with my LoadGrid. Like if one is overriding the other and ending up with an empty result because of it. If so how do I go about combining the two results from the spinners to get the desired view.
SOME DETAILS:
I want to use two spinners on my gridview to act as filters. One for TERMS and one for STATUS. Specifically what I want to happen is that the user can use those two spinners INDIVIDUALLY or TOGETHER to filter the view of accounts in the DB.
Taking it one step at a time I've gotten one spinner running and working properly. So I cloned that working spinner code to get my second one up and running. Nope, not working. Using either of the two spinners now result in nothing. No accounts show up anymore. It'll let me select from the spinners but my grid will still be empty.
If anything is missing or you need to see more of the code please feel free to ask. Cheers.
Here's the spinners snippets:
Utilities.ManageTermSpinner(this.getParent(), spinTerm);
try {
spinTerm.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> arg0, View arg1,
int arg2, long arg3) {
LoadGrid();
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {
}
});
} catch (Exception ex) {
txtTest.setText(ex.toString());
}
Utilities.ManageStatSpinner(this.getParent(), spinStat);
try {
spinStat.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> arg0, View arg1,
int arg2, long arg3) {
LoadGrid();
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {
}
});
} catch (Exception ex) {
txtTest2.setText(ex.toString());
}
}
Here's the LoadGrid that it references:
public void LoadGrid() {
dbHelper = new DatabaseHelper(this);
try {
View v = spinTerm.getSelectedView();
TextView txt = (TextView) v.findViewById(R.id.txtTermClass);
String Terms = String.valueOf(txt.getText());
Cursor c = dbHelper.getAccByTerms(Terms);
startManagingCursor(c);
View x = spinStat.getSelectedView();
TextView txt2 = (TextView) x.findViewById(R.id.txtStatID);
String Status = String.valueOf(txt2.getText());
Cursor b = dbHelper.getAccByStatus(Status);
startManagingCursor(b);
String[] from = new String[]{DatabaseHelper.colName, DatabaseHelper.colAmount, DatabaseHelper.colTermsClass, DatabaseHelper.colStatClass};
int[] to = new int[]{R.id.colName, R.id.colAmount, R.id.colTerms, R.id.colStat};
SimpleCursorAdapter sca = new SimpleCursorAdapter(this, R.layout.gridrow, c, from, to);
grid.setAdapter(sca);
SimpleCursorAdapter sba = new SimpleCursorAdapter(this, R.layout.gridrow, b, from, to);
grid.setAdapter(sba);
} catch (Exception ex) {
AlertDialog.Builder b = new AlertDialog.Builder(this);
b.setMessage(ex.toString());
b.show();
}
}
The dbHelper snippets for TERMS and STATUS:
public Cursor getAccByTerms(String Terms)
{
SQLiteDatabase db=this.getReadableDatabase();
String [] columns=new String[]{"_id",colName,colAmount,colPurpose,colTermsClass,colDate,colEditDate,colStatClass};
Cursor c=db.query(viewAccs, columns, colTermsClass+"=?", new String[]{Terms}, null, null, null);
return c;
}
public Cursor getAccByStatus(String Status)
{
SQLiteDatabase db=this.getReadableDatabase();
String [] columns=new String[]{"_id",colName,colAmount,colPurpose, colTermsClass,colDate,colEditDate,colStatClass};
Cursor d=db.query(viewAccs,columns, colStatClass +"=?",new String[]{Status},null,null,null);
return d;
}
And the Utilities snippet where I keep the ManageSpinner stuff:
public class Utilities {
static public void ManageTermSpinner(Context context, Spinner view) {
DatabaseHelper dbHelper = new DatabaseHelper(context);
Cursor c = dbHelper.getAllTerms();
SimpleCursorAdapter ca = new SimpleCursorAdapter(context, R.layout.termspinnerrow, c, new String[]{DatabaseHelper.colTermsClass, "_id"}, new int[]{R.id.txtTermClass});
view.setAdapter(ca);
}
static public void ManageStatSpinner(Context context, Spinner view) {
DatabaseHelper dbHelper = new DatabaseHelper(context);
Cursor d = dbHelper.getAllStatus();
SimpleCursorAdapter da = new SimpleCursorAdapter(context,R.layout.statspinnerrow, d, new String[]{DatabaseHelper.colStatClass,"_id"}, new int[]{R.id.txtStatClass});
view.setAdapter(da);
}
This is how you need to do it:
public Cursor getAccByTermsAndStatus(String Terms, String status)
{
SQLiteDatabase db=this.getReadableDatabase();
String [] columns=new String[]{"_id",colName,colAmount,colPurpose,colTermsClass,colDate,colEditDate,colStatClass};
Cursor c=db.query(viewAccs, columns, colTermsClass+"=? AND " + colStatClass + "=?", new String[]{Terms, status}, null, null, null);
return c;
}

Android: Delete database row from the list view on long tap

I am building an application in which I'm populating data from the database into the listview of my activity. Now, on longItemClick Listener I want to delete that value from the database and update my listview in the app.
Here my code for longClickListener on the listview:
private ListView showing_history;
private ListAdapter mHistoryListAdapter;
private ArrayList<SearchHistoryDetails> searchArrayList;
private ArrayList<SearchHistoryDetails> HistoryObjArrayList;
mHistoryListAdapter = new ArrayAdapter<String>(this,
R.layout.mylistlayout, populateList());
showing_history.setAdapter(mHistoryListAdapter);
HistoryObjArrayList = new ArrayList<SearchHistoryDetails>();
/**
* Long tap on listview to delete the selected item from the history
* list.
* */
showing_history
.setOnItemLongClickListener(new OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> arg0,
View arg1, int arg2, long arg3) {
final AlertDialog.Builder b = new AlertDialog.Builder(
MainActivity.this);
b.setIcon(android.R.drawable.ic_dialog_alert);
b.setMessage("Delete this from history?");
b.setPositiveButton("Yes",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int whichButton) {
Toast.makeText(getApplicationContext(),
"Yes", Toast.LENGTH_LONG)
.show();
}
});
b.setNegativeButton("No",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int whichButton) {
dialog.cancel();
}
});
b.show();
return true;
}
});
By this code I'm inserting data into the database:
/**
* Inserting the string when user searches for anything into the database.
* */
public void insertHistory(SearchHistoryDetails paraHistoryDetailsPojoObj) {
AndroidOpenDbHelper androidOpenDbHelperObj = new AndroidOpenDbHelper(
this);
SQLiteDatabase sqliteDatabase = androidOpenDbHelperObj
.getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put(AndroidOpenDbHelper.COLUMN_NAME_HISTORY_STRING,
paraHistoryDetailsPojoObj.getHistoryName());
#SuppressWarnings("unused")
long affectedColumnId = sqliteDatabase.insert(
AndroidOpenDbHelper.TABLE_NAME_HISTORY, null, contentValues);
sqliteDatabase.close();
}
Here is the code from which I'm retrieving data from the database and repopulating it into the list view:
public List<String> populateList() {
List<String> HistoryNamesList = new ArrayList<String>();
AndroidOpenDbHelper openHelperClass = new AndroidOpenDbHelper(this);
SQLiteDatabase sqliteDatabase = openHelperClass.getReadableDatabase();
Cursor cursor = sqliteDatabase
.query(AndroidOpenDbHelper.TABLE_NAME_HISTORY,
new String[] { AndroidOpenDbHelper.COLUMN_NAME_HISTORY_STRING },
null, null, null, null, AndroidOpenDbHelper.ID
+ " DESC");
startManagingCursor(cursor);
while (cursor.moveToNext()) {
String ugName = cursor
.getString(cursor
.getColumnIndex(AndroidOpenDbHelper.COLUMN_NAME_HISTORY_STRING));
SearchHistoryDetails histPojoClass = new SearchHistoryDetails();
histPojoClass.setHistoryName(ugName);
searchArrayList.add(histPojoClass);
HistoryNamesList.add(ugName);
}
sqliteDatabase.close();
int history_db = cursor.getCount();
if (history_db == 0) {
history_layout.setVisibility(LinearLayout.GONE);
} else {
history_layout.setVisibility(LinearLayout.VISIBLE);
}
return HistoryNamesList;
}
Retrieving and inserting data all things are perfectly, but deleting of a particular row is not working for me. Right now I have done some action using toast and it is working, what should I do so that, the database data updates after deletion of the row from the listview. Please give me any idea to overcome this problem.
Try this..,.
b.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
DatabaseHandler db=new DatabaseHandler(getApplicationContext());
db.delete(arg3+"");
list.remove(position);
YourActivity.this.adapter.notifyDataSetChanged();
}
});
and in DatabaseHandler class
public void delete(String id)
{
SQLiteDatabase db = this.getReadableDatabase();
db.delete(TABLE_NAME, KEY_ID + "=?", new String[]{id});
db.close();
}
most proper solution is to use Loaders.. especially CursorLoader along with Content Provider.
If Content Provider is an overkill for what you are doing try CursorLoader from commonsware. The update after deletion happens automatically and not in the ui thread which is good.
https://github.com/commonsguy/cwac-loaderex.. its pretty easy once you get your head around this.
However if you still want to delete the row as per your code. Just delete it in your longpress listener. But after that refresh the adapter.
How to refresh Android listview?
To update list.. Firstly call the method where you specified the delete query from builder positive button.
After that delete the item from HistoryNamesList array list
Now the data removed from both database and arraylist but listview still show previous data so you have to notify listview adapter as show below
adapter.notifyDataSetChanged();
adapter.notifyDataSetInvalidated();
Solved!
Changes made on onCLick():
public void onClick(DialogInterface dialog,
int whichButton) {
HistoryNamesList.remove(pos);
((BaseAdapter) mHistoryListAdapter)
.notifyDataSetChanged();
}

Get information from a listItem onClick with _id

I have a listview populated by a database. The listview displays some columns of the database. I want to be able to click the listItem and display a new activity that shows that whole record. I am trying to do this by passing the id between through an intent but it is not working. The activity is starting but the textViews aren't being populated. I have included the relevent code snippets here. Would really appreciate some help
MainActivity
public class MainActivity extends ListActivity {
....
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
...
}
private void fillList() {
ListView lv = getListView();
Cursor c = db.getData();
startManagingCursor(c);
CustomCursorAdapter adapter = new CustomCursorAdapter(getApplicationContext(), c);
lv.setAdapter(adapter);
lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id){
Intent k = new Intent(MainActivity.this, ProductDetails.class);
k.putExtra("item_id", id);
startActivity(k);
}
});
}
}
ProdDetails
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
db = new ProdDB(this);
setContentView(R.layout.product_details);
db.open();
long id = getIntent().getLongExtra("item_id", 0);
Cursor cursor = db.getDetails(id);
while(cursor.moveToFirst()){
tv1.setText(cursor.getString(cursor.getColumnIndex(ProdDB.KEY_PRODCODE)));
tv2.setText(cursor.getString(cursor.getColumnIndex(ProdDB.KEY_PRODNAME)));
...
...
}
}
}
DB.getDetails() - from ProdDB
String[] columns = new String[] {KEY_ROWID,
KEY_PRODCODE,
KEY_PRODNAME,
KEY_PRODTYPE,
KEY_PRODCAT,
KEY_PRODPRICE,
KEY_PRODRRP,
KEY_PRODSUPPLIER,
KEY_NOTES};
return db.query(DB_TABLE, columns, KEY_ROWID + " = ?", new String[]{String.valueOf(id)}, null, null, null);
you're having issue cause you're using while(cursor.moveToFirst()){. if you must use the while loop then you should use the .moveToNext() instead. Otherwise, you can just take out the while loop for moveToFirst since i'm assuming you only need the one row. as for why, don't ask me, i couldn't tell you.

How to trigger an alert message for user when there's no match with built-in contacts and database table?[Android]

I'm working on an app where I've a ListView of contacts' names retrieved from the built-in contacts list. I use ContentProvider to get the names from the built-in contacts list. Plus, I store the names from the ListView into my database table.
I need to synchronize with the built-in contacts list with database table. So, I need to trigger an alert message for the user when there is no match between the DISPLAY_NAME in ContactsContract from the built-in and a name from my table. And, the user will have to manually change the name and I'll have to change the name which is from the table.
How do I do that? i need help with this.
I appreciate any help provided. Any examples and tutorials will be also appreciated.
Thanks.!
UPDATE
ContactsList.java - this is the ListView of contacts retrieved from the android phone's contacts.
BuddyDBAdapter buddyDB = new BuddyDBAdapter(this);
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.contacts_list);
ListView list = getListView();
setListAdapter(new ArrayAdapter<String> (this, R.layout.contacts_list, R.id.contactName, buddiesList));
Uri allContacts = Uri.parse("content://contacts/people");
Cursor c = managedQuery(allContacts, null, null, null, null);
String[] columns = new String[] {ContactsContract.Contacts.DISPLAY_NAME};
int[] views = new int[] {R.id.contactName};
startManagingCursor(c);
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.contacts_list, c, columns, views);
this.setListAdapter(adapter);
}
#Override
public void onListItemClick(ListView l, View v, int position, long id)
{
buddyDB.open();
long name_id;
super.onListItemClick(l, v, position, id);
/*String action = getIntent().getAction();
if(Intent.ACTION_VIEW.equals(action))
{
//startActivity(intent);
}*/
//startActivityForResult(new Intent(Intent.ACTION_PICK),PICK_CONTACT);
Cursor c = ((SimpleCursorAdapter)l.getAdapter()).getCursor();
TextView contactName = (TextView) v.findViewById(R.id.contactName);
String NameValue = contactName.getText().toString();
name_id = buddyDB.insertNames(NameValue);
Toast.makeText(getBaseContext(),
"Selected: " + buddiesList[position], Toast.LENGTH_SHORT).show();
buddyDB.close();
when you giving the query to sqlite database or contacts table, if your cursor is null OR cursor.getCount() is not > 0 then you can show the alert messege to that contacts or data is not present in the database.
Hope this will help you.
and to alertView use this code:
final AlertDialog alertDialog = new AlertDialog.Builder(this).create();
alertDialog.setTitle("ALERT");
alertDialog.setMessage("NO DATA FOUND");
alertDialog.setIcon(R.drawable.warning);
alertDialog.setButton("OK",new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// DO YOUR STUFF ON CLICK OF OK
}
});
alertDialog.show();

Categories

Resources