Challenge: Another Implicit Intent - Solution

Hi guys,

I want to share with you my proposed solution to this challenge, here is the steps:

1- create the UI elements
2- create a new field in Crime class: phone field where i’ll save the phone number of suspect

@Entity
data class Crime(
@PrimaryKey val id: UUID = UUID.randomUUID(),
var title: String = “”,
var date: Date = Date(),
var isSolved: Boolean = false,
var suspect: String = “”,
var phone: String = “”)

3- In CrimeDatabase, i modified its version and added a new Migration object to it

@Database(entities = [Crime::class], version = 3)
@TypeConverters(CrimeTypeConverters::class)
abstract class CrimeDatabase : RoomDatabase() {
abstract fun crimeDao(): CrimeDao
}
val migration_2_3 = object : Migration(2, 3) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(“ALTER TABLE Crime ADD COLUMN phone TEXT NOT NULL DEFAULT ‘’”)
}

4- To apply this migration, i added it in the crimeRepository when creating the database

class CrimeRepository private constructor(context: Context){

private val database: CrimeDatabase= Room.databaseBuilder(
    context.applicationContext,
    CrimeDatabase::class.java,
    DATABASE_NAME

).addMigrations(migration_2_3)
.build()

After finishing with the model layer, i will change the CrimeFragment,
5- i create the callSuspectButton object, then i get its reference in onCreateView

callSuspectButton = view.findViewById(R.id.call_suspect) as Button

6- To get the suspect phone number , i modified the overrided function onActivityResult (Here is the central part of this challenge):

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    when {
        resultCode != Activity.RESULT_OK -> return

        requestCode == REQUEST_CONTACT && data != null -> {

            val contactUri: Uri? = data.data
     // queryFieldsName: a List to return the DISPLAY_NAME Column Only
            queryFieldsName = arrayOf(ContactsContract.Contacts.DISPLAY_NAME)

     // queryFieldsId: a List to return the _ID Column Only, i will use it to get the suspect Id
            val queryFieldsId = arrayOf(ContactsContract.Contacts._ID)

            val cursorName = requireActivity().contentResolver
                .query(contactUri!!, queryFieldsName, null, null, null)
            cursorName?.use {
                if (it.count == 0) {
                    return
                }

                it.moveToFirst()
                val suspect = it.getString(0)
                crime.suspect = suspect
                suspectButton.text = suspect
            }

      // I created another Cursor to get the suspect Id 
            val cursorId = requireActivity().contentResolver
                .query(contactUri!!, queryFieldsId, null, null, null)
            cursorId?.use {
                if (it.count == 0) {
                    return
                }

                it.moveToFirst()
     // here i put the suspect Id in contactId to use it later (to get the phone number)
                val contactId = it.getString(0)

     // This is the Uri to get a Phone number
                val phoneURI = ContactsContract.CommonDataKinds.Phone.CONTENT_URI

     // phoneNumberQueryFields: a List to return the PhoneNumber Column Only

                val phoneNumberQueryFields = arrayOf(ContactsContract.CommonDataKinds.Phone.NUMBER)

     // phoneWhereClause: A filter declaring which rows to return, formatted as an SQL WHERE clause (excluding the WHERE itself)
                val phoneWhereClause = "${ContactsContract.CommonDataKinds.Phone.CONTACT_ID} = ?"

     // This val replace the question mark in the phoneWhereClause  val
                val phoneQueryParameters = arrayOf(contactId)

                val phoneCursor = requireActivity().contentResolver
                    .query(phoneURI, phoneNumberQueryFields, phoneWhereClause, phoneQueryParameters, null )

                phoneCursor?.use { cursorPhone ->
                    cursorPhone.moveToFirst()
                    val phoneNumValue = cursorPhone.getString(0)

     // after retrieving the phone number i put it in the crime.phone        
                    crime.phone = phoneNumValue
                }
                crimeDetailViewModel.saveCrime(crime)
            }
        }
    }
}

7- in onStart() function, i implementd an onClickListener on callSuspectButton

    callSuspectButton.setOnClickListener {
        val callContactIntent =
            Intent(Intent.ACTION_DIAL).apply {
                 
                val phone = crime.phone
                data = Uri.parse("tel:$phone")

            }
  // this intent will call the phone number given in Uri.parse("tel:$phone")
        startActivity(callContactIntent)
    }

I hope this solution help you in your challenge

2 Likes

Thanks for sharing. I was struggling what to do with the id after I got it.

Also fyi for those looking, you don’t have to create a second cursor. If you add it to the original query field like

 val queryFields =
                arrayOf(ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.Contacts._ID)

Then the it.getString(1) will be the id.

1 Like

Here is a way to choose a number if the contact has multiple numbers:

   override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        when {
            resultCode != Activity.RESULT_OK -> return

            requestCode == REQUEST_CONTACT && data != null -> {
                val contactUri: Uri? = data.data

                var contactId: String? = null

                
                val queryFields = arrayOf(ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.Contacts._ID)
                val cursor = contactUri?.let { requireActivity().contentResolver.query(it, queryFields, null, null, null) }
                cursor?.use {
                    if(it.count == 0) return

                    it.moveToFirst()

                    val suspect = it.getString(0)
                    //get the contact ID
                    contactId = it.getString(1)

                    crime.suspect = suspect
                    suspectButton.text = suspect
                }

                // This is the Uri to get a Phone number
                val phoneURI = ContactsContract.CommonDataKinds.Phone.CONTENT_URI

                // phoneNumberQueryFields: a List to return the PhoneNumber Column Only
                val phoneNumberQueryFields = arrayOf(ContactsContract.CommonDataKinds.Phone.NUMBER)

                // phoneWhereClause: A filter declaring which rows to return, formatted as an SQL WHERE clause (excluding the WHERE itself)
                val phoneWhereClause = "${ContactsContract.CommonDataKinds.Phone.CONTACT_ID} = ?"

                // This val replace the question mark in the phoneWhereClause  val
                val phoneQueryParameters = arrayOf(contactId)

                val phoneCursor = requireActivity().contentResolver
                    .query(phoneURI, phoneNumberQueryFields, phoneWhereClause, phoneQueryParameters, null )

                //phone number string
                var phoneNumber: String = ""

                val allNumbers: ArrayList<String> = arrayListOf<String>()
                allNumbers.clear()

                phoneCursor?.use {cursorPhone ->

                    cursorPhone.moveToFirst()
                    while (cursorPhone.isAfterLast == false)
                    {
                        phoneNumber = cursorPhone.getString(0)
                        allNumbers.add(phoneNumber)
                        cursorPhone.moveToNext()
                    }
                }


                val items = allNumbers.toTypedArray()

                var selectedNumber: String = ""


                val builder = AlertDialog.Builder(context)
                builder.setTitle("Choose a Number:")
                builder.setItems(items, DialogInterface.OnClickListener { dialog, which ->  selectedNumber = allNumbers[which].toString().replace("_","")
                    crime.phone = selectedNumber
                    callSuspect.text = crime.phone
                })

                val alert = builder.create()
                if(allNumbers.size > 1) {
                    alert.show()
                }
                else if (allNumbers.size == 1 && allNumbers[0].length != 0) {
                    selectedNumber = allNumbers[0].toString().replace("_","")
                    crime.phone = selectedNumber
                    callSuspect.text = crime.phone

                }

                else
                {
                    callSuspect.text = "no phone number found!"
                    crime.phone = ""
                }

                crimeDetailViewModel.saveCrime(crime)
            }

        }
    }

Hi I followed you solution but have permission problem and the application crushed.
Did you solve the permission problem ?

Sorry if I bother, I used permission
Go to manistest and add permission before the application tag

left-arrow uses-permission android:name=“android.permission.READ_CONTACTS” right-arrow left-arrow uses-permission right-arrow

Then at run time go to phone, app and notification, and allow the app to use the contact.
Bye Paolo