Search code examples
androidkotlinandroid-manifestandroid-api-30

what xml queries are needed when adding or editing to contacts? intent.resolveActivity(getPackageManager) returns null


I am trying to write Kotlin code that adds a contact to an android users's contact list. But the line intent.resolveActivity(getPackageManager) returns null. I understand starting with android API verions 30, one needs to add queries statements to the AndroidManifest.xml file. This issue has been posted on stack overflow. Ref: How to add the <queries> tag in the manifest? But based upon these, it's not clear what to use in the case of updating or editing a contacts list. So the question is what do I use in this case? I would like to avoid using the QUERY_ALL_PACKAGES permission because this can cause the app to be "subject to permission" when submitted to the Google Play (ref: https://developer.android.com/training/package-visibility. My Android Manifest is:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-sdk tools:overrideLibrary="       androidx.camera.camera2, androidx.camera.core,       androidx.camera.view, androidx.camera.lifecycle" />

    <uses-feature android:name="android.hardware.camera" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.WRITE_CONTACTS" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <queries>
        <!-- Browser -->
        <intent>
            <action android:name="android.intent.action.VIEW" />
            <data android:scheme="http" />
        </intent>

        <!-- Camera -->
        <intent>
            <action android:name="android.media.action.IMAGE_CAPTURE" />
        </intent>

        <!-- Gallery -->
        <intent>
            <action android:name="android.intent.action.GET_CONTENT" />
        </intent>
    </queries>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".AddToContacts"
            android:exported="true">
            <meta-data
                android:name="android.app.lib_name"
                android:value="" />
        </activity>
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <meta-data
                android:name="com.google.android.gms.version"
                android:value="@integer/google_play_services_version" />

            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

My code for the activity is

import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.provider.ContactsContract
import android.view.View
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
import androidx.camera.core.impl.utils.ContextUtil.getApplicationContext
import androidx.core.content.ContextCompat.startActivity

class AddToContacts : AppCompatActivity() {


   private lateinit var textViewName: TextView
   private lateinit var textViewPhoneNumber: TextView
   private lateinit var textViewEmailAddress: TextView
   private lateinit var textViewEmailAddress2: TextView
   private lateinit var buttonSaveToContacts: Button
   private lateinit var strQRCodeResult: String


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_add_to_contacts)

        textViewName = findViewById(R.id.textViewName)
        textViewPhoneNumber = findViewById(R.id.textViewPhoneNumber)
        textViewEmailAddress = findViewById(R.id.textViewEmailAddress)
        textViewEmailAddress2 = findViewById(R.id.textViewEmailAddress2)
        buttonSaveToContacts = findViewById(R.id.SaveToContacts)
        strQRCodeResult= intent.getStringExtra("mystring").toString()



        buttonSaveToContacts.setOnClickListener(object: View.OnClickListener {
            override fun onClick(v: View) {
                if (textViewName.text.toString().isNotEmpty()&&textViewPhoneNumber.text.toString().isNotEmpty()&&
                    textViewEmailAddress.text.toString().isNotEmpty()&&
                    textViewEmailAddress2.text.toString().isNotEmpty()){
                    val intent =  Intent(Intent.ACTION_INSERT)
                    intent.setType(ContactsContract.RawContacts.CONTENT_TYPE)
                    intent.putExtra(ContactsContract.Intents.Insert.NAME,textViewName.text.toString())
                    intent.putExtra(ContactsContract.Intents.Insert.PHONE,textViewPhoneNumber.text.toString())
                    intent.putExtra(ContactsContract.Intents.Insert.EMAIL,textViewEmailAddress.text.toString())
                    if (intent.resolveActivity(getPackageManager())!=null) {
                        startActivity(intent)
                    }
                    else {
                        Toast.makeText(getApplicationContext(),"there is no app to support this action",
                        Toast.LENGTH_SHORT).show()
                    }
                }
                else {
                    Toast.makeText(getApplicationContext(),"please fill in all fields",Toast.LENGTH_SHORT).show()
                }
                }

        })
        ParseData(strQRCodeResult)



    }

    private fun ParseData(strQRCodeResult: String) {
        textViewName.text="yyy xxx"
        textViewPhoneNumber.text="2125551212"
        textViewEmailAddress.text="[email protected]"
        textViewEmailAddress2.text="[email protected]"
    }


}

Solution

  • The simple solution is to get rid of resolveActivity():

                        try {
                            startActivity(intent)
                        }
                        catch (anf: ActivityNotFoundException) {
                            Toast.makeText(getApplicationContext(),"there is no app to support this action",
                            Toast.LENGTH_SHORT).show()
                        }
    

    If you insist on using <queries>, your <intent> needs to match the Intent that you are passing to startActivity(). Your Intent action is ACTION_INSERT, whose string value is "android.intent.action.INSERT". Your MIME type is ContactsContract.RawContacts.CONTENT_TYPE, whose string value is "vnd.android.cursor.dir/raw_contact". So, your <intent> would be:

        <intent>
          <action android:name="android.intent.action.INSERT" />
          <data android:mimeType="vnd.android.cursor.dir/raw_contact" />
        </intent>