resolveActivity return null but startActivity works if not checking

The button is always disabled if I put checking before setting the onClickListener. Doesn’t matter if I use queryIntentActivity, resolveActiviy or just use intent.resolveActivity they all return null or empty content back. The strange thing is startActivity works fine that Contacts App launched and I could select a contact and query its display name without any problem. Can anyone give me a hand? Thanks.

btnSelectSuspect.apply {
val pickIntent = Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI)
val packageManager = requireActivity().packageManager
val resolverInfo = packageManager.queryIntentActivities(pickIntent, PackageManager.MATCH_DEFAULT_ONLY)
if (resolverInfo == null || resolverInfo.size == 0)
this.isEnabled = false;
else
setOnClickListener {
startActivityForResult(pickIntent, REQUEST_CONTACT)
}
}

Gradle file:

apply plugin: ‘com.android.application’
apply plugin: ‘kotlin-android’
apply plugin: ‘kotlin-android-extensions’
apply plugin: ‘kotlin-kapt’

android {
compileSdkVersion 30
buildToolsVersion “30.0.2”

defaultConfig {
    applicationId "com.ibm.test.criminalintent"
    minSdkVersion 21
    targetSdkVersion 30
    versionCode 1
    versionName "1.0"

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

buildTypes {
    release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
}

}

dependencies {
implementation fileTree(dir: “libs”, include: ["*.jar"])
implementation “org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version”
implementation ‘androidx.core:core-ktx:1.3.1’
implementation ‘androidx.appcompat:appcompat:1.2.0’
implementation ‘androidx.constraintlayout:constraintlayout:2.0.1’
implementation ‘androidx.lifecycle:lifecycle-extensions:2.2.0’
implementation ‘androidx.legacy:legacy-support-v4:1.0.0’
implementation ‘androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0’
implementation ‘androidx.recyclerview:recyclerview:1.1.0’
implementation ‘androidx.room:room-runtime:2.2.5’
kapt ‘androidx.room:room-compiler:2.2.5’
testImplementation ‘junit:junit:4.12’
androidTestImplementation ‘androidx.test.ext:junit:1.1.2’
androidTestImplementation ‘androidx.test.espresso:espresso-core:3.3.0’

}

It only happened on the enumerator, in the real device it works.

The resolveActivity() method is very important and API dependent. If we are targeting API 29 (Android 10) or lower, then we can view what other apps exist on the device. Starting with API 30 (Android 11) and newer, the principle of least privilege applies and we need to tell the system what other packages to make visible to our app.

Here is the documentation page describing Package Visibility beginning in Android 11.

https://developer.android.com/about/versions/11/privacy/package-visibility

We must request permission to access anything outside of our app. Any interaction between our app and the OS goes in to the Manifest to register. In the AndroidManifest.xml, we need to tell the OS that we plan to query the Contacts package in order to pick a contact. The Contacts app should be part of the list of apps that automatically visible to our app.

https://developer.android.com/training/basics/intents/package-visibility#automatic

But as soon as we add the data field to the intent, we now need to make an explicit query request. I was able to gain proper access using the following query in the manifest:

<!-- AndroidManifest.xml -->
<manifest ...>
    <queries>
        <package android:name=”com.android.contacts”/>
    </queries>
    <application ...>
        ...
    </application>
</manifest>

I would have thought from reading the documentation that the query could have been structured as an intent, such as

<queries>
    <intent>
        <action android:name="com.androind.action.PICK"/>
        <data android:scheme="content" android:host="com.android.contacts"/>
    </intent>
</queries>

But no combination of attributes gave proper access. Only by using the package query did the intent with the data URI resolve. I feel there should be an alternate way, but I haven’t found it yet.

2 Likes

I agree the documentation is a bit confusing it seems to indicate any of the two alternatives (i.e. explicit package declaration or intent filters) would work.

I got it to work by using one of the android documentation examples for a totally different contacts intent

<queries>
    <intent>
        <action android:name="android.intent.action.VIEW" />
        <data android:scheme="content" android:host="com.android.contacts"
            android:mimeType="vnd.android.cursor.item/*" />
    </intent>
</queries>

This makes absolutely no sense to me because that isn’t the action name I can see in the debugger for the constructed Intent that gets passed to the package manager query. But it does seem to work so ¯\_(ツ)_/¯

1 Like