I'm working on an Android application that manages contacts (Add, Update, Delete)
This is the code that lists the contacts in listview
list_ct.clear();
Cursor cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
if (cursor != null)
{
while (cursor.moveToNext())
{
String id = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.CONTACT_ID));
String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
Cursor cursor2 = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null, CommonDataKinds.Phone.CONTACT_ID +" = "+ id,null, null);
ArrayList<String> phones = new ArrayList<String>();
while (cursor2.moveToNext())
{
String phoneNumber = cursor2.getString(cursor2.getColumnIndex(CommonDataKinds.Phone.NUMBER));
phones.add(phoneNumber);
}
Contact ct = new Contact(id,name,phones);
adapter.notifyDataSetChanged();
}
}
This is the listview event listener
lv.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
{
Contact current = (Contact)list_ct.get(position);
Intent in=new Intent(Intent.ACTION_EDIT,Uri.parse("content://contacts/people/"+current.getId()));
startActivityForResult(in,2);
}
});
When I click on a contact in the listview, for some of them the intent opens and closes immediatly, as if the id doesn't exist in the contact's database, and for others it opens the contact edit for another contact.
The contact display name and its phone numbers are correctly shown, but the id is wrong. What I don't understand is why the id in the first cursor works for showing the contact's phone numbers, but not to update.
You're using a very old and deprecated Android API called "People" for your Contact URI, never use People's API, always ContactsContract.
You should build your contact uri like this:
Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
Other then that, your query is super-slow, you can replace those hundreds of queries with a single fast query to make your app faster and life easier, see an example code in this answer:
https://stackoverflow.com/a/47788324/819355
Related
I use a ListView to show a history of a record. I use database in order to store and pull the record into the ListView. Now, I want to pull this record again by doing this:
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
positions = position;
checkdata();
}
}
and get the data from the database using the positions and turning into id
public void checkdata() {
String[] columns = {"id", "titleexam", "address", "time", "score", "Examdata", "totalscore", "hour", "min", "sec", "timedis"};
Cursor cursor = sqLiteDatabase.query("record", columns, null, null, null, null, null);
cursor.move(positions);
ExamRecord recordr = new ExamRecord(Integer.parseInt(cursor.getString(0)), cursor.getString(1), cursor.getString(2), cursor.getString(3), cursor.getString(4), cursor.getString(5), cursor.getString(6), cursor.getString(7), cursor.getString(8), cursor.getString(9), cursor.getString(10));
title = cursor.getString(1);
data = cursor.getString(5);
}
The problem begin when I sort the ListView in reverse, because the ListView will show all the recent record at the top and the older ones at the bottom using this in the adapter to show it at the user.
Collections.reverse(records);
How can I get the position(id) on the reverse order?
you have many options
option 1: you can reverse query order based on one column
Cursor cursor= sqLiteDatabase.query("record", columns, null, null, null, null yourColumn+" DESC");
option 2: if you send data on ArrayList data structure you can reverse it
Collections.reverse(YourList);
option 3: you can reverse ListView it self i don't it will work for your question
android:stackFromBottom="true"
android:transcriptMode="normal"
I'm working on an app for a robot where the user can define punch combinations which the robot will later fetch from the device. To allow the user to store these trainings I have defined a class "Trainings" which holds the id, the name and the punch combination of the training. This training is later saved in a database, for which I have written a DatabaseHandler class. Adding and displaying the data works fine, but whenever I want to delete an entry with the method below:
public void deleteTraining(Training training) {
SQLiteDatabase db = this.getWritableDatabase();
db.delete(TABLE_TRAININGS, KEY_ID + " = ?",
new String[] { String.valueOf(training.getID()) });
db.close();
}
and later try to populate my GridView again ( handled by a GridAdapter class), I get a Nullpointer Exception
java.lang.NullPointerException: Attempt to read from field 'java.lang.String com.noeth.tobi.mcrobektrainingsplaner.Training._name' on a null object reference
at com.noeth.tobi.mcrobektrainingsplaner.GridAdapter.getView(GridAdapter.java:50)
the getView method of the GridAdapter:
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
// if it's not recycled, initialize some attributes
btn = new Button(context);
btn.setLayoutParams(new GridView.LayoutParams(370, 350));
btn.setPadding(2,100,2,100);
btn.setOnClickListener(new CustomOnClickListener(position, context));
btn.setOnLongClickListener(new CustomOnLongClickListener(position, context, btn));
}
else {
btn = (Button) convertView;
}
btn.setText(db.getTraining(position)._name); //Here the programm throws a Nullpointer Exception AFTER deleting an entry from the database
btn.setTextColor(Color.WHITE);
btn.setBackgroundResource(R.drawable.button_border);
btn.setTag("not_activated");
btn.setId(position);
return btn;
}
I figured that it must have something to do with the id of the deleted training, as the loop simply goes through all ids so I wrote a method recalcIDs which recalculates the id of every item coming after the deleted training:
recalcIDs
public void recalcIDs(){
int k = 1;
int subtract = 1;
int id;
Training training;
for(int i = deleted.get(0)+1; i < db.getTrainingCount(); i++){
if(deleted.size() > 1){
if(i < deleted.get(k)){
training = db.getTraining(i);
id = training.getID();
training.setID(id-subtract);
}
else{
k+=1;
subtract+=1;
}
}
else{
training = db.getTraining(i);
id = training.getID();
training.setID(id-subtract);
}
}
}
However this does not fix it.
When reinstalling the app and starting with a completely new database everythings works again.
Does anybody have an idea what I've done wrong?
P.S.: Here's the getTraining method where it can't find the name:
Training getTraining(int id) {
SQLiteDatabase db = this.getReadableDatabase();
Training training;
Cursor cursor = db.query(TABLE_TRAININGS, new String[] { KEY_ID,
KEY_NAME, KEY_SK}, KEY_ID + "=?",
new String[] { String.valueOf(id) }, null, null, null, null);
if (cursor != null && cursor.moveToFirst()){
training = new Training(Integer.parseInt(cursor.getString(0)),
cursor.getString(1), cursor.getLong(2));
cursor.close();
}
else{
training = null;
Toast.makeText(con,"Couldn't find any training sessions!", Toast.LENGTH_LONG).show();
}
// return training
return training;
}
I'm assuming your the Training.setId method doesn't call the database.
You shouldn't change the id of your training because they get managed by the underlaying database. If you only change the ids in you application logic both datasets (application and database) will differ.
I would recommend to reload all the trainings from the database after a user decided to delete one and call the Gridview.notifyDatasetChanged afterwards.
Im so new to this android development. What im trying to do is to pass the id from MainActivity to Details. So i need to get the id from the MainActivity in order to display the details of a person in Details class. I can pass the id already and display it, but im having trouble on displaying the details of that specific id which is from the database values. How can i display the contents of that specific id in the Details class using the Database function getDetails(id); ? I need to display it on the textviews of the Details activity class. Please help
DB Function
public Cursor getDetails(String id) {
return database.query(TABLENAME, null, id + "=" + id, null, null,
null, null);
}
MainActivity.java
this.listView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View view, int position, long arg3){
TextView txtID = (TextView) view.findViewById(R.id.id);
String id = txtID.getText().toString();
Intent intent = new Intent(MainActivity.this, Details.class);
intent.putExtra("id", id);
startActivity(intent);
}
});
In Details.java, i want to display here the contents of the ID which is on the database. How can i call on to that function and make it display using the ID from the intent being passed?
Details.java
Intent i = getIntent();
String id = i.getStringExtra("id");
txtId = (TextView) findViewById(R.id.id);
txtName= (TextView) findViewById(R.id.name);
txtAbout = (TextView) findViewById(R.id.about);
txtId.setText(id);
txtName.setText(name);
txtAbout .setText(about);
public HashMap<String,String> getDetails(String id) {
HashMap<String, String> hm = new HashMap<>();
SQLiteDatabase db = getWritableDatabase();
Cursor c = db.query(TABLENAME, null, "id=?", new String[]{id}, null, null, null);
if(c.getCount()>0){
c.moveToFirst();
hm.put(KEY, c.getString(c.getColumnIndex(COLUMN_NAME)));
// do this for all columns that you want.
}
c.close();
db.close();
return hm;
}
and in Details.java
String id = getIntent().getExtras.getString("id");
HashMap<String, String> hm = (Your database class Object).getDetails(id);
if(hm.size()>0){
String value1 = hm.get(KEY);
// get all data and show according to you.
}
I am using the bellow code to list a unique list of people that sent me SMS. It works fine but still its a bit slow it takes 4 to 5 seconds to load and I have 650 SMS on my device any suggestion ?
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listSMS();
}
private void listSMS()
{
TextView tview = (TextView) findViewById(R.id.list);
Uri uriSMSURI = Uri.parse("content://sms/inbox");
ContentResolver cr= this.getContentResolver();
Cursor cur = cr.query(uriSMSURI, null, null, null, null);
LinkedHashSet contactList= new LinkedHashSet();
String sms = "";
while (cur.moveToNext()) {
if(!contactList.contains(cur.getString(2)))
{
contactList.add(cur.getString(2));
sms += "From :" + getContactName(cur.getString(2),cr)+"\n";
}
}
cur.close();
tview.append(sms);
}
public static String getContactName(String num, ContentResolver cr) {
Uri u = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,Uri.encode(num));
String[] projection = new String[] { ContactsContract.Contacts.DISPLAY_NAME};
Cursor c = cr.query(u, projection, null, null, null);
try {
if (!c.moveToFirst())
return num;
int index = c.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
return c.getString(index);
} finally {
if (c != null)
c.close();
}
}
Instead of preparing the list of contacts with their names up front and then passing it to the adapter, try preparing the list with ids only and then fetch the corresponding names inside the adapter. This will solve the delay to start but will make scrolling of the ListView a bit slower which can be solved by using a View Holder or some caching mechanism to prevent fetching the same name more than once. Also note that the adapter will query for names of contacts that are currently visible to the user only.
I have an application that displays a listView of contacts sorted by Last, then first names. Beside each contact is an image (icon). There are 3 kinds of contacts for which I'd like to display 3 different images (customers/suppliers/other) I have a default image now that is set to customer. I'm wondering if there's a way using the cusorLoader shown below to alternate images on the fly, or whether it would just be best to add a method involving a cursor in my onResume. (onResume is called each time I need to display the images). I believe simpleCursorAdapter can only take textViews as args, so if it's possible, maybe a compound textview/image would work. My icons are not stored in the database, just in the drawables.
Thanks in advance for any replies.
#Override
protected void onResume() {
super.onResume();
//Starts a new or restarts an existing Loader in this manager
getLoaderManager().restartLoader(0, null, this);
}
/*
* The fillData method binds the simpleCursorAadapter to the listView.
*/
private void fillData() {
String[] from = new String[] { ContactsDB.COLUMN_LAST_NAME, ContactsDB.COLUMN_FIRST_NAME };
//The XML views that the data will be bound to:
int[] to = new int[] {R.id.label2, R.id.label};
getLoaderManager().initLoader(0, null, this);
adapter = new SimpleCursorAdapter(this, R.layout.contact_row, null, from,
to, 0);
setListAdapter(adapter);
}
// Sort the names by last name, then by first name
String orderBy = ContactsDB.COLUMN_LAST_NAME + " COLLATE NOCASE ASC"
+ "," + ContactsDB.COLUMN_FIRST_NAME + " COLLATE NOCASE ASC" ;
// Creates a new loader after the initLoader () call
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String[] projection = { ContactsDB.ROW_ID, ContactsDB.COLUMN_LAST_NAME, ContactsDB.COLUMN_FIRST_NAME };
CursorLoader cursorLoader = new CursorLoader(this,
SomeContentProvider.CONTENT_URI, projection, null, null, orderBy);
return cursorLoader;
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// Swap the new cursor in.
// (The framework will take care of closing the old cursor once we return.)
adapter.swapCursor(data); //Call requires Min API 11
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
// This is called when the last Cursor provided to onLoadFinished()
// above is about to be closed.
// Data is no longer available, delete the reference
adapter.swapCursor(null);
}
}
Here is the code I use to dynamically show a drawable on a ListView, you have to use the function setViewBinder on your adapter:
mAdapter.setViewBinder(new ViewBinder() {
public boolean setViewValue(View aView, Cursor aCursor, int aColumnIndex) {
//Modification of the icon to display in the list
if (aColumnIndex == aCursor.getColumnIndex(DatabaseHandler.RATE_EMOTION)) {
int emotionID = aCursor.getInt(aColumnIndex);
Drawable emotionDrawable = resources.getDrawable(R.drawable.ic_unknown_rate);
//if emotion is set
if(emotionID > 0){
String emotionDrawablePath = "ic_smi" + emotionID;
int emotionDrawableID = resources.getIdentifier(emotionDrawablePath,"drawable", getPackageName());
//if a drawable is found
if(emotionDrawableID > 0){
emotionDrawable = resources.getDrawable(emotionDrawableID);
}
}
ImageView emotionImage = (ImageView) aView;
emotionImage.setImageDrawable(emotionDrawable);
return true;
}
return false;
}
});
You can see in this example that I change the drawable according to the data I get from the cursor for every row.