Android: SearchableInfo is null when using packageNameSuffix in Gradle build script

1.3k Views Asked by At

I encountered that the method getSearchableInfo always returns null during SearchView initialization if I use the packageNameSuffix in the project's Gradle build script.

SearchView initialization:

final SearchManager searchManager = (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
SearchableInfo info = searchManager.getSearchableInfo(componentName);
mSearchView.setSearchableInfo(info);

Project's build.gradle:

android {
    [...]
    buildTypes {
        debug {
            packageNameSuffix ".debug"
            versionNameSuffix "-debug"
            signingConfig signingConfigs.debug
        }
        [...]
    }
}

If the package suffix is not used, the given componentName is ComponentInfo{com.example.android/com.example.android.MapActivity} and the SearchView as well as its associated SuggestionsProvider work fine.

But if packageNameSuffix is set to ".debug", the given componentName is ComponentInfo{com.example.android.debug/com.example.android.MapActivity} and the SearchManager returns null, instead of returning the respective SearchableInfo object.

Does anyone know how to get the right SearchableInfo from the SearchManager? Thanks!

[EDIT]

Eugen Martinov mentioned in the comments that this behaviur could have to do something with an improper or missing authorities renaming. But i also configured a build type dependent naming of the authorities, that i omitted in the initial post for the sake of simplicity.

Project's build.gradle:

android {
    [...]
    sourceSets {
        debug {
            java.srcDirs = [
                'src/main/java'
            ]
            java.srcDirs = [
                'src/debug/res',
                'src/main/res'
            ]
        }
        release {
            java.srcDirs = [
                'src/main/java'
            ]
            java.srcDirs = [
                'src/release/res',
                'src/main/res'
            ]
        }
        [...]
    }
}

src/debug/res/values/build-config.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="cfg_app_name">App - Debug</string>
    <string name="cfg_authorities">com.example.debug.SuggestionsProvider</string>
    <string name="cfg_maps_key"><!-- some key --></string>
</resources>

src/release/res/values/build-config.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="cfg_app_name">App</string>
    <string name="cfg_authorities">com.example.SuggestionsProvider</string>
    <string name="cfg_maps_key"><!-- some other key --></string>
</resources>

src/main/res/xml/searchable.xml:

<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
    android:hint="@string/action_search_hint"
    android:label="@string/cfg_app_name"
    android:includeInGlobalSearch="false"
    android:queryAfterZeroResults="true"
    android:searchSuggestAuthority="@string/cfg_authorities"
    android:searchSuggestIntentAction="android.intent.action.VIEW"
    android:searchSuggestThreshold="3" />

Installing both the debug (with the packageNameSuffix option) and the release apk on the same device works. I don't get an error like Failure [INSTALL_FAILED_CONFLICTING_PROVIDER]... But as already said, SearchableInfo is null then.

Installing both apk withouth the packageNameSuffix option leads into the following error: Failure [INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES] - Installation failed since the device already has an application with the same package but a different signature.

Or am i missing something here?

[/EDIT]

1

There are 1 best solutions below

2
On

I am using product flavors that completely changes the package name. I found that by using the ComponentName(Context pkg, Class<?> cls) constructor for my search activity I get a valid SearchableInfo.

SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchableInfo searchableInfo = searchManager.getSearchableInfo(new ComponentName(this, SearchActivity.class));

I also had to adjust my provider to use a different package so my searchable xml is in each flavor directory and the provider is listed in the manifest for each flavor as well. This is my directory structure:

src
  main
    AndoridManifest.xml
  flavor1
    res
      xml
        search.xml
    AndroidManifest.xml
  flavor2
    res
      xml
        search.xml
    AndroidManifest.xml

main/AndroidManifest.xml

<application
    android:icon="@drawable/ic_launcher"
    android:logo="@drawable/actionbar_icon"
    android:label="@string/app_name"
    android:name=".App"
    android:allowBackup="true"
    android:theme="@style/AppTheme">
    <activity
        android:name=".ui.MainActivity"
        android:launchMode="singleTask">
        <meta-data
            android:name="android.app.default_searchable"
            android:value=".ui.activity.SearchActivity"/>
    </activity>
    <activity android:name=".ui.SearchActivity" android:launchMode="singleTop">
        <intent-filter>
            <action android:name="android.intent.action.SEARCH" />
        </intent-filter>
        <meta-data
            android:name="android.app.searchable"
            android:resource="@xml/search"/>
    </activity>

    <provider
        android:authorities=".provider.SuggestionProvider"
        android:name="com.example.main.provider.SuggestionProvider"
        android:exported="false"
        android:enabled="true"
        />
</applicaiton

flavor1/AndroidManifest.xml

<application>
    <provider
        tools:replace="android:authorities"
        android:authorities="com.example.flavor1.provider.SuggestionProvider"
        android:name="com.example.main.provider.SuggestionProvider"
        android:exported="false"
        android:enabled="true"
        />
</application>

flavor1/res/xml/search.xml

<searchable xmlns:android="http://schemas.android.com/apk/res/android"
    android:label="@string/app_name"
    android:includeInGlobalSearch="false"
    android:voiceSearchMode="showVoiceSearchButton|launchRecognizer"
    android:searchMode="queryRewriteFromText"
    android:searchSuggestAuthority="com.example.flavor1.provider.SuggestionProvider"
    android:searchSuggestSelection="title LIKE ?"
    android:searchSuggestThreshold="0"
    >
</searchable>

flavor2/AndroidManifest.xml

<application>
    <provider
        tools:replace="android:authorities"
        android:authorities="com.example.flavor2.provider.SuggestionProvider"
        android:name="com.example.main.provider.SuggestionProvider"
        android:exported="false"
        android:enabled="true"
        />
</application>

flavor2/res/xml/search.xml

<searchable xmlns:android="http://schemas.android.com/apk/res/android"
    android:label="@string/app_name"
    android:includeInGlobalSearch="false"
    android:voiceSearchMode="showVoiceSearchButton|launchRecognizer"
    android:searchMode="queryRewriteFromText"
    android:searchSuggestAuthority="com.example.flavor2.provider.SuggestionProvider"
    android:searchSuggestSelection="title LIKE ?"
    android:searchSuggestThreshold="0"
    >
</searchable>