ContactList in RecyclerView with Cardview - cursor

i want to display my contacts in a recyclerview with cardelements. The problem is, that it is only displayed one contact for several times. So the same name is shown in every card. It seems like the cursor doesn't get to the next contact. But I don't know why. And I have to admit that I'm quite new to android.
Here is the code:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.contacts);
RecyclerView recList = (RecyclerView) findViewById(R.id.cardList);
recList.setHasFixedSize(true);
LinearLayoutManager llm = new LinearLayoutManager(this);
llm.setOrientation(LinearLayoutManager.VERTICAL);
recList.setLayoutManager(llm);
contactRecAdapter ca = new contactRecAdapter(data);
recList.setAdapter(ca);
data = displayContact();
}
and my displayContact():
private ArrayList<contactInfo> displayContact(){
ContentResolver cr = getContentResolver();
Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI,null,null,null,null);
String name ;
String number;
String id;
contactInfo cI = new contactInfo();
if(cur.getCount()>1){
while (cur.moveToNext()){
id = cur.getString(cur.getColumnIndex(ContactsContract.Contacts._ID));
name = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
if(Integer.parseInt(cur.getString(cur.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)))>0){
cI.name = name;
Cursor pCur = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = ?",new String[]{id},null);
while (pCur.moveToNext()){
number = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
cI.number =number;
}
data.add(cI);
pCur.close();
}
}
}
cur.close();
return data;
}
Thank you!

What you need to understand is that currently, you have 1 contactInfo object and you are adding this contactInfo object into the data (arraylist).
Let's take it down one step at a time. This is what the code is currently doing:
Create a contactInfo object (Let's name it C1).
Set the name as "abc" and number as "123".
Add this C1 into data arraylist. --> Essentially the arraylist is holding a reference to where this C1 resides in the memory (i.e. it is only pointing to C1)
Cursor founds another contact and set the name of C1 to "def" and number to "456"
Add this C1 into data arraylist. (At this point of time, what the arraylist contains is actually 2 references, both pointing to C1)
Assuming the cursor finds nothing next and finally returns the data arraylist.
You will then get an arraylist that contains 2 records and both consists of contact name "def" and number "456".
Instead, what I would recommend based on your code is to simply add 1 more line to create a new contactInfo object each time you find a new contact, before setting the name.
private ArrayList<contactInfo> displayContact(){
ContentResolver cr = getContentResolver();
Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI,null,null,null,null);
String name ;
String number;
String id;
contactInfo cI; //Remove the creation of cI object at the start
if(cur.getCount()>1){
while (cur.moveToNext()){
id = cur.getString(cur.getColumnIndex(ContactsContract.Contacts._ID));
name = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
if(Integer.parseInt(cur.getString(cur.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)))>0){
cI = new contactInfo(); //Add in this to create a new cI object when there's a new contact
cI.name = name;
Cursor pCur = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = ?",new String[]{id},null);
while (pCur.moveToNext()){
number = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
cI.number =number;
}
data.add(cI);
pCur.close();
}
}
}
cur.close();
return data;
}
And you might want to optimize this code a little by having it fetch the phone numbers together with the names as the current way will take a fair bit of time if the data is huge. You can have a look at this post: https://stackoverflow.com/a/28690030/3717990

Related

Insert CSV using Apex Batch Class Salesforce for OpportunityLineItem

I want to add a button to my opportunity header record that is called Insert Products. This will send the opportunity ID to a visualforce page which will have a select file button and an insert button that will loop through the CSV and insert the records to the related opportunity.
This is for non technical users so using Data loader is not an option.
I got this working using standard apex class however hit a limit when i load over 1,000 records (which would happen regularly).
I need to convert this to a batch process however am not sure how to do this.
Any one able to point me in the right direction? I understand a batch should have a start, execute and finish. However i am not sure where i should split the csv and where to read and load?
I found this link which i could not work out how to translate into my requirements: http://developer.financialforce.com/customizations/importing-large-csv-files-via-batch-apex/
Here is the code i have for the standard apex class which works.
public class importOppLinesController {
public List<OpportunityLineItem> oLiObj {get;set;}
public String recOppId {
get;
// *** setter is NOT being called ***
set {
recOppId = value;
System.debug('value: '+value);
}
}
public Blob csvFileBody{get;set;}
public string csvAsString{get;set;}
public String[] csvFileLines{get;set;}
public List<OpportunityLineItem> oppLine{get;set;}
public importOppLinesController(){
csvFileLines = new String[]{};
oppLine = New List<OpportunityLineItem>();
}
public void importCSVFile(){
PricebookEntry pbeId;
String unitPrice = '';
try{
csvAsString = csvFileBody.toString();
csvFileLines = csvAsString.split('\n');
for(Integer i=1;i<csvFileLines.size();i++){
OpportunityLineItem oLiObj = new OpportunityLineItem() ;
string[] csvRecordData = csvFileLines[i].split(',');
String pbeCode = csvRecordData[0];
pbeId = [SELECT Id FROM PricebookEntry WHERE ProductCode = :pbeCode AND Pricebook2Id = 'xxxx HardCodedValue xxxx'][0];
oLiObj.PricebookEntryId = pbeId.Id;
oLiObj.Quantity = Decimal.valueOf(csvRecordData[1]) ;
unitPrice = String.valueOf(csvRecordData[2]);
oLiObj.UnitPrice = Decimal.valueOf(unitPrice);
oLiObj.OpportunityId = 'recOppId';;
insert (oLiObj);
}
}
catch (Exception e)
{
ApexPages.Message errorMessage = new ApexPages.Message(ApexPages.severity.ERROR, e + ' - ' + unitPrice);
ApexPages.addMessage(errorMessage);
}
}
}
First problem that I can sense is that the insert DML statement is inside FOR-loop. Can you put the new "oLiObj" into a List that is declared before the FOR-loop starts and then try inserting the list after the FOR-loop ?
It should bring some more sanity in your code.

Unable to cast object of type '<>f__AnonymousType0`2[System.String,System.Int32]' to type 'System.IConvertible'

I am trying to populate a data list box to text box on list box's click event but I found this error
Additional information: Unable to cast object of type '<>f__AnonymousType0`2[System.String,System.Int32]' to type 'System.IConvertible'
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
StudenRecordDataContext std = new StudentRecordDataContext();
int selectedValue = Convert.ToInt32(listBox1.SelectedValue);
StudentRecord sr = std.StudentRecords.Single(s =>s.ID==selectedValue);
txtId.Text = sr.ID.ToString();
txtName.Text = sr.Name;
txtPassword.Text = sr.Password;
txtCnic.Text = sr.CNIC;
txtEmail.Text = sr.Email;
}
I think the error is on line StudentRecord sr = std.StudentRecords.Single(s =>s.ID==selectedValue);
Where does that error come from and what do I need to change to fix that error?
I'm sorry to say so but you provided us with the wrong diagnosis of the line your program fails.
The culprit is this line:
int selectedValue = Convert.ToInt32(listBox1.SelectedValue);
I expect you have earlier populated that listbox1 with a collection from StudentRecords coming from an instance of your StudentRecordDataContext.
If you select a value from the listbox the SelectedValue holds the object you added to the items collection (or indirectly by setting the DataSource property).
To fix your code you could first make sure the object becomes a StudentRecord again. That is not that easy because you created an anonymous type, I expect something like:
listbox1.DataSource = new StudentRecordDataContext()
.StudentRecords
.Select(sr => new { Name = sr.Name, ID = sr.ID });
When you try to retrieve the SelectedValue you get that anonymous type, not something that is strongly typed.
Instead of adding an anonymous type, create a new class that has the properties for the Name and the Id:
class StudentRecordItem
{
public string Name {get; set;}
public int ID {get; set;}
}
When you populate the Datasource create StudentRecordItem classes for each record and add those to the datasource.
listbox1.DataSource = new StudentRecordDataContext()
.StudentRecords
.Select(sr => new StudentRecordItem { Name = sr.Name, ID = sr.ID });
The your code can become something like this:
StudentRecordItem selectedStudent = listBox1.SelectedValue as StudentRecordItem;
if (selectedStudent == null)
{
MessageBox.Show("No student record");
return;
}
int selectedValue = selectedStudent.ID;
You don't need the Convert.ToInt32 because I assume ID is already an int.
Remember that the debugger in Visual Studio shows the actual types and values of all your properties and variables. When a type conversion fails you can inspect there what the actual type is you're working with.

praogramatically fetch any particular object's related objects from its related list

I am pretty new to SFDC. I am trying to implement a clone functionality of a custom object by which when I am cloning an object, the object as well as all the object in its related list are to be cloned. I have implemented the part of cloning a object but stuck how to get the object list associated with a object's related list. pls let me know , how to implement this.
Thanks
You can try this...
public class PurchaseOrderCloneWithItemsController {
//added an instance varaible for the standard controller
private ApexPages.StandardController controller {get; set;}
// add the instance for the variables being passed by id on the url
private Purchase_Order__c po {get;set;}
// set the id of the record that is created -- ONLY USED BY THE TEST CLASS
public ID newRecordId {get;set;}
// initialize the controller
public PurchaseOrderCloneWithItemsController(ApexPages.StandardController controller) {
//initialize the stanrdard controller
this.controller = controller;
// load the current record
po = (Purchase_Order__c)controller.getRecord();
}
// method called from the VF's action attribute to clone the po
public PageReference cloneWithItems() {
// setup the save point for rollback
Savepoint sp = Database.setSavepoint();
Purchase_Order__c newPO;
try {
//copy the purchase order - ONLY INCLUDE THE FIELDS YOU WANT TO CLONE
po = [select Id, Name, Ship_To__c, PO_Number__c, Supplier__c, Supplier_Contact__c, Date_Needed__c, Status__c, Type_of_Purchase__c, Terms__c, Shipping__c, Discount__c from Purchase_Order__c where id = :po.id];
newPO = po.clone(false);
insert newPO;
// set the id of the new po created for testing
newRecordId = newPO.id;
// copy over the line items - ONLY INCLUDE THE FIELDS YOU WANT TO CLONE
List<Purchased_Item__c> items = new List<Purchased_Item__c>();
for (Purchased_Item__c pi : [Select p.Id, p.Unit_Price__c, p.Quantity__c, p.Memo__c, p.Description__c From Purchased_Item__c p where Purchase_Order__c = :po.id]) {
Purchased_Item__c newPI = pi.clone(false);
newPI.Purchase_Order__c = newPO.id;
items.add(newPI);
}
insert items;
} catch (Exception e){
// roll everything back in case of error
Database.rollback(sp);
ApexPages.addMessages(e);
return null;
}
return new PageReference('/'+newPO.id+'/e?retURL=%2F'+newPO.id);
}
Sounds like you need to "Deep Clone" - check out the links below for reference:
https://salesforce.stackexchange.com/questions/8493/deep-clone-parent-child-grand-child
http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_System_List_deepClone.htm

Java Email: Getting the name to print next to the email address

Using Java to send an email, how would I get the name to print next to email address, preferably in this style?:
Joe A. Blow <joe.a.blow#host.org>
I have my email lists in in a *.properties file with the data like this:
email_list_3 = Bob Smith<bob.smith#acme.com>,Jane Doe<jane.doe#acme.com, Betty Crocker<betty.crocker#acme.com
I'm using this function to return each email list in the properties file, like "email_list_3", as an array list:
private static ArrayList setEmailList(String email_list_name)throws Exception {
Properties props = new Properties();
ArrayList email_list = new ArrayList();
try {
props.load(MailSender.class.getResourceAsStream("/email_lists.properties"));
email_list.addAll(Arrays.asList(props.getProperty(email_list_name).split(",")));
}// end try
catch (Exception e) {
logger.error("Failure populating email list: " + email_list_name + "\n",e);
}
return email_list;
}// end function setEmailLists()
Next I will collect the email addresses ( Im assuming the names are still with them? ) into an array of InternetAddresses like this
ArrayList<String> recipientsArray = null;
ArrayList<InternetAddress> addressTo = new ArrayList<InternetAddress>();
recipientsArray = getAnArrayListOfEmailAddresses();
for (String tempAddress: recipientsArray) {
if (tempAddress != null) {
addressTo.add(new InternetAddress(tempAddress));
}
}
And this is how I would plug my addresses into a email message:
InternetAddress[] to = new InternetAddress[addressTo.size()];
to = addressTo.toArray(to);
// processed in a for loop
tempAddress = (to[i]).getAddress();
MimeMessage msg = null;
msg = new MimeMessage(session);
msg.setSubject(subject);
msg.setFrom(new InternetAddress(fromAddressStringl));
msg.setRecipients(Message.RecipientType.TO, tempAddress);
To me, right now, it looks like to get the names displayed next to the email addresses I have use the other constructor for InternetAddress, that takes two arguments, address and name.
Is that right or is there a setting I can change such that when Java parses the name + address combination items out of my file it will do it automagically?
Thanks much in advance
Steve
You're making this too complicated.
You have a bunch of InternetAddress objects, just use them directly to set the recipients:
InternetAddress[] to = new InternetAddress[addressTo.size()];
to = addressTo.toArray(to);
MimeMessage msg = new MimeMessage(session);
msg.setSubject(subject);
msg.setFrom(new InternetAddress(fromAddressStringl));
msg.setRecipients(Message.RecipientType.TO, to);
It's your use of getAddress() that separates the address field from the personal name field; don't do that.
Also, you can use the InternetAddress.parse() method to parse your property into an array of InternetAddress objects, rather than splitting on commas yourself.

How to write testcase for trigger?

I have a trigger which copies the billing street address from the related Account to the other streets address on the related Contact. I wrote this trigger from reading materials online is this correct? Is there better way to write it?
Public class iTestClass {
public static testmethod void test()
{
Contact objContact1;
Contact objContact2;
objContact1 = New Contact();
objContact1.OtherStreet = '123 lane';
objContact1.OtherCity = 'Washington';
objContact1.OtherState = 'OR';
objContact1.OtherCountry = 'USA';
objContact1.OtherPostalCode = '12122';
objContact2 = New Contact();
objContact2.OtherStreet = '232 st.';
objContact2.OtherCity = 'cleveland';
objContact2.OtherState = 'OH';
objContact2.OtherCountry = 'USA';
objContact2.OtherPostalCode = '166030';
}
}
You're on the right lines, but a) you're not inserting the contact records, and b) you need to insert an account first, then set the account ID on those contacts before you insert them.
// before creating the contacts create an account
Account sAcct = new Account();
sAcct.Name = 'Test Account';
sAcct.BillingStreet = '1 Some Street'; // I forget the name of the field!
// etc.
insert sAcct;
// then on your contacts do this:
objContact1.Account = sAcct.Id;
// then insert them at the end to fire the trigger
insert objContact1;

Resources