Using PreferenceScreen with applicationIdSuffix
Asked Answered
D

3

6

I'm having some problems making PreferenceScreen and applicationIdSuffix work together.

Supposing my app package/applicationId is com.myapp in Gradle and in AndroidManifest.xml, and applicationIdSuffix is defined as:

buildTypes {
    debug {
        applicationIdSuffix '.dev'
    }
}

If I define a PreferenceScreen as the following

<PreferenceScreen
    android:key="key_about" android:summary="something" android:title="About">
    <intent
        android:targetClass="com.myapp.activities.AboutActivity"
        android:targetPackage="com.myapp" />
</PreferenceScreen>

when launching the debug version of the app I get an exception

java.lang.SecurityException: Permission Denial: starting Intent { (...) } from ProcessRecord{(...)} (pid=13658, uid=10105) not exported from uid 10067

which makes sense, as I'm trying to launch an activity from a different application. The problem is that I can't find a way to Android launch the right activity.

If I change targetClass to ".activities.AboutActivity" it still can't find the activity

ActivityNotFoundException: Unable to find explicit activity class {com.myapp.dev/.activities.AboutActivity}

I even tried to define a value for each of the versions with the correct package:

android:targetClass="@string/classname"
android:targetPackage="@string/packagename"

but it can't find the right activity:

ActivityNotFoundException: Unable to find explicit activity class
    {com.myapp.dev/com.myapp.dev.activities.AboutActivity};
have you declared this activity in your AndroidManifest.xml?

So how can I make this work?

Deland answered 29/6, 2015 at 11:22 Comment(1)
According to the exception here, I think it says that you should define your AboutActivity in the manifest.Cyn
C
6

Your confusion comes from the fact that applicationIdSuffix only changes the package name of the application (its unique id), but does not change the java package name of your classes inside the application, including your Activity.

This means that you should declare in your preferences:

android:targetClass="com.myapp.activities.AboutActivity"
android:targetPackage="@string/packagename"

Where targetClass is always the same, while targetPackage depends on your build type and can be com.myapp or com.myapp.dev.

Crossly answered 6/7, 2015 at 15:5 Comment(2)
won't this still fail, given that the applicationId won't have the suffix?Maupassant
You should define the string constant in 2 different folders matching the declared build types and give the string the same value as the application Id (with and without suffix). Unfortunately it's not possible yet to use applicationId as a variable in resources, you can only do it in the manifest.Crossly
C
2

There is currently no direct way to include the effective applicationId into other XML files than the AndroidManifest (where it is available as ${applicationId}), so you need to create a string property for it.

Because the gradle applicationId value changes based on the flavor, you can not use it directly to define the string property. Instead, you need to create an afterEvaluate rule in build.gradle to generate it for all variants:

afterEvaluate {
    android.applicationVariants.all { variant ->
        variant.resValue 'string', 'application_id', variant.applicationId
    }
}

This will create a @string/application_id (or R.string.application_id) that you can use wherever needed, e.g. in your layout / preferences XML (the class name doesn't need to be changed because it is based on the java package that's the same for all flavors):

<PreferenceScreen
    android:key="key_about" android:summary="something" android:title="About">
    <intent
        android:targetClass="com.myapp.activities.AboutActivity"
        android:targetPackage="@string/application_id" />
</PreferenceScreen>

This solution is based on https://gist.github.com/Takhion/74b67cb518e90faf2708 which also provides a provider property.

Coyne answered 22/3, 2019 at 13:11 Comment(1)
Thank you! afterEvaluate should be under productFlavors (itself under android)Lathy
U
0

What I do in my app is attach an onPreferenceClickListener to the preference in its host activity, and build the intent inside that listener's onPreferenceClick method. By generating the intent from code, you directly reference the target class and the question of packages never comes up.

Unutterable answered 6/7, 2015 at 19:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.