Issue
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>
Answered By - CommonsWare
Answer Checked By - Clifford M. (JavaFixing Volunteer)