Screen Name Mapping
Screen name mapping lets you show friendly, human-readable screen names in your Paylisher dashboard instead of raw class names. Reports become readable for non-technical teammates, and you can rename screens without touching your code — just edit a JSON file.
| Raw class name | Mapped screen name |
|---|---|
HomeFragment | Home |
ProfileFragment | Profile |
SettingsActivity | Settings |
Without mapping, the SDK still reports a clean default name (it strips the Fragment suffix, so HomeFragment becomes Home). Mapping is what lets you override that default with any label you like — including localized or product-specific names such as Ana Sayfa or Shopping Cart.
How it works
- On startup, the SDK prepares a screen mapper as part of
PaylisherAndroid.setup(). - The first time a screen is captured, the mapper lazily loads
paylisher_screens.jsonfrom your app'sassets/folder, then caches it for the rest of the process. - When the user navigates to a screen, the SDK looks up the screen's fully qualified class name in the mapping.
- If a match is found, the mapped name is used. Otherwise the default name is used.
- The resulting name is sent with the
$screenevent and shown in the dashboard.
The mapper is created for you automatically — there is no extra setup call. If paylisher_screens.json is missing, mapping is simply skipped and default names are used, so adding the file is completely optional and non-breaking.
Because the file is read once per process and then cached, changes to
paylisher_screens.jsononly take effect after the app is restarted.
1. Add the mapping file
Create the file at:
app/src/main/assets/paylisher_screens.json
If the
assetsfolder doesn't exist yet, create it underapp/src/main/. The file must live at the root ofassets/— the SDK looks forpaylisher_screens.jsondirectly, so a copy placed in anassets/subfolder is silently ignored.
The file is a flat JSON object where each key is a fully qualified class name and each value is the display name:
{
"com.myapp.fragments.HomeFragment": "Home",
"com.myapp.fragments.ProfileFragment": "Profile",
"com.myapp.fragments.CartFragment": "Shopping Cart",
"com.myapp.MainActivity": "Home Screen",
"com.myapp.ProductDetailActivity": "Product Detail"
}
Important: Keys must be the fully qualified class name (package + class), not just the simple class name. If mapping doesn't work, this is almost always the cause — see Troubleshooting.
Keep it flat, values as strings. Every value must be a plain string. A nested object or array value will abort loading of the file.
You can map both Fragments and Activities in the same file. Whether Activity mapping actually takes effect depends on your tracking mode (see below).
2. Enable screen tracking
Screen tracking is enabled by default, so no code change is required for the common case. The relevant config options are:
val config = PaylisherAndroidConfig(
apiKey = PAYLISHER_API_KEY,
host = PAYLISHER_HOST,
).apply {
// Fragment screen views (single-activity architecture). true by default.
captureFragmentScreenViews = true
// Activity screen views. true by default, but automatically
// suppressed while Fragment tracking is enabled to avoid duplicates.
captureScreenViews = true
}
PaylisherAndroid.setup(this, config)
Screen tracking installs only when the SDK is initialized with an
Applicationcontext (the recommended setup, from yourApplicationsubclass — see Getting Started).
Fragments vs. Activities
Modern Android apps typically use a single Activity with multiple Fragments, so Fragment tracking is enabled by default. To prevent duplicate $screen events, Activity-level tracking is automatically disabled whenever captureFragmentScreenViews = true.
This has a practical consequence for mapping:
- Default setup (Fragment tracking on): Your Fragment entries in
paylisher_screens.jsonare used. Activity entries are ignored because Activity tracking is suppressed. - Legacy setup (Activity tracking only): Set
captureFragmentScreenViews = falseandcaptureScreenViews = true. Now your Activity entries are used.
// Use this only if your app is Activity-based and you want Activity mapping.
config.captureFragmentScreenViews = false
config.captureScreenViews = true
Name resolution order
Fragments
When a Fragment becomes visible (onResume), the SDK resolves its screen name in this order:
- Custom transformer —
fragmentScreenNameTransformer, if you set one (highest priority). - JSON mapping — a match for the Fragment's fully qualified class name in
paylisher_screens.json. - Default — the simple class name with the
Fragmentsuffix removed (HomeFragment→Home).
NavHostFragment (the Navigation Component container) is skipped so it never shows up as a screen.
Activities
When Activity tracking is active, the SDK resolves the name in this order:
- JSON mapping — a match for the Activity's fully qualified class name.
android:label— the<activity>'s label, if set and different from the application label.- Default — the simple Activity class name (package stripped, e.g.
MainActivity).
Alternative: custom transformer (no JSON)
If you'd rather resolve Fragment names in code instead of maintaining a JSON file, provide a fragmentScreenNameTransformer. It takes precedence over the JSON mapping:
config.fragmentScreenNameTransformer = { fragment ->
when (fragment) {
is HomeFragment -> "Home"
is ProfileFragment -> "Profile"
is SettingsFragment -> "Settings"
else -> fragment.javaClass.simpleName
}
}
Because this uses is type checks rather than string class names, it keeps working even after R8/ProGuard obfuscation — see the next section.
R8 / ProGuard (release builds)
JSON mapping relies on matching the fully qualified class name at runtime. When R8/ProGuard obfuscation is enabled (typical for release builds), class names are renamed — com.myapp.HomeFragment may become something like a.b.c — and the lookup no longer matches.
| Build type | Runtime class name | Mapping result |
|---|---|---|
| Debug | com.myapp.HomeFragment | ✅ Works |
| Release (R8 on) | a.b.c | ❌ Does not work |
You have three options.
Option 1 — Keep the class names (recommended)
Add keep rules to proguard-rules.pro:
# Paylisher screen mapping — preserve Fragment and Activity class names
-keepnames class * extends androidx.fragment.app.Fragment
-keepnames class * extends android.app.Activity
-keepnames class * extends androidx.appcompat.app.AppCompatActivity
-keepnames preserves the class names while still allowing method obfuscation.
Option 2 — Keep only the classes you map
If you don't want to preserve every Fragment/Activity name, keep only the ones referenced in your mapping:
-keepnames class com.myapp.fragments.HomeFragment
-keepnames class com.myapp.fragments.ProfileFragment
-keepnames class com.myapp.MainActivity
-keepnames class com.myapp.ProductDetailActivity
Option 3 — Use a transformer instead
Use fragmentScreenNameTransformer (see above). Because it relies on is type checks rather than string class names, it is unaffected by obfuscation and needs no keep rules.
Verify the release mapping
After a release build, check app/build/outputs/mapping/release/mapping.txt. A preserved class looks like this (name unchanged on both sides):
com.myapp.fragments.HomeFragment -> com.myapp.fragments.HomeFragment
If instead you see the name renamed, JSON mapping won't match:
com.myapp.fragments.HomeFragment -> a.b.c
A typical build.gradle.kts release block:
android {
buildTypes {
release {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro",
)
}
}
}
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Mapping has no effect | Key is not the fully qualified class name | Use fragment.javaClass.name (package + class) as the JSON key |
| Mapping has no effect | Invalid/malformed JSON | A JSON syntax error fails silently (only logged with debug = true) and falls back to default names — validate the file |
| Mapping has no effect | File in a subfolder | The file must be at the root: app/src/main/assets/paylisher_screens.json |
| Some screens map, others don't | Missing entry | Add the class name to the JSON, or accept the default name |
| Activity names not mapping | Fragment tracking is on | Activity tracking is suppressed by default; set captureFragmentScreenViews = false if your app is Activity-based |
| Works in debug, not in release | R8/ProGuard obfuscation | Add keep rules or use a transformer (see above) |
| Edits to the JSON don't apply | File cached | The file is read once per process — restart the app |
Enable debug logging
Filter Logcat by the Paylisher tag to see the mapper at work. Per-lookup diagnostics (the class name being looked up and the match result) are logged as you navigate. To also see the load summary and integration logs, enable debug:
config.debug = true
Example output:
✅ Loaded 5 screen mappings from paylisher_screens.json
🔍 Fragment class name: com.myapp.fragments.HomeFragment
✅ Using mapped name: Home
Example paylisher_screens.json
{
"com.myapp.fragments.HomeFragment": "Home",
"com.myapp.fragments.ProfileFragment": "Profile",
"com.myapp.fragments.SettingsFragment": "Settings",
"com.myapp.fragments.CartFragment": "Shopping Cart",
"com.myapp.fragments.CheckoutFragment": "Checkout",
"com.myapp.MainActivity": "Home Screen",
"com.myapp.ProductDetailActivity": "Product Detail",
"com.myapp.LoginActivity": "Login"
}
Using the same feature on iOS? See the iOS Screen Name Mapping guide — the concept is identical, but iOS loads the file from the app bundle and keys are class names (module prefix optional).