MyContacts

Introduction
Classes

Introduction

The app MyContacts is modeled after the app "Contacts" that comes with standard with an Android phone. It is intended as a sample app to demonstrate Android programming skills but also contains several upgrades to the standard app.

Standard capabilities:
- Import contacts from Android database.
- Can display multiple phone numbers for each contact - along with the type of phone for each number. (Work, home, etc.)
- Fast indexing to allow for easy scrolling through list of contacts.
- Allows contacts to be placed in groups for sorting.
- Ability to store notes, address, email & organization for each contact.
- Uses standard Android phone and texting capabilities.

Upgrades from standard capabilities:
- Ability for user to delete groups.
The app checks that any contacts in that group are already included in at least one other group, if not it puts the contact in the "Not Assigned" group.)
- Ability to sort the contacts by date entered.
- Ability to display multiple groups of contacts in the same list.
- Ability for user to delete groups. (The app checks that any contacts in that group are already included in at least one other group, if not it puts the contact in the "Not Assigned" group.)

Classes

AddContactFragment (AddContactActivity)
AddDialogFragment
CategoriesFragment (CategoriesActivity)
ChooseFieldFragment
ContactFragment (ContactAcitivy)
ContactsListFragment (ContactListActivity)
CursorAdapterDelCategories
CursorAdapterUserGroups
DeleteCategoriesFragment
DetailCursorAdapter
EditContactFragment (EditContactActivity)
MyContacts - Main / Launcher
MyContactsDbAdapter
MyListAdapter (Found in ContactsListFragment file)
SingleFragmentActivity
UserGroupsFragment
Note: All Activity classes are subclasses of SingleFragmentActivity and do nothing besides handling of the Fragment.


MyContacts
Called by: Launcher

onCreate(...)
This method starts ContactListActivity which hosts the
ContactsListFragment.

MyContactsDbAdapter

This class handles all the calls to the database.

MycontactsDbAdapter (Constructor)
Called by: ContactsListFragment

Constructor(Context appContext)

- The signature receives the Context and assigns it to mAppContect.

- A new DatabaseHelper (sdk) is created using the Context passed in and assigned to the local variable mDbHelper.

- .getWritableDatabase is then called on the just created DatabseHelper (mDbHelper) to create a new SQLiteDatabase member variable mDb. - The member variable 'open' is set to "Yes"

Methods:
addBlankNumber(int primaryRow) - Adds a blank number for the contact currently being displayed.
addCategories(...) - Adds a list of default categories (groups) the user can assign a contact to.
addContact(...) - Takes in the information for a new contact and updates both tables.
addNewCategory(...) - Adds a new category (group) that contacts can be placed into.
addTempCategories() - Same as addCategories() except the "ContactID" in the "categories" table is set to '-3'(whichc is used when user is choosing which category to display)
addTempContact() Creates a new row in the db when AddContact is first started.
addTempName(...) - Adds a row to the contacts db with the current value of the name EditText box - and adds this row number to the primary row for the contact.
addTempNumber() - Adds a new row in the db when user clicks + for add a new number.
addToNotAssigned(int id) Add the contact to the "categories" table in the group "Not Assigned". Used during the sync process.
addTypes() Sets up the "Types" table used to convert the Phone app int to a type of phone.
checkName(String name) - Checks to see if the name entered is already in the db "contacts" table.
clearTemps() Clear all the rows in the contacts table with "Temp" = "Yes"
copyCategory(...)Adds a rows necessary for a new category - called when copying old db.
copyContactsFromCategories(...) Used with ReadOldDb delete(int row) - Deletes the contact with the given row number.
deleteCategories() - Deletes the categories the user choose to be deleted from DeleteCategoriesFragment
deleteNumber(int row) Deletes both the temp row and the original row for a phone number & type.
deselectAllCategories() - Marks all categories to be not shown.
fixedInputs() - When the option is chosen by the user (from the ContactListFragment )the method enters sample contacts into the database - used for testing and demonstrations.
getAllNumbers(int row) Provides all the numbers associated with a given contact and "Temp" = "No".
getAllTempNumbers(...) Provides all the numbers associated with a given contact and "Temp" = "Yes".
getCategories() - Provides a list of the categories (groups) available that a user can place a contact in.
getContact(String row) - Provides all the information for a single contact.
getContactRow(String row) Used to retrieve the field information from a contact - does not provide the category information.
getListOfContactsAlpha() Provides the list of contact, sorted by name, to be displayed given the current categories the user has choose to display. It also provides the list of contacts.
getListOfContactsDateTemp() Provides the list of contact, sorted by date, to be displayed given the current categories the user has choose to display. It also provides the list of contacts.
getNoOfCategories()
getShowCat(int row, String category)Was used by CursorAdapterUserGroups which was deleted
getShowCat1(String category) Returns the value of the "Show" category in categories table where "ContactID" = -4 and "Category" equals the inputted value.
getShowCat2(String category)Returns a Cursor with all the categories where "ContactID" = -4 and "Show" = "Yes".
getSortType()
getTempFields(String row) Provides all the information from the temp row where the primary row is input.
getTempFields
getTempName(String origRow)
getTempName1(String row) - Used by AddContact.setupRepeat(...), requires different steps then in getTempName(String row)
getTempRow(String tempRow)
inputx() - Thee three classes are used by fixedInputs() to enter to sample contacts into the database.
markCatDel(...) - Marks a category for deletion. Used by CategoriesFragment.
markCatShow(...)
printTables() selectAllCategories() - Marks all categories to be shown.
setCategories(String row)
Sets the only entry in this table to "Alpha" or "Date"
sync()
syncCheckNames() Insures all the names copied from the Phone app are in a group, if not assigns them to "Not Assigned".
syncGroups(Cursor c)
syncNames(Cursor c)
transferOtherCategories(...) Used by ReadOldDb
update(...) - Takes in the user input and updates a contact's information.
updateCategories - Used by CategoriesFragment when the user presses the "Apply" button.
updateField(String row, String field, String value)
updateName(String row, String name)
updateNumber(String primaryId, String row, String type, String number)
updateNumberTemp(String id, String type, String number, String name)
updateNumberTemp1(String id, String PrimaryID, String type, String number)
updatePhoneGroups
updateSingleCategory Used by Categories when the user checks or unchecks a box.
updateTempField(...)
updateTempField1(...)
updateTempFieldFlag(String row, String hasField, String yes/no)Used to update the HasField column with "Yes" or "No".
updateTempFieldFlag1(...) Similar to updateTempFieldFlag(...) except it takes in the temp row instead of the original row.
updateTempName(String getId, String newName)
updateTempName1(String id, String name)

Inner class DatabaseHelper

addBlankNumber( int primaryId
called by: EditContactFragment.addNumber(View view)
AddContactFragment.setupNew(View v)

This method adds a row to the "contacts" table. The row will have the "Type" column set to the default of "Mobile", the "Number" column set to "", and the PrimaryID set to the "id" that was passed in. In addition, "Temp" & "New" are set to "Yes", while "IsPrimary" is set to "No"

addCategories()
Called by: MyContacts.onCreate(...)

This method is used when the app is first used to set the defaults of which categories of contacts will be displayed. The default is for all the categories to be displayed.

The first step is to assign the class SQLiteDatabase object created in .open to a local variable so that the class variable does not have to be recalled for each step.

A ContentValues object (cv) is created and for each category .put is called on that object to assign the "Category" name, "ContactID" = -2, and "Show" = "Yes".

SQLiteObject (db) .insert is then used with the ContentValues.

addContact(String id, string name)
Called by: AddContact.save(View view)

This method takes in the "_id" for the primary row of the contact and changes "Temp" to "No" and also updates the "Name".

addNewCategory(String input) y
Called by: EditContactFragment.add(String input), setGroups()

This method takes in the String the user inputted for the new group and makes two new entries into the "Categories" table. One where the "ContactID" = '-2' (groups that are available) and the other "ContactID" = '-3' (temporary listing of groups while user chooses which ones are to be displayed.) It also does the same for any group that was found when syncing with the Phone app.

addTempCategories()
Called by: MyContacts.onCreate(...)

This method is almost the same as addCategories() except the "ContactID" is set to '-3'. These rows are used when the user is selecting which categories to display. (see CategoriesFragment)

addTempContact
Called by: AddContact.setupNew(View v)

Adds a new row to the contacts table. "IsPrimary" & "Temp" are both set to "Yes". "Temp" will be changed to "No" if use clicks on "Save". The phone number and type of phone will also be added at this time.

The row number for this contact is returned as an int.

addTempName(String Name, String origId)
Called by: EditContact.addNumber(...)

This method is used to keep track of the current state of the EditText box for the contact name.

The first step is to insert a new row with the name from the primary row along with the flags from the "HasField" columns. The insert returns the row number. This row number is then used to update the original contact where _id is the row number passed in and "Temp_id" is set to the new row number.

addTempNumber
Called by: EditContactFragment

It is used by EditContact.onCreateView(...) to create a new row in the contacts db for each number for the contact. In each row "Temp" = "Yes", "New" = "No", & orig_Id is set to the id of the original entry.

addToNotAssigned(int id)
Called by: syncCheckNames()

This method takes in the contact "_id" and adds a row with the "category" = "Not Assigned" and the "ContactID" equal to the inputted row number converted to a String.

addTypes()
Called by: ContactListFragment

Used to convert the Phone app int to a type of phone. Used when syncing is the Phone app.

checkName(String name)
Called by: AddContact.save(...)
EditContact.save(...)

This method queries the "contacts" table for the column "Name" where "Name" = the String passed into the method. It then checks the Cursor with .getCount() and if the result is > 0 the String to be returned is set to "Yes".

clearTemps()
Called by: EditcontactsFragment.afterDelete(int position)
EditContactsFragment.addNumber(View view)

This method simply deletes any row in the contacts table that has column "Temp" = "Yes".

copyCategory()
Called by: ReadOldDb.copyCategories()

This method simply inserts three new rows into the Categories table. One row each with a ContactID = -2, -3, -4. (-2 is used to list all the categories, -3 & -4 are used to check which categories the user has shown to show.) The Category along with the values for Show where ContactID = -3 and -4 are passed into the method. A separate insert is used for each row.

copyContactsFromCategories(...)
Called by: ReadOldDb.copyPreviousDb()

This method takes in all the information for a primary contact and inserts a row into the Contacts table. The insert returns the new row number. This row number, along with the Category value that was passed in, is then used to insert new row into the Category table. The row number from the Contact table is returned to the calling method.

delete(int row)
Called by: ContactFragment - contact_menu id = Delete

This method takes in the int row number of the contact to be deleted that was passed in and converts it to a String. It then uses the DatabaseHelper object (mDb) created in .open() and calls .delete(...) on that object.

mDb.delete(TABLE_NAME , ID + "=?" , new String[] {rowS}") ;
TABLE_NAME = "CONTACT
ID = "_id" - column name to search
{rowS} value (String) to search for.

deleteCategories()
Called by DeleteCategoriesFragment

This method deletes the groups the user has selected to be deleted - The "DeleteThis" column has been set to "Yes" and the "ContactID" = '-3".

A "while" loop then goes through each of the categories (groups) found.

For each category found a new query is created and this query is used to find any contacts that are in that group. A second "while" loop then goes this query and for each one a third "while" loop (1 row only, used to set the Cursor to the correct position) changes all contacts in the group to be deleted to "Not Assigned".

After the second and third "while" loops are closed the category is deleted from the database.

deleteNumber(int row)
Called by: EditContactdeleteNumber(int count), AddContact.deleteNumber(int count)

This method deletes both the temp row and the non-temp row for the number. The method queries the "contacts" table to retrieve the Orig_id from the temp row. The temp row and the value for "Orig_id" are both converted to Strings and these values are then used in Database.delete(...) method. If this method is used during AddContact there will be no non-temp rows in which case only the temp row is deleted but no errors occur looking for the non-temp row.

deslectAllCategories()
Called by:
CategoriesFragment
The methods sets the column "Show" to "No" in all rows where the "ContactID" = '-3'. This is used when the user is selecting which categories to display.

fixedInputs()
Called by: MyContacts.onCreate(...)

The method simply calls three different inputx() methods. (These methods are used to put some sample contacts into the database for testing purposes. It uses ContentValues along with SQLiteDatabase.insert(...) to add the data. In addition it uses the return of this call - the row into which the data was inserted to update the categories table.

getAllNumbers(int row)
Called by: ContactFragment
EditContactFragment

This method selects the value from the "Name" column in the "Contacts" table where "_id" or "PrimaryID" = the row passed in. It also takes in whether the "Temp" column should be "Yes" or "No". It then queries the db to find the name associated with the "_id" and then uses that name to retrieve all the rows that have that name and where "Temp" equals the value of "Yes" that was passed in.

getAllTempNumbers()
Called by:
EditContactFragment
AddContact.setupNew(...), AddContact.setupRepeat(...), AddContact.save1(...)

This method simply returns all rows from the contacts db where "Temp" = "Yes" and "IsPrimary" = "No".

getCategories()
Called by: MyContacts.onCreate(...), EditContactFragment, AddContactFragment

This method retrieves the list of groups that a contact can be assigned to. It insures that the rows that are used when the user selects which groups to delete are all set so that the "DeleteThis" column = "No" when the user first starts that display.

getContact(String rowS)
Called by: CategoriesFragment,     EditContactFragment

This method returns an array of 2 Cursor objects. Cursor[0] is the result of a query on the "contacts" table that provide the details of the contact (Name, Number, Type of phone) given the row number of the contact. Cursor[1] in the array is a result of a query on the "categories" table and provide the group(s) the contact is in.

getContactRow(String row)
Called by: ChooseField

This method simply returns all the categories from the input row.

getListOfContacts_alpha()
Called by: ContactsList.onCreate(...)

This method provides the list of contacts, sorted by name, to be displayed given the current categories the user has choose to display. It also provides the list of contacts.

The first step is to create a Cursor[] that will have to elements: A Cursor that will the list of categories and one that will hold the list of contacts from the contacts table that will be displayed.

The first step is to perform a query to determine which categories to display. This is done by selecting all the rows in the table "Categories" where "Show" = "Yes" and ContactId = -2.

Next, the first part of the query is formed which will be used for a query that will take "ContactID" from the rows in the "Categories" table where the input "Categories" are found and return the rows from the "Contacts" table where the "_id" matches the "ContactID" in the "Categories" table. The addition to the query of which "Categories" to search for is provided in the next section.

The next section is a while loop that cycles through the Cursor (c) of the first query which returned the list of "Categories" For each "Category" that is found it adds that category to the query.

The last thing that is added to the query statement is "group by name nocase". After this statement the "SortType" table is updated so that if the app is restarted with the type of sort the user selected.

The final two steps in this block are the initiate the query for the statement just assembled and assign the results to cursorArray[0]. The query for the list of contacts is the repeated to reset that Cursor and the results sorted in cursorArray[1.]

Need to look at this when I come back to this section Sort by date modified
The first part of the else block that handles the sort by date is the same as the sort by name. The method gets a Cursor that list all the "Categories" by querying the contacts table for all rows where "Show" = "Yes" & "ContactID" = -2, this Cursor is used to update the query statment with all the "Categories" that will be searched for, the "SortType" table is updated with the type of sort that has been chosen, and "Group by name" is added to the query.

This is where the blocks for sort by date and name start to differ. First, the "CategoriesTemp" table is cleared so that there will be no confusion with this update. Than the Cursor is cycled through and each contact found is entered int the table "CategoriesTemp". Since the original query had the "Distinct" keyword this table will not contain the duplicates. (Note: Using "distinct" with date search did not work and there were duplicates.)

The same type of "Join" search as was done in the sort by name block is performed here, but this time it uses the "CategoriesTemp" table instead of the "Categories" table and it returns all the rows since only those "Categories" that are to be shown will be listed.

This block again repeats the query to provide the list of "categories" to be shown.

The last step in the method, after the if/else block is to return cursorArray.

getListOfContactsDateTemp() Provides the list of contact, sorted by date, to be displayed given the current categories the user has choose to display. It also provides the list of contacts.
getNoOfCategories
Called by: ContactListFragment

This method returns the value for the column "Category" in the contacts table for all rows where "_id" = '-2'.

getShowCat(int row, String category)
Called by: ContactList

getShowCat(int row, String category
Called by: CursorAdapterUserGroups
ContactList

This method is used when the user is choosing which categories (groups) a contact should be in. The current groups are shown with the CheckBox checked. For each category this method is called passing in the row number for the contact and the current category. If a row contains both the ContactID & the "Category" name, the Cursor will have a count > 0 and Boolean value of "true" is returned, if not "false" is returned.

getSortType()
Called by: ContactsListFragment.onCreateOptionsMenu(...)

This method simply queries the Sort table for the value of the SortType column where _id = 1 and returns this value. getTempFields(String tempRow)
Called by: EditContact.setupRepeat(...)

The temp row number is input and a Cursor is returned with all the columns from the given row.

getTempName(String row)
Called by: EditContact
The row number for the primary contact is input and the method returns the name from the temp row.

The first query selects the "Temp_id" from the row of the primary contact. This was input in addTempName(String name, String row).

getTempName1(String row)
Called by AddContact.setupRepeat(...)

This method returns a Cursor with the result of the "Name" column from the contacts table for the inputted row.

Called by: EditContact.setupRepeat(...)

inputx()
Called by: fixedInputs()

These methods input sample contacts into the database for testing and demonstration purposes. Each contact is inputted into the "contact" and the row number of that contact is returned. That row number is then used to update the "categories" database.

markCatDel(String category, String YesNo)
Called by: CursorAdapterDelCategories

This method takes in the category (group) that was clicked on by the user and updates the "DeleteThis" column to either "Yes" (when the box is clicked) or not (when the box is unclicked).

markCatShow(String cat, String Called by: CursorAdapterUserGroups

This method updates the "categories" table to mark the row where the "category" matches the category name passed in and ContactID = -3. The rows for the actual ContactID will be updated when setCategories(String row) is called.

selectAllCategories
Called by: CategoriesFragment
The methods sets the column "Show" to "Yes" in all rows where the "ContactID" = '-3'. This is usesd when the user is selecting which categories to display.

setCategories(String row)
Called by UserGroupsFragment.apply(View view)

This method is used to set which categories (groups) a contact belongs to.

The first step is to delete all the previous categories the user was in (this is easier than trying to compare which are different). It then selects all of the categories where "Show" has been set to "Yes" for id = -3 (the temp categories). Next, it loops through all the items in the Cursor object, and for each one adds a row to the "categories" table for that "category" and the "ContactID" that was passed into the method.

setGroups()
Called by: sync()

The purpose of this method is insure that all the groups in the Phone app .Groups table is listed in the MyContacts "categories" table.

The method queries Contacts.Contract.Groups.CONTENT_URI and retrieves .TITLE & ._ID. It then loops through the Cursor and for each loop queries the MyContact "categories" table with the name just retrieved. If there is a match it calls updatePhoneGroups(String name, String number). If there is not a match it calls updatePhoneGroups(...) & addNewCategory(String name). The method updatePhoneGroups update the MyContacts "phone_group_numbers" table which translates between the value stored in the Phone app db and the group name that is to be displayed. The method addNewCategory does the work of updating the "contacts" table with the proper group entries.

setSortTable
Called by: ContactsList.onCreateView(...)

This method simply inputs to the table "Sort" inserting into column "SortType" the String "Alpha". This is only done when the app first starts.

sync()
Called by: ContactList.sync()

The purpose of this method is for MyContacts to read in all the of Phone information. The method simply calls: syncNames(), setGroups(), syncGroups(), and syncCheckNames.

syncCheckNames()
Called by: sync()

This method is called after all the other methods of the sync process. It goes through all of the names now in the MyContact and insures that they are assigned to at least one group. The first step is to retrieve all of the rows in the "contacts" table. In each loop through that Cursor it checks that "Name" is not null, and if so retrieves the "_id" for that name. It then queries the "categories" table and if there are any rows in the returned Cursor nothing happens. If there are no rows then the contact "_id" is passed to addToNotAssigned(int id)

syncGroups()
Called by: sync()

The purpose of this method is to update the MyConctacts "categories" table after a sync with the Phone app so that the contacts will show up in the proper groups. The first step is to query to ContactsContract.data table. The first step in the loop through that query is to check if the value in ContactsContract.Data.MIMETYPE = GROUP_MIMETYPE. If not, nothing happens.

If the MIMETYPE = GROUP_MIMETYPE then the "contacts" table is queried to retrieve the _id that matches the name retrieved from the Phone app query.

Next, the "phone_group_numbers" table is queried to retrieve the group name that corresponds to the int that is stored in the Phone db representing the group name. The "categories" table is then queried to insure that this group and contact is not already listed. If not, the "categories" table is updated with this group and contact id and the columns set to the proper settings to show the contact.

syncNames()
Called by: sync()

Summary:
This method is the first step in updating the MyContacts app to the Android Contacts app. The method queries the Contacts app for the name, number and type. It then quries the MyContacts db to see if there is a match for the name and if so for the number. After the query of MyContacts if there was no match for the name, it inserts the name as a primary contact, retrieves the new row number from that insert and inserts the number and type with the returned row number for the "PrimaryID". If the name had already been inserted into the MyContacts db the loop through that db retrieves the "PrimaryID" and then in this section the number and type are inserted with that "PrimaryID" also inserted into this new row.

Details:
The Phone db is queried to retrieve the "DISPLAYNAME", "NUMBER", and "TYPE". The index numbers for the name & number are assigned to local variables for convenience.

The type of phone is stored in the Phone db as an int. The "Types" table of MyContacts is used to convert the int to a String for the type of phone. This value is stored in the local variable "type1". "type1" is set to "Other" if no other value is found.

The name, phone number, and type of phone for the current row are retrieved and assigned to local variables.

Next, the name from the Phone's database is used in a query of MyContacts's database to check if the name already exists. (The phone database has a separate row for each number associated with a name.) The method checks to see if there is a match by checking if Cursor(c1).getCount() is greater then 0.

- I didn't think there would ever be a count > 1, but there is which will make the loop repeat uncessarily. - need to do something to get out of the loop after the first one (and/or find out why there would be more then 1 match.)

- The cursor to see if the name exist in MyContacts db uses "select *..." when the only column needed is "primaryID".

If there is a match, the _id is retrieved and assigned to "primaryID" and "duplicate" is set to "Yes". Both "primaryID" and "duplicate" are local variable with the scope of the while loop going through the Phone db. They are reset on each loop through that Cursor.

In addition, if there is a match the contacts table is again queried to retrieve all of the numbers associated with this contact. If there is a match "duplicateNumber" is set to "Yes".

Select * is used when only tempNumber is needed.

This ends the loop through the MyContacts db.

If "duplicate" does equal "Yes" "PrimaryID" is used to set the "PrimaryID" column, "IsPrimary" is set to "No" and the "Name" and "Type" columns are set.

If "duplicate" equals "Yes" and "duplicateNumber" equals "Yes" nothing happens - the number is already in the database and is ignored. This is necessary when syncing to the app when there already are contacts listed.

If "duplicate" equals "Yes" and "duplicateNumber" equals "No" the number and type are inserted into the db with the "PrimaryID set to the value obtained above and "IsPrimary" is set to "No"

If "duplicate" does not equal "Yes" it is a new contact and the "contacts" table is first updated with the name of the contact and "IsPrimary" set to "Yes". The returned row number is then used to enter a new row for the phone number and type, in this case the "PrimaryID" is set to the returned row number from the previous entry and "IsPrimary" is set to "No";

transferOtherCategories(...)
Called by: ReadOldDb.
copyCategories()

This method simply inserts a new row into the Categories table with the values for the Category ContactId columns.

update(...)
Called by: EditContact.save(View view)

This method takes in the inputs for all of the columns and assigns them to local variables. The id is the value that will be searched for in the db and the rest of the columns for that row will be updated.

It then creates a ContentValues variable. For each variable it uses ContentValues (object).put(String ColumnName , String value).

It then calls on the SQLiteDatabase object, created in .open(), input the values.

Next, it uses the return of the input(...) method to update the "Categories" table with the category the user choose.

The final step is to close the SQLiteDatabase object.

Example:
mDb.update(table_name , cv , id + "=?" , new String[] {getId} );

table_name - the table name
cv - the ContentValues object
id + "=?" - id is a String that represents _id the column name for the index
getId - The string equivalent of of the row number - passed into the method.

updateCategories()
Called by: CategoriesFragment.apply()

This method queries the "category" table for all rows where "ContactID" = '-3' (the temp settings) and for each row updates the table so that the values of "Show" where "ContactID" = '-2' is the same as for '-3'.

updateField(String row, String field, String value)
Called by: EditContact.save(...)

This method simply updates the given column (field) in the given row (row) with the given value (value).

updateFieldStatus(String getId, String hasField, String value)
Called by: EditContact.save(...)

This method takes in the row of the primary contact, the "HasField" column to be updated, and the value ("Yes" or "No") and updates the given row.

updateName(String row, String number)
Called by: EditContactFragment.save1(View view)

This method takes in the row number (String) for the name that is being changed and the updated name. It changes the row number to a int and does a query to get the original name. It then does a query to select all the values from the column "_id" where the column "Name" has the original name. For each one of these it changes value of "Name" to the name passed in.

updateNumber(String primaryId, String row, String type, String number)
Called by:
EditContactFragment.save(View view)

The method takes in the primary row number, row number, type, and number of a number to be changed by EditContact and updates the "contacts" table. The primary row number is to update that row with current date for the DateModified column.

updateNumberTemp(String getId, String type , String number, String name)
Called by: EditcontactFragment.after_delete(...), generalUpdate(), AddContact.generalUpdate()

This method takes in the row number for the temp row and updates the number and type of phone.

updateNumberTemp1(String id, String primaryID, String type, String number)
a Called by: AddContact.save1(View v)

This method updates a row used for temp numbers in AddContact to non temp numbers. "Temp" & "New" are changed to "No", the "PrimaryID" is set to the "PrimaryID" of the contact, and "Type" & "Number" are updated to what the user has inputed.

updatePhoneGroups(String groupName, String number)
Called by: setGroups()

The purpose of this method is to populate the "phone_group_numbers" table which serves as a translation between the group name and the int that represents that group in the Phone app database.

updateSingleCategory(...)
Called by: DetailCursorAdapter.java

This method is used when the user is viewing the Activity Category. It toggles the "Show" column in the categories table for the given category from "Yes" to "No" or vice versa.

updateTempField(...)
Called by: EditContact.generalUpdate()

This method takes in the original primary contact row and finds the temp primary row. It then uses that to update both the "Field" and the "HasField" columns with the values passed in.

updateTempField1(...)
Called by: addContact.generalUpdate()

This method takes in the temp row instead of the primary row like update_temp_field. It then updates both the "Field" and the "HasField" columns with the values passed in.

updateTempFieldFlag(String row, String field, String yes_no)
Called by: ChooseField.addField(String type)

The first step in the method is to find the "Temp_id" using the primary id passed into the method. (This is a two step process, first the query, and the while loop to retrieve the answer.)

The resulting int is converted to a String and then, along with the field name and the flag ("Yes" or "No") the row is updated.

updateTempFieldFlag1(String row, String field, String yes_no)
Called by: ChooseField.addField(String type)

This method is called when AddContact calls ChooseField instead of EditContact. It takes in the temperary row number instead of the primary (since there is no primary row for AddContact).

Making a version1 of the method was the easiest method since ChooseField does not have the temporary row number.

updateTempName(String id, String name)
Called by: AddContact.updateNumber(...,) Editcontact.generalUpdate

This method takes in the "_id" (row number) of the primary contact and updates the "Name" to what is input to the method.

updateTempName1(String id, String name)
Called by: DatabaseHelper
Superclass: SQLiteOpenHelper (sdk)
Called by: MyContactsDbAdaper.open()

Notes:
- Extending SQLiteOpenHelper requires implementing two methods: 1) onCreate(...) & onUpgrade(...).
- Subclassing SQLiteOpenHelper provides the class with a SQLiteDatabase object.

The generic call to super:
SQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version)
is change to:
super(context, DB_NAME, null, DATABASE_VERSION)

The DQLDatabase.CursorFactor is not used so null is passed in.

MyContactsDbAdapter.onCreate(...)

A SQLiteDabase object is passed into this method. This object (db) is then used to pass in to .execSQL statements, both defined at the top of the outer class as static final String(s).

MyContactsDbAdapter.onUpgrade(...)

- An SQLiteDatabase object, along with the old & new version numbers are passed into this method.
- This method will only be invoked on database upgrades.
- A tag is set to log the fact that an upgrade in the database is occurring.
- .execSQL is call on the SQLiteDabase object that was passed into to "DROP TABLE IF EXISTS" xxx think I need a static statement for this.
- The last step is to call the inner class onCreate(...) method passing along the SQLiteDatabase object that was passed into this method courtesy of SQLiteOpenHelper.

ContactsListFragment
Hosted by: ContactsListActivity
Called by:
MyContacts.onCreate(...)
AddContactFragment.save1(...)
AddContactFragment.cancel(...)
ContactFragment - contact_menu - "Delete"
ContactFragment "Back Button"

Methods & Inner Classes
onBackPressed()
onCreate(...)
onCreateView(...)
onCreateOptionsMenu(...)
onOptionsItemSelected(...)
go(int i)
sync()
MyListAdapter (Inner class)
Sync (Inner class)

Activity method - onBackPressed()
Called by System when "Back Button" is pressed

This method restarts ContactsList - This way the list is updated with the new information. Otherwise when EditContact was used the list would initially show the information as it was before the update.

onCreate(...)

This method calls super.onCreate(...) in the first step and setHasOptionsMenu(true) as the last step (necessary for Fragments if it implements options menu.)

The method also checks to see if the arguments are null and if not assigns the value to String "mSortType" which lets the rest of the class know what the current sort type is.

onCreateView(...)

- Insurring that the tables have been populated with the necessary initial data
- Setting up the ProgressBar and the label for the type of sort
- Retrieve the Cursors for both the list of categories & names
- Prep title bar that states the categories listed
- Set up the list of names and and a fast scroll bar and listeners so each name can be clicedk on

- Insurring that the tables have been populated with the necessary initial data
There is some infomration that needs to be put into the database the first time the app is run or after it's data has been cleared. This information is the default categories (groups), the types of phones, and the intial type of sort (Date). To do this the method first calls MyDbAdapter.getNoOfCategories() which returns an int. If this value is < 1 the method call addCategories, addTempCategories(), addTypes(), & setSortTable() all in MyContactsDbAdapter.

- Setting up the ProgressBar and the label for the type of sort

R.Layout.contact_list is inflated and the resulting View object is used obtain a FrameLayout object based on R.id.progress_bar. This layout contains a ProgressBar widget so setting it visible or invisible will make the ProgressBar visible or not. At this point is is set to invisible.

The resulting View object is also used create a TextView object based on R.id.sort_types. "mSortTye" is evaluated and if it == "Alpha" the text is set to "Listed by name". If not it is set to "Listed by date modified".

Summary:
This method gets the names of the contacts that are members of the categories the user selected to be viewed and displays them. When a name is clicked the control goes to go(int i) passing along the id of the name clicked.

If there are no results - Cursor (c) . getCount() == 0 - the TextView empty is set to "No contacts listed"

Details:
A View object (v) is created using inflate.inflate(...)

Next, MyContactsDbAdapter. getListOfContacts() . That method returns a Cursor[] which contains two Cursor objects. The first is the result of a query of the categories table and provides which categories (of contacts) have been chosen by the user to be displayed. The results of that query is then used for the second query to get which contacts to display. The first query is also used to set a title for the list, which is why it is also returned.

When the Cursor[] is returned, Cursor c (the results of the categories table query) is set to value [0] and Cursor c1 (the results of the contacts table is set to [1].

The title for the list will depend on the number of categories the user has chosen to display:

For a single category the title will be:
"Contacts from categoryName"

For two categories it will be:
"Contacts from categoryName and CategoryName1"

For two categories but not all it will be:
"Contacts from categoryName, CategoryName1, and others"

And for all the contacts it will be:
"Contacts from all the categories"

Since the cursor has already been cycled through, moveToFirst() is called to reset it. Next, a While loop gets the name(s) of the first two categories that have been chosen to be displayed.

A TextView objec is then created for the widget "list_title" and setText(text) is called with the title that was just created.

If Cursor c1 (the list of contacts) has a count of 0 a TextView object for "empty" is created with the text "No contacts listed". If the count is not 0 this widget will not show up.

A MyListAdapter (extends CursorAdapter) object (adapter) is created to map a column (Name) from Cursor c1 to a TextView (contactName1) from the layout list_layout. Note, that in previous version a generic SimpleCursorAdapter was used, but this did not give the ability to have indexes.

The constructor for MyListAdapter is (Context context, int layout, Cursor c, String[] from, int[] to)
Context - getActivity()
int (layout) - R.layout.list_layout
Cursor - c1
String[] (dataFrom) - MyContactsDbAdapter.NAME
int[] (toWidgetName) - R.id.contactName1
/>
A ListView object (lv) is created from the widget list_view (from contact_list - inflated at top of method) and setAdapter(adapter) is called using the MyListAdapter just created. setOnItemClickListner is added to the ListView - it will call to .go(int i) when a name is clicked. .

The last step in the method is to return the View (v) that was inflated at the top of the method.



onCreateOptionsMenu

This method retrieves the MenuItem with the id of "sort" so that the title can be set. It also retreives the type of sort that is currently being done to display the contacts by calling MycontactsDbAdapter.getSort().

If the sort is currently done by name the title will be "Sort by date modified". If the sort is currently by date the title will be "Sort by name". Thus giving the user the option to change the type of sort that the contacts are listed by.

onOptionsItemSelected

case = "sort" "Sort By Date Modified" or "Sort By Name"

This method calls MyContactsDbAdapter.setSortType(String type) where "type" is either "Alpha" or "Date". "type" is determed by mSortType which is set in onCreateOptionsMenu.

case = "add" "Add Contact"

This case simply creates a new Intent object to start AddContact

case = "show_categories" "Show Categories"

This case creates a new Intent object to start the Categories activity. It also passes along the message "List" Categories looks at the message and behaves differently if the message is "Categories".

case = "case_sync" "Import Contacts"

This case simply calls the sync() method.

go(int i)
Called by: onCreateView(...)/ListView (lv).setOnClickListener(...)
This method simply starts ContactFragment (via ContactFragmentActivity) passing in the id of the contact clicked.

sync()
Called by: optionsMenu - sync

This method first sets the ProgressBar created in onCreateView(...) to visible, then creates a new Sync object and calls execute(...) on that object.

This technique works in putting up a spinner over the current list of contacts while the sync is taking place. Sync extends AsyncTask. Sync handles restarting the ContactsListActivity. When ContactsListActivity is restarted the ProgressBar (or rather the FrameLayout that holds it) is set to visible (View.VISIBLE). Note that it was not necessary to have any calls to check on when the sync was done, rathre the restarting of the Activity just needed to be done in the AsyncTask class.

MyListAdapter Inner class in ContactsListFragment
Called by ContactsListFragment.onCreateView(...)

Class in located in ContactsListFragment.
Extends CursorAdapter.
Replaced the use of a SimpleCursorAdapter so that letter indexes could be displayed prior to each new first letter found in list.

constructor

dd public MyListAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {...

layout = R.layout.list_layout
Cursor = List of names in contacts db
From = The columns in the database that the data is from. In this case MyContactsDbAdapter.NAME
To = The widget in the layout where the data is being displayed. In this case R.id.contactName1 from the layout list_layout
Note: list_layout also has the widget R.id.devider that holds the letter of the divider, but this in not included in the constuctor. Note: From and int are both arrays so it is possible to assign different data sources (columns from a Cursor) to different widgets in a layout, but this was not necessary here.

newView(...)
Called by: System. Method is required when subclassing CursorAdapter.

This method inflates the layout. In this case is also assigns the widgets to names in a new class (in this case called NameHolder) so that the reference needs to be continuously recalled. The last step in this method is to set a tag on the view with the name of the NameHolder object so that it can then be retrieved when needed.

bindView(...)
Called by: System. Method is required when subclassing CursorAdapter.

The first step is to create a new NameHolder object based on the tag that was set to the view. This simply holds the xml references so that they do not have to be continually recalled.

Next, the Boolean "needSeparator" is set to false and the int "position" is retrieved from the cursor with .getPosition().

The next step is to copy the Cursor information to a buffer. This done using Cursor (cursor).copyStringToBuffer(int columnName, CharArrayBuffer) where in this case, CharArraBuffer = NameHolder(holder).titleBuffer. If "position" == 0, this is the first item in the list so it will need an index label and so "needSeparator" is set to "true".

If "position" != 0, then the method needs to check if a separator is needed. The first step is to move the Cursor (cursor) back by -1. This is because the method is comparing the current entry to the one before it to see if a new index is needed.

After the Cursor is moved back by 1 the value is assigned to a second buffer mBuffer.

Both buffers are then truncated to a single character, converted to a String (raw characters do not a toUppderCase() method) and converted to uppercase (so that lowercase entries do not cause an extra index separator).

An if block then check if both buffers are > 0 and that they are not equal. If this is true then "needSeparator" is set to true.

The last step in the block is to move the Cursor (cursor) position back to where it was.

If "needSeparator" is true then the text is set for separator widget with .setText(buffer, first char, last char), and the visibility is set to View.VISIBLE. If it is not true then the visibility is set to View.GONE.

The last step is to set the text for the widget holding the full name.

Sync Sync Inner class to ContactsListFragment
Called by: sync()

This class extends AsyncTask (skd). The only method it invokes is doInBackground(...). In this method MyContactsDbAdapter.sync() is called and then the ContactsListActivity is restarted.

AddContactFragment
Hosted by: AddContactActivity
Called by: ContactsListFragment - menu - id = add - label = "Add Contact"

Methods
Member Variables
Database Notes

Member Variables
String mRowS - The row number for this contact.
String mLocation - Holds the name of the calling class. The code changes depending on if this this class was called by another class or called by itself to update the display.
String mPresetName - Holds the original contact name - this might be left over from EditContact, but will leave in for now because it doesn't seem to be causing a problem.
int[] mTempRows[] - Holds the "_id" of the rows in the contacts table for the name & numbers established by this class. It is used in addNumber(...) to update the table with any changes the user has made prior to updating the table.
ListView mListView - The ListView object is what attaches the CursorAdapter. It is created in both setupNew(...) & setupRepeat(...) and is used latter to cycle through the Listview children to retrieve the information from the various widgets.
View mView - Established in .onCreateView(...)

Methods
newInstance(...)
onCreate(...)
onCreateView(...)
setupNew(View v)
setupRepeat(View v)
addNumberCheck(View v)
addNumber(View v)
ChangeGroups(View v)
save(View v)
save1(View v)
cancel(View v)
delete()
afterDelete()
generalUpdate()
deleteNumber()

newInstance(String row)

This method takes in the row number (in String form) for the contact to be edited and attaches it to the argument bundle with the key MyContacts.EXTRA_CONTACT_ROW. The value will be retrieved in onCreate(...) and assigned to mIdS

The method also takes in the name of the calling class and attaches it to the argument bundle with the key MyContacts.EXTRA_CONTACT_LOCATION. The value will be retrieved in onCreate(...) and assigned to mLocation. The class behaves differently if it is being called by another class vs. called by itself to update the display.

onCreate(...)

This method simply retrieves the arguments discussed in newInstance(...)

onCreateView(...)

The first step in this method is to create a View object (v) by calling .inflate(...) on the LayoutInflater that was passed into the method. The view inflated is edit_contact. The View is assigned to the member variable mView

Next, the following 4 Button objects are created:
Label = "Save" ; object name = save ; id = save ; method called = save(View view)
Label = "Cancel" ; object name = cancel ; id = cancel ; method called = cancel(View view)
Label = "+" ; object name = addNumber ; id = add_number ; method called = addNumberCheck(View view)
Label = none - click anywhere in the "group_layout" layout ; id = group_layout ; method called = changeGroups(View view)

If mLocation = "AddContact" setupRepeat(View v) is called. If not setupNew(View v) is called. The major difference is that in setupNew(...) the rows in the db must be created.

setupNew(View v)
Called by: onCreateView(...)

The first step in this method is to call MyContactsDbAdapter.addTempContact(). This creates a new row for the contact and returns the row number. The row number is converted to a String and assigned to mRowS

The next step is to call MyContactsDbAdapter.addBlankNumber(String name, String row) The String "name" is just a place holder, not sure if it really necessary but put it in to avoid a problem with a null entry.

The method then calls MyContactsDbAdapter.getAllTempNumbers(). There will be only 1 number at this point, however, doing this way provides the information in a Cursor form. The Cursor is assigned to the local variable cTempNumbers.

cTempNumbers is then cycled through to establish mTempRows[] which holds the "_id" of the newly created contacts table row. mTempRows is used to see how many numbers are already associated with the contact.

A CursorAdapterPhonesEdit object is then created using the Cursor result from MyContactsDbAdapter.getAllTempNumbers() and the cursor adapater is set to a ListView object. The member variable mListView is then set equal to this object.

'this' is then passed to the newly created CursorAdapterPhonesEdit object so that 'this' AddContact will be available after delete is called.

The cursor adapter is then attached to a ListView object.

The rest of the method is all to display which group(s) is(are) the contact is a member of.

The categories are retrieved with a call to MyContactsDbAdapter.getContact(String row).

As the groups are looped through, after the first group, the method adds a , and a blank (" ") to the String that will list the groups. In addition, if there is more than one groups the label is changed from group to groups.

SetupRepeat(...)
Called by: onCreateView(...)

This method is called when the Activity has restarted itself as opposed to be calling by another Activity. The purpose is to reset the screen updating what was there before except for what was deleted or adding to prompt to redisplay.

Instead of calling adapter.addTempContact() & .addBlankNumber it calls MyContactsDbAdapter.getTempName1(String row)

It also populates the int[] mTempRows[] with the '_id' of each number that was retrieved from adapter.getAllTempNumbers(). The remainder of the method is the same as setupNew(...).

addNumberCheck(View v)
Called by: onCreateView(...)

The purpose of this number is to simply check that there are not already three numbers associated with this contact. (Causes problem with the display). It does this by check the length of mTempRows. If there are not already three rows addNumber(View v) is called. If there are already 3 numbers a Toast is shown alerting users to the problem.


addNumber(View v)
Called by: addNumberCheck(View v)

Besides adding a new row in the "contacts" db table, the main function of this method is to save all the user data to the db so that it is available when the display is redone. The first step is to save the information in the name EditText widget. This is done by first retrieving the information, checking that its length is not 0, and if it is reassigning "name" to mPresetName (this is really only necessary when this method has been called a second or third time, the first time PresetName will also be blank), and then calling MyContactsDbAdapter.updateTempName(String row, String name) where "row" is mRows

The second part of this method is to loop through all the Views in the ListView and for each one to retrieve the name in the EditText widget for the number and the String that the Spinner is pointing to and to use this information, along with the value of mTempsRows[i] to call MycontactsDbAdapter.updateNumberTemp(...)

The third part simply calls MyContactsDbAdapter.addBlankNumber(String row) passing along the row number for the primary contact mRows

The final step in this method is to restart this Activity.

changeGroups(View view)
Called by: Called by: onCreateView(...) - Group panel

The first part of this method is the same as for .addNumber(...) - It updates the name and phone information in the contacts tables in rows where "Temp" = "Yes". It then starts the UserGroupsFragment. When this Fragment is done, control is returned to AddContact.

save(View v)
Called by: onCreateView(...) - Button save

The purpose of this method is to check that the name is not blank and is not a duplicate of a name already in the db.

mPresetName was originally used in EditContact so that if the user accidently deleted the name it would go back to the original. This might not be necessary for AddContact but will leave in since things seem to be working.

The method gets the name entered in to the EditText widget (tvName) and assigns the value to "name". The first check is to see if "name" = = "mPresetName". If it does, it then checks to see if the length is = = 0. If it is then a Toast message is shown stating "The name is already being used". If it is not then save1(View v) is called.

If "name" does not equal "mPresetName" then the else statement is invoked. (Left over from EditContact, but seems to work). The first step in this else statment is to check if "name".length() = = 0, if it is, it is set to "mPresetName". If "name".length() is greater then 0 MyContactsDbAdapter.checkName(String name) is called to see if the name is already in use. This method returns String "Yes" or "No". If the string is "No" save1(View v, String name) is called to continue the process of saving the information for the new contact. If the String "found" = "Yes" a Toast is shown stating "The name is already being used."

If "name".length() is not greater then 0, then a Toast is shown stating: "Name cannot be blank."

save1(View view, String name)
Called by: addContactSave(View v)

The first step in this method is to call MyContactsDbAdapter.addContact(...), passing in the mRowS and "name". "name" was passed into this method. The type of phone & the number are also used in this call, but I do not believe those last two are necessary.

Next, MyContactsDbAdapter.getAllTempNumbers() is called to retrieve all the rows where "Temp" = "Yes" and "IsPrimary" = "No". ("IsPrimary" = "Yes") is for the row that contains the name of the contact, the other rows contain the phone info.

The method then loops through the resulting Cursor and for each row, retrieves the row "_id" and uses the count of the loop to retrieve the info from the EditText widget and the Spinner for the type of phone. It then uses these three pieces of information, along with the row number for the primary contact (mRowS to call MyContactsDbAdapter.updateNumberTemp1 to update these numbers in the db.

Finally, ContactsListFragment is restarted (via ContactListActivity).

cancel()
Called by: Cancel button

This method calls MyContactsDbAdapter.clearTemps() before restarting ContactListFragment

addPhoneNumbers(...)
Called by: setUpNew(...) & setUpRepeat(...).

This method handles each row of phone numbers displayed. It is necessary since a CursorAdapter would lose info on the text boxes.

afterDelete(int position)
Called by: CurosrAdapterPhonesEdit.deleteNumber(...)

This method is very similiar to EditContact.afterDelete(...). The method first retrieves the name from the EditText widget and calls MyContactsDbAdapter.updateTempName(String name). The method then loops through all the Views of the ListView (except for the position that was deleted) and retrieves the number and type of phone and then calls MyContactsDbAdapter.updateNumberTemp(...) to update temp db values prior to restarting AddContact.

generalUpdate
Called by: addNumber(...), changeGroups(...), and addField(...)

Whenever the screen needs to be updated, the current values for the user inputs need to be saved temporarily to the database so that they can be re-displayed after the update.

deleteNumber(int count)
Called by:
addPhoneNumbers(...) / mDeletButton[count].

The database row number for row clicked (count) is stored in mTempRows[count]. MyContactsDbAdapter.deleteNumber(int row) is called to delete this row.

The rest of this method simply takes care of updating the db with the information on the screen so that it can be re-displayed after the update.

AddContact Database Notes

- Initial setup - setupNew(...)
  o MyContactsDbAdapter.addTempContact() - "Temp", "New", & "IsPrimary" all set to "Yes" - no name at this point.

  o MyContactsDbAdapter.addBlankNumber() - "PrimaryId" is set to the id of the main contact, "Temp" & "New" are set to "Yes" and "IsPrimary" is set to "No".

ContactFragment
Called by
ContactsListFragment.go(int i)

Member Variables
Methods

Member Variables
mRowS - String version of "contacts" table row number. It is passed in, in newInstance(...) & onCreate(...)
mRowi - int version of "contacts" table row number. It is converted from mRowS in onCreate(...).

Methods
Activity.onBackupPressed()
newInstance(...)
onCreate(...)
onCreateView(...)
onCreateOptionsMenu(...)
onOptionsItemSelected(...)
callNumber(int number)
delete(...)
formatPhoneNumbers(...)
addField(String fieldName, String fieldValue, int count)
addPhoneNumbers(...)

Activity method - onBackupPressed()
Called by System when "Back Button" is pressed

This method restarts ContactsList - This way the list is updated with the new information. Otherwise when EditContact was used the list would initially show the information as it was before the update.

newInstance(int row)

This method takes in the row number of the contact to be displayed. It then puts that value in the arguments bundle with the key EXTRA_CONTACT_ID. See onCreate(...)

onCreate(...)

This method retrieves the row number (String) from the arguments and assigns it to member variable
mRowS. It then converts the String to an int and also assigns that value to member variable mRowi. In addition it calls setHasOptionsMenu(true) which tells the System to call onCreateOptionsMenu(...)

onCreateView(...) The first step is to inflate R.layout.contact.

Next the methods calls MyContactsDbAdapter.
clearTemps(). It does this in case any temporary entries (used during EditContact & AddContact) were not cleared by those Activities.

Then MyContactsDbAdapter.getAllNumbers(int row) is called. This method returns all the numbers (and type of phone) associated with a given contact.

Since using a ListView/CursorAdapter caused problems (I believe in making phone calls???), the display is set up calling a separate method in each loop through the while Cursor block. To prepare for this an array is created to hold each widget in the display and an array for the RelativeLayout that will hold the row.

A FrameLayout is created from the R.Layout.contact.xml file that was inflated at the beginning of this method. This layout serves as the placeholder for everything that goes into it and has the advantage of simply not showing up if there is nothing placed in it. (There should always be at least 1 number, but this works well for the fields dealt with later in this method.). A LinearLayout object (llNumbers) is then created that will hold all of the rows of numbers. Its id is set to mIdCount. All of the widgets will use this int as the id, this way a unique id is insured for each widget. After the id is set, mIdCount is increased by 1.

The local variable "count1" is used to keep track of the array indexes for each widget. For each row of phone numbers "count1" will be the same for each widget.

Next, is the while block that moves through the Cursor holding the phone information. The type and number are retrieved from the Cursor. This information along with a reference to the View that was inflated and the index for all the widget arrays is passed to addPhoneNumbers(...). addPhoneNumbers(...) is takes care of creating all the objects within the widget arrays along with the formatting of those widgets and the text. addPhoneNumbers(...) takes care of everything in the display below the LinearLayout.

The final step in the Cursor while block is to increase count1 by 1.

After setting that adapter to the ListView for the phone numbers getContact(String row) is called on the MyContactsDbAdapter object. The method returns a Cursor array. Element [0] is the result of a query on the "contacts" table that provide the details of the contact (Name, Number, Type of phone) given the row number of the contact. Element [1] in the array is a result of a query on the "categories" table and provide the group(s) the contact is in.

onCreateOptionsMenu(Menu menu)

This method inflates R.menu.contact, when a menu item is clicked onOptionsItemSelected(MenuItem item) Is brought up.

onOptionsItemSelected(MenuItem item)

case: edit

Starts EditContactFragment (via EditContactActivity) and passes along mRowS - which holds the row number selected.

case: delete

This case brings up and AlertDialog. Pressing the "Cancel" button simply dismisses the dialog. Pressing the "Delete" button calls the delete (int row) method which will delete the contact being displayed and then return to the list of contacts.

callNumber(int number)
Called by:addPhoneNumbers(...) mCallButton[count]

In this method the number is retreived from the Textview (mEtNumber) and assigned to the CharSequence "Phone". Next a new Intent object is called and setData(...) is called on this intent. The last step is to call startActivit(Intent intent) on the Context object. (In this case the Context object was retrieved using getActivity().

formatPhoneNumbers(...)
Called by: addPhoneNumbers(...)

This method simply returns the results of return PhoneNumberUtils.formatNumber(number)

addField(View view)
Called by: Contact.onCreateview(...) Button - addFieldbutton

The method calls ChooseField passing along the original row number for this contact.

addField(View v, String field, String value, int count)
Called by: onCreateView(...)

Layout:
contact.xml onCreateView(...)
    FrameLayout fl - onCreateView(...)
       LinearLayout llFrame - onCreateView(...)

          The following is repeated for each field in addField (layoutSignature).
          LinearLayout llField
             LinearLayout llLabel
                TextView tvSpace - for looks
                TextView tvLabel - hold the field title
             LinaearLayout llEdit
                TextView tvSpace1 - for looks
                TextView tvValue] - Hold he value for the field

The purpose of this method is to setup the portion of the display for that each additional field requires. (The same layout cannot be reused multiple times and a Cursor Adapter does not work because of the way the db is setup. However, Cursor Adapter seems to have the downside of losing information when a EditText goes out of view.)

Delete(int row)
Called by: menu - delete

This method calls ...Adapter.delete(int) on that object passing in the row number to be deleted.

addPhoneNumber(...)
Called by: onCreateView(...)

This method is called by the Cursor while block going through all the phone numbers of a contact. It lays out the display for everything contained in a LinearLayout, which itself is contained in a FrameLayout.
- The FrameLayout is in the contact.xml file.
- The LinearLayout is created in the calling method.
- The arrays are also created in the calling method.
- The widgets that the arrays contained are created in this method.
- The array index "count" will be the same for each type of object created in this method. "count" is passed into this method and is increased by 1 each time this method is called.
- mIdCount is used to set the id for each widget and is increased by 1 every time a new widget object is created.

R.layout.contact.xml - inflated in onCreateView(...)
    FrameLayout flNumbers - R.id.phone_list - created in onCreateView(...)
       LinearLayout llNumbers - created in onCreateView(...)
          RelativeLayout - mllrow[count] - holds each row
             Button - mMessageButton[count] - calls startText(int countTemp)
                o Note: countTemp is just the count number passed into this method, converted to final
                o Uses LayoutPararms ALIGN_PARENT_RIGHT & ALIGN_PARENT_TOP
             Button - mCallButton[count] - calls callNumber(int countTemp)
                o Note: countTemp is just the count number passed into this method, converted to final
                o Uses LayoutParams LEFT_OF, mMessageButton[count].getId()
                o Symbol for button is R.drawable.phone_black
             Textview mType[count] - displays type of phone (home, work, etc)
                o Uses LayoutParams align_parent_left & align_top, mCallButton[count]
             TextView mTvNumber[count] - displays the phone number
                o Uses LayoutParams align_bottom, mCallbutton[count]                

This method lays out view for each row container the type of phone, the phone number, and a button that makes the phone call.

startText(int count)
Called by: addPhoneNumbers(...)/mMessageButton[count]

This message takes in the index number for the array of phone numbers and then uses that number to retrieve the number listed in mEtNumber[number]. It then creates a new intent Intent intent = new Intent(Intent.ACTION_VIEW) ; , and calls setData(Uri.parse("sms:" + phone)) on that Intent. Note "phone" is the charSequence retrieved in the first step of the method. It then retrieves the Contact by calling getActivity() and then uses that context to call startActivity(Intent intent).

EditContactFragment
Hosted by: EditContactActivity
Called by; ContactFragment.onOptionsItemSelected(MenuItem item)

Member Variables
Methods

Member Variables
- String mIdS - row number of contact from contacts table - passed in with the arguments bundle - Used in onCreateView(...) to call MyContactsDbAdapter.getcontact
- String mName - Original name for this contact - set in onCreateView(...) from Cursor from MyContactsDbAdapter.getcontact(...).
- String mLocation - Hold the name of the calling activity. The program responds differently if the class calls itself to update the display or if it was started from another activity. If if calls itself it will be set to "EditContact".
- String
mPresetName - Retrieved from Cursor object that holds the contact info - Used when updating the display (addNumber(...) & delete(...)) if the name is blank it goes back to this original value
- String mName - Retrieved from Cursor object that holds the contact info - not sure where it is used.

- int mFieldBox Field is the place holder for the name of the field (Address, Note, etc) - This object holds the number assigned to this field, so that it can later be retrieved.
- int mTempNameRow - Holds the row number for the db entry holding the temporary information for the name - Established in .setupNew(...) and used in .addNumber(...)
- int mIdCount Used to set the ID for every Widget that needs a unique id. (Keeps data from disappearing when system restarts an Activity.
- int
mNumbersCount Set in setupNew(...) & setupRepeat(...) based on the Cursor.getCount() from .getAllTempNumbers(). It is then used in the updates to know how many numbers to cycle through.

- int[]
mRows - Holds the "_id" for each row of phone numbers - filled when looping through a Cursor result from adapter.getAllNumbers(String row, String temp) - CursorAdapter needs this for delete.
- int[] mTemp Rows - Holds the "_id" for each of the temp rows - used when EditContact updates itself.

- EditText[] mEtField - Used so that each EditText field holding the value for that field can have a unique identifier.
- EditText[] mLlRow - Used so that the LinearLayout holding each row of phone information (type, number, deleteButton) can have a unique identifier.
- EditText[] mEtNumber, Spinner[] mSpinner, & Button[] mDeleteButton are also used in this same manner.

- View mView - Set equal to the View in onCreateView(...). Used so other methods can retrieve widgets from the layout without having to pass the View along.

Note: The need for mListView went away when the class changed from using a cursor adapter to laying out the display programmatically.

Methods
newInstance(...)
onCreate(...)
onCreateView(...)
setupNew(...)
setupRepeat(...)
save(...)
save1(...)
cancel()
addNumberCheck(...)
addNumber(...)
deleteNumber(int count)
changeGroups(...)
addField(View view)
addFieldLayout(String fieldName, String fieldValue, int count)
addPhoneNumbers(...)
generalUpdate()

newInstance(String row)

This method takes in the row number (in String form) for the contact to be edited and attaches it to the argument bundle with the key MyContacts.EXTRA_CONTACT_ROW. The value will be retrieved in onCreate(...) and assigned to mIdS

The method also takes in the name of the calling class. The value will be retrieved in onCreate(...) and assigned to mLocation. The class behaves differently if it is being called by another class vs. called by itself to update the display.

onCreate(...)

This method simply retrieves the arguments discussed in newInstance(...)

onCreateView(...)

The first step in this method is to create a View object (v) by calling .inflate(...) on the LayoutInflater that was passed into the method. The view inflated is edit_contact. The View is assigned to the member variable mView

Next, the following five Button objects are created:
Label = "Save" ; object name = save ; id = save ; method called = save(View view)
Label = "Cancel" ; object name = cancel ; id = cancel ; method called = cancel(View view)
Label = "+" ; object name = addNumber ; id = add_number ; method called = addNumber(View view)
Label = none - click anywhere in the "group_layout" layout ; id = group_layout ; method called = changeGroups(View view)
Label - "Add Field" - method called addField(View v)

MyContactsDbAdapter.getContact(String row) is called with "row" being the row number of the contact in the "contact" table.. (row = mIds) The method returns a Cursor array with the first Cursor (c) holding the basic data for the contact and the second Cursor (c1) in the array holding the group(s) the contact belongs to.

If mLocation = "EditContact" setupRepeat(View v) is called. If not setupNew(View v) is called. The major difference is that in setupNew(...) the information is retrieved from the db where "Temp" = "No" and for setupRepeat "Temp" = "Yes".

setupNew(View v)
Called by: .onCreateView(...)

MyContactsDbAdapter.clearTemps() is called to wipe the db clear of the temp rows - otherwise there can be duplicate entries if user left a page that uses the temp rows without clicking cancel or save.

MyContactsDbAdapter.getContact(String row ) is called to get two Cursor objects, the first holds primary information for the contact (Name, Fields) and is assigned to Cursor "c". The second holds the category information and is assigned to Cursor "c1". Where "row" in the getContact(...) signature is mIdS

The method then loops through the Cursor "c" holding the contact info and assigns the name to mPresetName, mName, & the EditText box tvName. ...Adapter.addTempName(String name, string row) is then called to add row to the contacts db table that will hold the name or any changes to the name until the user clicks save. (It also copies over all the "Field" information") This is so that any use updates that the user makes while the display is being updated will be maintained. .addTempNames returns mTempNameRow so that this row number will be available in other sections of this class.

The method then creates the layout Widgets that will be used to hold any of the "Field" information that this contact has (Address, Note, etc). This includes the FrameLayout from edit_contact.xml and the LinearLayout that goes inside of it. In addition, the LinearLayout has the orientation set and is added to the FrameLayout.

MyContactsdbAdaptergetTempFields(String row) is then called and returns a Cursor. This Cursor is then looped through checking for how many of the "HasField" columns are marked "Yes", for each one the int "field" is increased by 1. In addition, if any of the fields is not set to "Yes" the String "allShow" is set to "No" After the loop "allShow" is checked, and if it is still "Yes", that means all of the additional fields are already being shown and the button to add fields is set to invisible.

The EditText[] mEtField is then created to hold an EditText object for each field that is to be displayed. The size of this array is based on the count from the loop above.

The method then creates the layout Widgets that will be used to hold any of the "Field" information that this contact has (Address, Note, etc). This includes the FrameLayout from edit_contact.xml and the LinearLayout that goes inside of it. In addition, the LinearLayout has the orientation set and is added to the FrameLayout.

MyContactsdbAdapter.getTempFields(String row) is then called again. This time as each field is checked, if "HasField" is "Yes" the value for that field is retrieved, addFieldLayout(...) is called, the returned LinearLayout is added to the FrameLayout, mField is assigned the value of "fieldCount", and "fieldCount" is increased by one.

After the Cursor while coop, the LinearLayout (LLFrame) is added to the FrameLayout. MyContactsDbAdapter .getAllNumbers(int row, String temp) is then called to retrieve a Cursor object (cNumbersTypeOrig) that will hold all the numbers associated with this contact. The values for the columns "Name", "Number", "Type", and "_id" are all retrieved from the Cursor and are passed into MyContactsDbAdapter. addTempNumber(...) to create a new row that will hold all the information along with "Temp" being set to "Yes". The result from addTempNumber is the row number for the newly created row, this value is assigned to mTempRows[count], where "count" is advanced by 1 each time through the while loop of the Cursor. In addition, the value of "_id" besides being used for .addTempNumber(...) is also used to set the value of mRows[count].

...Adapter.getAllTempNumbers() is then used to retrieve all of the rows in the db where "Temp" is marked "Yes". Cursor.getCount() is then used to see how many numbers will be displayed and the necessary arrays of Widgets to display those numbers are created. This is includes the Spinner[] to hold the type of phone, an EditText[] to hold the number, a Button[] to hold the delete button, and a LinearLayout to hold the entire row.

The method then creates the display Widgets that will hold all of the phone numbers. This includes the FrameLayout that is created from edit_contact.xml and the LinearLayout that goes inside of this and will hold all the LinearLayouts from each row. In addition, the id is set for the LinearLayout using mIdCount and the orientation of the LinearLayout is set to vertical.

The Cursor is the looped through, and for each row the "Type" & "Number" are retrieved and addPhoneNumbers(...) is called. The method adds a LinearLayout object to the array mLlRow. The array index will be the same as count1. This LinearLayout object is then added to the LinearArray object for all the number (llNumber).

After looping through all the numbers llNumbers is added to the FrameLayout for the numbers - "fLNumbers".

The last part of the method is the second Cursor from getContact(...) is then looped through to establish the label for the categories (groups). After the first group is added a "," and a space is added to the String prior to adding the new group. In addition, if there is more than one group, the label is changed from "Group" to "Groups". The TextViews for both the label and the groups are then updated.

setupRepeat(View v)
Called by: onCreateView(...)

The first step in this method is to call MyContactsDbAdapter. getTempName(String row) where row = mIdS This is then used to set the text for the EditText widget.

Next, the method calls MyContactsDbAdapter.getAllTempNumbers() which returns all the rows where "Temp" = "Yes" & "IsPrimary" = "No"

mTempRows[] & mRows[] are created and initialized to the count of the Cursor from .getAllTempNumbers(). mTempRows[] is filled with the values from "_id" and mRows[] with the values from "Orig_id".

MyContactsDbAdapter.getAllTempNumbers() is called again. The result of this Cursor.getCount() is assigned to "size" and this value is used to set the size of the array that will hold the layout Widgets.

The arrays for the layout Widgets are all initialized as well as the FrameLayout and LinearLayout that will hold them. The id is set for the LinearLayout using mIdCount and the orientation is set to vertical. Next, the Cursor result from getAllTempNumbers() is looped through and for each loop the "Type" and "Number" are retrieved. addPhoneNumbers is called. The LinearLayout that is method creates mLlRow[count1] is then added to the LinearLayout holding all the numbers. The final step in this loop is to increase the count by 1. After the loop the LinearLayout is added for the phone numbers.

MyContactsDbAdapter.getContact(String row) is the called to retrieve the original name of the (Cursor[0]) & the categories information (Cursor[1]). The original name of the contact is assigned to mPresetName and is used in case the temp name is blank.

The second Cursor from getContact(...) is then looped through to establish the label for the categories (groups). After the first group is added a "," and a space is added to the String prior to adding the new group. In addition, if there is more than one group, the label is changed from "Group" to "Groups". The TextViews for both the label and the groups are then updated.

A FrameLayout & LinearLayout object are then created from the layout to hold any additional fields (Address, Note etc). MyContactsDbAdapter.getTempFields(String primaryRow) is called to retrieve all of the columns of the primary temp row given the primary row. The Cursor from this call is then cycled through to determine how many fields will be shown ("HasField" = "Yes").

An EditText[] mEtField is then created so that each EditText which allows the user to enter/edit the value of the field can be created automatically, yet have a unique identifier.

.getTempFields(String row) is then called again and this time through each of the views for the field is populated. The first step is to check if "HasField" = "Yes". If "HasField" = "Yes" "mField" is set to "Yes", addField(View v, String field, String value, int count) is called which creates the TextField object in the TextField[] mEtField[] and sets the text for the field label and value. Still inside the if loop, the member variable int to keep track of the mEtField[] index is set, the count for the array index is increased by one and the LinearyLayout that was returned from addField(...) is added to the overall LinearLayout for the fields (llFrame).

save(...)
Called by: EditContact.onCreateView(...) - Button - id = save - text = "Save" - onClick

This method retrieves the value in the EditText widget for the contact name. It then checks that the value is not 0 and if it is it resets it to the preset name (mPresetName). It then calls on ...Adapter.checkName(String name) to see if that name already exists in the "contacts" table. If it does not exists save1(View v, String name) is called. If it does exists a Toast is shown to let the user know they must enter a different name.

The method then loops through all the field flags to see if the field has been set and if so retrieves the field value from mEtField and calls .updateField(String row, String field, String value).

save1(View v, String name)
Called by: save(View v, String name)

The first thing the method does is to loop through the ListView that hold a EditText for the phone number and a Spinner for the type of phone. The number of items in the ListView is held by mNumbersCount which was established by calling .getCount() on the Cursor holding the numbers / types.

For each element of the ListView a new View object is created using ListView (mListView).getChildAt(i). For the Spinner, a Spinner object is created from the widget in the ListView layout (type_number_edit) and .getSelectedItem().toString() is called on that object. For the EditText .getText().toString() is called on the EditText object created in the same manner as the Spinner object. The last item to be retrieved is the contacts table row number held in the int array mRows. This array was created in onCreateView from the Cursor object holding the numbers and type.

These three values are then used in a call to MyContactsDbHelper.updateNumber(String row, String type, String Number).

The only other item left to retrieve is the name (groups are submitted separately). This is retrieved from the EditText widget in the main layout (edit_contact). It then calls MyContactsDbHelper.updateName(String row, String name).

The final step is to restart the ContactFragment (via ContactListActivity)

cancel()
Called by: EditContact.onCreateView button id = cancel - text = "Cancel"

This method simply calls MyContactsDbAdapter.clearTemps() and then restarts the ContactActivity.

changeGroups(View view)
Called by: onCreateView(...)

This method calls
generalUpdate() to update the temp rows in the db with the information currently in the display Widgets.

It then starts UserGroupsFragment passing along the row number for the contact.

addNumberCheck
Called by: onCreate(...) Button named addNumber ; labeled "+"

This method checks to insure that numbers can still be added to a contact (3 is the limit for display purposes). If a number can be added addNumber(...) is called. If not a Toast is displayed alerting the user.

addNumber(View view)
Called by: onCreate(...) Button named addNumber ; labed "+"

The purpose of this method is to add a number to the contacts table with reference to the name being edited.

The first step is to call generalUpdate() which retrieves all the current information from the display Widgets and updates the database. It then converts mIdS to and int and calls MyContactsDbAdapter.addBlankNumber(int row).

deleteNumber(int count)
Called by addPhoneNumbers(...)

The purpose of this method is to restart the EditContactFragment after the user has clicked to delete a number. After EditContactFragment creates a CursorAdapterPhoneEdit object it passes the instance of itself to that object and a class member object is set equal to that instance. The cursor adapter then calls on this method after the number has been deleted.

The first step is to check the EditText widget for the name and retrieve whatever name is currently in the box. If the box is empty the name is reset to the original name when this Fragment first started.

Next, the method calls MyContactsDbAdapter.clearTemps() to clear all the contact table's rows where Temp = "Yes". (New Temp = "Yes" rows are set each time EditContact is restarted).

The method then loops through the number of rows of numbers (using mNumbersCount, which determined from the Cursor in onCreateView(...)). It first checks that that current line in the ListView (i) is not the same as the one that was deleted (position). If it is, nothing happens in this loop and the line is ignored. If it is not the same one the values from the Spinner and the EditText widget from that row are retrieved and used in a call to MyContactsDbAdapter.updateNumberTemp(...) which passes along all the values just retrieved plus the name which was retrieved in onCreateView(...) and the value of the contact table row number stored in the String array rowS.

The next section of the method checks each of the mField flags and if it is "Yes" the value is retrieved from mEtField[] using the index of mFieldBox. It then calls MyContactsDbAdapter.updateTempField(...) passing along the information just retrieved.

EditContactActivity is then restarted with extras to indicate the contact table row number of the contact and the String that lets the class know that it is being restarted from itself (and therefore to check on contact table column "Temp" = "Yes").

addField(View view)
Called by: EditContact.onCreateview(...) Button - addFieldbutton

The method calls generalUpdate() to update all the temp rows based on the current inputs in the Widgets before starting ChooseField

addFieldLayout(View v, String field, String value, int count)
Called by: setupNew(...) or setupRepeat

Layout:
edit_contact.xml onCreateView(...)
    FrameLayout fl - setupNew(...) or setupRepeat(...)
       LinearLayout llFrame - - setupNew(...) or setupRepeat(...)

          The following is repeated for each field in addField (layoutSignature).
          LinearLayout llField
             LinearLayout llLabel
                TextView tvSpace - for looks
                TextView tvLabel - hold the field title
             LinaearLayout llEdit
                TextView tvSpace1 - for looks
                TextView mEtField[count] - Array created in setupNew(...) or setupRepeat(...) - hold the value of the field

The purpose of this method is to setup the portion of the display for that each additional field requires. (The same layout cannot be reused multiple times and a Cursor Adapter does not work because of the way the db is setup. However, Cursor Adapter seems to have the downside of losing information when a EditText goes out of view.)

addPhoneNumbers(...)
Called by: setupNew(...) & setupRepeat(...)

da R.layout.edit_contact (onCreateview)
    FrameLayout flNumbers - R.id.frame_phone_numbers (calling method)
        LinearLayout llNumbers (calling method)
       - Orientation - Vertical
            RelativeLayout mLlrow[count]
                TextView tv (for spacing)
                - RelativeLayout.ALIGN_PARENT_LEFT
                Spinner mSpinner[count]
                - RelativeLayout.RIGHT_OF, tv.getId()
                TextView tv (for spacing)
                - RelativeLayout.RIGHT_OF, mSpinner[count].getId()
                EditText mEtNumber[count]
                - RelativeLayout.RIGHT_OF, mSpinner[count].getId()
                mDeleteButton[count]
                - RelativeLayout.ALIGN_PARENT_RIGHT

generalUpdate()
Called by addNumber(...), changeGroups(...), and addField(...).

This method handles the bulk of the work for the methods that need to update the database before EditContact is re-started. This work consist of reading the EditText and Spinner Widgets and updating the temp rows of the "contact" db.

Note that this does not handle the updates when the delete number method is called. This is because that method requires separate steps to handle the deleted row.

The first step is to retrieve the name. The EditText Widget for the name is retrieved using mView. If the name is blank it is reset to the original name before the edit. mTempNameRow is converted to a String and MyContactsDbAdapter.updateTempName(String tempRow, String name) is called.

The next section loops through all the numbers mNumbersCount, retrieves the values from the Widgets mEtNumber[i], & mSpinner[i],, along with the temp row number from mTempRows[i] and calls MyContactsDbAdapter.updateNumberTemp(...)

The last section of the method checks each of the mField flags and if it is "Yes" the value is retrieved from mEtField[] using the index of mFieldBox. It then calls MyContactsDbAdapter.updateTempField(...) passing along the information just retrieved.

CategoriesFragment
Called by:
ContactsListFragment - menu - Show Categories
Self

onCreateView(...)

The method creates three Button objects. "Deselect calls deselectAll(View view), "Select All" calls selectAll(View view), and "Apply") calls apply(View view)

It then calls getCategories() on a MyContactsDbAdapter object and uses the returned Cursor object to create a DetailCursorAdapter (subclass of SimpleCursorAdapter)

deleteGroup
Called by: onOptionsItemSelected(...) "Delete Group"

This method simply starts the
. The first extra tells the fragment being called which fragment called it. It behaves differently depending on which fragment calls it. The second extra is simply because the activity being called requires two extra - it is just a dummy string.

addDialog()
Called by: menu item

This method creates the dialog "AddDialogFragment that allows the user to enter the name of a new group for their contacts. Creating the dialog and setting it up so that it returns the user input takes 4 steps. 1) Create a FragmentManager. 2) Create the dialog object (in this case AddDialogFragment). 3) Call setTargetFragment(Fragment "this", int requestCode) on the DialogFragment object. 4). Call show(FragmentManager fm, String tab) on the DialogFragment object.

The answer is returned below in onActivityResult(...).

onActivityResult
Called by: UserGroupFragment.sendResults(...)

This method retrieves the input from the dialog and calls add(String input) with that input.

add(String input)
Called by:
onActivityResult(...)

This method calls MyContactsDbAdapter.addNewCategory(String cat) , adds the row number and the name of the calling fragment to the argument bundle, and then restarts the EditContactFragment.

selectAll(View view)
Called by: onCreateView

This method allows the user to check all the boxes at once.

The method calls selectAllCategories() on a MyContactsDbHelper object. It then restarts this activity.

deselectAll(View view)
Called by: onCreateView(...)

This method allows the user to uncheck all the boxes at once.

The method calls deselectAllCategories() on a MyContactsDbHelper object. It then restarts this activity.

apply(View view)
Called by: onCreateView(...)

The first step in this method is to call getCategories() on a MyContactsDbAdapter object. The returned Cursor is then looped through to see if any of the column "Show" values has been set to "Yes". If so MycontactsDbHelper.updateCategories() is called and then this activity is restarted. If no category is has column "Show" marked "Yes" then a Toast is put on the screen letting the user know that at least one category must be selected.

SingleFragmentActivity

This is an abstract class that is subclassed by the Activities to handle the Fragment management. It uses activity_fragment.xml for the layout. This layout consist of a FrameLayout with an id = fragmentContainer. The layout for the Fragment itself is handled by the Fragment.

DeleteCategoriesFragment
Called by: UserGroupsFragment.deleteGroup() , Addcontact.addDelete(), CategoriesFragment.deleteGroup()

onCreateView(...)

After inflating "categories_delete" it creates a Button object from widget "delete". The button is labeled "Delete" and will call the delete(View view) method below. getCategories() is then called on a MyContactsDbAdapter object. The returned Cursor object is then used in the constructor for a CursorAdapterDelCategorie (subclass of SimpleCursorAdapter) object.

Finally a ListView object is created and setAdapter is called on that object passing in the CursorAdapterDelCategories object.

delete(View view0
This methods calls
deleteCategories() on a MyContactsDbAdapter object.

The final step in the method is to call getActivity().finish() in order to return the program control back to the calling class.

CursorAdapterDelCategories
Called by: DeleteCategoriesFragment

Constructor

The constructor takes in all the information that is necessary to list the groups that can be deleted. (All groups currently being used except "Not Assigned." It also assigns to off the variables to class variables - the remainder of the variables passed in are used by the system and not explicitly by the code.

getView(...)
The method calls moveToPosition(int position) based on the int "pos" that was passed to the method. It then creates a checkbox for each item, in which setChecked is false. Next, if checks with the database if this group can be deleted ("CanDelete column is set to "Yes") and if not calls setEnabled with "false"

The method then creates a Textview object based on the widget R.id.category, and calls setText on the widget setting the text based on the database value of "Category".

The final step in the method is to add an OnClickListener to the CheckBox(s) that will call MyContactsDbAdaper.markCatDel(String category, string YesorNo) to update the database to either "Yes" or "No" for whichever category was selected.

UserGroupsFragment
Called by: EditContactFragment.changeGroups(View view)

newInstance
This method takes in the row number of the contact being edited when this activity was called. (So that the same contact can be displayed when this activity is finished). It then creates a new Bundle object, assigns the row number to that bundle, creates a new UserGroupsFragment, sets the arguments with this bundle, and returns the fragment just created.

onCreateView(...)

After first inflating the layout R.laout.categories1_layout the method calls MyContactsDbonAdapter.getCategories() This returns a Cursor object which contains the list of groups available for the user to put the contact in.

The count for the Cursor (the number of categories) is assigned to the local variable size. This is then use to create the array of Widgets for each item necessary for each row. This includes a CheckBox[], a TextView[] for the category name, and a RelativeLayout[] to for each row.

A FrameLayout is retrieved from the View (this serves as a place holder for the rows of categories in the display). A new LinearLayout is created which will be placed in the FrameLayout - The LinearLayout is the only child of the FrameLayout and is necessary because FrameLayout does not have the necessary functions. All rows will be added to this LinearLayout.

addDialog()
Called by: menu item

This method creates the dialog "AddDialogFragment that allows the user to enter the name of a new group for their contacts. Creating the dialog and setting it up so that it returns the user input takes 4 steps. 1) Create a FragmentManager. 2) Create the dialog object (in this case AddDialogFragment). 3) Call setTargetFragment(Fragment "this", int requestCode) on the DialogFragment object. 4). Call show(FragmentManager fm, String tab) on the DialogFragment object.

The answer is returned below in onActivityResult(...).

onActivityResult
Called by: UserGroupFragment.sendResults(...)

This method retrieves the input from the dialog and calls add(String input) with that input.

add(String input)
Called by:
onActivityResult(...)

This method calls MyContactsDbAdapter.addNewCategory(String cat) , adds the row number and the name of the calling fragment to the argument bundle, and then restarts the EditContactFragment.

deleteGroup
Called by: Menu item.

This method simply starts DeleteCategoriesFragment

apply(View view)
Called by: onCreateView(...)

This method simply calls setCategories(String row) on an MyContactsDbAdapter object and then calls .finish on the Activity.

addRow(...)
Called by: UserGroupsFragment.onCreateview(...)

This method lays out a new row in the display for each category. The method takes in the category name, a Boolean if it is checked or not, and the count of how many categories have already been displayed.

The box for "Not Assigned" has .setEnabled set to false - this box is automatically checked if no other boxes are checked and is unchecked when another box is checked.

For each CheckBox a listener is added. When a box is checked MyContactsDbAdapter.markCatShowTemp(String category, String "Yes") is called to set "ShowTemp" to "Yes" on the row with the category name and contactID = -4. In addition, mCategoriesChecked is increased by 1. If this variable then equals 1, it means "Not Assigned" is no longer checked. The box for "Not Assigned" is then unchecked and .markCatShowTemp(String Cat, String "No") is called. The opposite happens when a box is unchecked, in this case the method looks for mCategoriesChecked to go to '0'.

mIdChecked is used to set a unique id for every Widget, it is increased by 1 every time a new Widget is created.

CursorAdapterUserGroups
Called by: UserGroupsFragment

getView(...)

As this method lays out the list, for each position it calls getShowCat(int row, String category) on a MyContactsDbAdapter. This methods returns a Boolean value. If the value is true the box is checked.

The method next sets the text for each item - the name of the category. It then adds a listener to each CheckBox and calls markCatShow(String category, String YesOrNo) setting the category "Show" to "Yes" or "No" as appropriate.

CursorAdapterPhones
Called by: ContactFragment
Extends: SimpleCursorAdapter (sdk)

This class takes in a Cursor object which contains all the numbers & phone types associated with a given contact. It then attaches this data to two Textview widgets for each set of numbers/type.

AddDialogFragment
Called by:
UserGroupsFragment.addDialog()
UserGroupsFragment.addDialog()

The purpose of this class is to display a dialog so that the user can enter the name of a new group they are creating for their contacts. The class takes steps so that information is not lost when the device is rotated.

newInstance(String group) The newInstance method is used instead of a standard constructor, so that the arguments are set prior to the object being initiated. All of items passed into the method need to be put into the bundle before the bundle is attached (.setArguments) to the Fragment.

sendResult(...)
Called by: onCreateDialog(...)

This method sends the results to the class that called this dialog subclass. It knows which class it is because it was set with .setTargetFragment in the calling class. The object that was set with this method is kept track of by the OS so no need to track it in this class. This method calls UserGroupFragment.onActivityResult(...)

onCreateDialog(Bundle savedInstanceState) The first thing the method does is retrieve the group name from the arguments Bundle. If the dialog was started from UserGroupsFragment this value will be "". However, if this method is restarted because the screen was rotated (or some other system operation" then the value will be whatever was in the text box when the screen was rotated. The value is assigned to mGroup.

Next a new EditText is created (in this case there is no layout for this widget). The text for this is then set with mGroup and a listener is added to check if the text has been upgraded. Three methods are required for this listener, but the only one that is used is onTextChanged(...) When this method is called the character sequence that was pass into the method from the system is converted to a String, and that String along with its key is put into the arguments bundle.

The AlertDialogBuilder is then used to set the attributes of the dialog. It is here that the EditText widget is added to the dialog.

The positive button of the dialog calls sendResult(int resultCode). The value from the EditText widget is set equal to a class member variable, so it does not need to be passed along.

The negative button is to cancel, it doesn't call anything but upon pressing the dialog is removed.

The last step in the AlertDialog.Builder and this method is to call .create(). Called by: UserGroupsFragment.addDialog()

ChooseField Called by: EditContact.addField(View v), AddContact

onCreateview(...)

This method, after inflating choose_field.xml calls MyContactsDbAdapter. getTempFields(String row) to return all in information in the given row. It then loops through all the fields to determne which ones are not being used.

It then creates a Button[] with size equal to the number of fields not being used. Next, it repeats the process of calling MyContactsDbAdapter.getContactRow(String row) and cycling through the fields. This time, instead of just counting the available fields it creates a button for each one found. For each button it sets the text to the field name and sets a listener that will call addField(String fieldName). The last step in the method is to loop through all the buttons and to add them to the LinearLayout contained in choose_field.xml. addField(String type)
Called by: onCreateView(...)

This method first checks what the type of field has passed in and then sets the local variable to "Has..." where the ... is the field name. It then calls MyContactsDbAdapter.updateTempFieldFlag(String row, String Type, String "Yes/No")

The method then restarts either EditContact or AddContact passing along which class called this class along with the primary row number