PageTracker is the recommended approach for tracking user interactions in the Permutive Android SDK. It provides automatic Pageview event tracking, engagement metrics, and seamless integration with Permutive’s insights platform.
Why Use PageTracker?
PageTracker is the primary tracking method because it:
Integrates with standard events - Works seamlessly with Permutive’s event schema
Tracks engagement automatically - Measures time spent and scroll depth
Enables contextual cohorts - Supports content-based targeting when URLs are provided
Provides better insights - Delivers richer data to the Permutive platform
Associates related events - Groups events with page context
Default to PageTracker for tracking screens and content views in your app. Use EventTracker only for standalone events not associated with a page or screen.
Basic Usage
Creating a PageTracker
A PageTracker object should be created when the user starts viewing a page/screen, and closed when viewing finishes.
import androidx.appcompat.app.AppCompatActivity
import android.net.Uri
import android.os.Bundle
import com.permutive.android.EventProperties
import com.permutive.android.PageTracker
class ArticleActivity : AppCompatActivity () {
private val permutive by lazy {
(application as MyApplication).permutive
}
private lateinit var pageTracker: PageTracker
override fun onCreate (savedInstanceState: Bundle ?) {
super . onCreate (savedInstanceState)
setContentView (R.layout.activity_article)
// Create the PageTracker with properties
pageTracker = permutive. trackPage (
title = "Article Title" ,
url = Uri. parse ( "https://www.example.com/article" ),
referrer = Uri. parse ( "https://www.example.com/home" ),
eventProperties = EventProperties. from (
"article_id" to 12345 ,
"category" to "technology" ,
"author" to "John Doe"
)
)
}
override fun onPause () {
super . onPause ()
pageTracker. pause ()
}
override fun onResume () {
super . onResume ()
pageTracker. resume ()
}
override fun onDestroy () {
super . onDestroy ()
pageTracker. close ()
}
}
Lifecycle Management
When to Create
Create the PageTracker in onCreate():
override fun onCreate (savedInstanceState: Bundle ?) {
super . onCreate (savedInstanceState)
setContentView (R.layout.activity_main)
pageTracker = permutive. trackPage (...) // Create early
loadContent () // Then load your content
}
When to Pause/Resume
Call pause() and resume() to track engagement time accurately:
override fun onPause () {
super . onPause ()
// User can't see the page - stop tracking time
pageTracker. pause ()
}
override fun onResume () {
super . onResume ()
// User can see the page again - resume tracking time
pageTracker. resume ()
}
When to Close
Always close the PageTracker when done:
override fun onDestroy () {
super . onDestroy ()
// Page viewing finished - send completion event
pageTracker. close ()
}
⚠️ Important: Always Close
Failing to close PageTracker can lead to memory leaks and inaccurate engagement metrics.
Tracking Engagement
Tracking Time Spent
PageTracker automatically tracks time spent by using pause() and resume():
class ArticleActivity : AppCompatActivity () {
private lateinit var pageTracker: PageTracker
override fun onCreate (savedInstanceState: Bundle ?) {
super . onCreate (savedInstanceState)
pageTracker = permutive. trackPage (...)
// Time tracking starts automatically
}
override fun onPause () {
super . onPause ()
pageTracker. pause () // Pause time tracking
}
override fun onResume () {
super . onResume ()
pageTracker. resume () // Resume time tracking
}
override fun onDestroy () {
super . onDestroy ()
pageTracker. close () // Final engaged time sent
}
}
The engaged time is sent in the PageviewComplete event when you call close().
Use updatePercentageViewed() to track how much content the user has read:
class ArticleActivity : AppCompatActivity () {
private lateinit var pageTracker: PageTracker
override fun onCreate (savedInstanceState: Bundle ?) {
super . onCreate (savedInstanceState)
setContentView (R.layout.activity_article)
pageTracker = permutive. trackPage (...)
// Add scroll listener
scrollView.viewTreeObserver. addOnScrollChangedListener {
val contentHeight = scrollView. getChildAt ( 0 ).height
val visibleHeight = scrollView.scrollY. toFloat () + scrollView.height
val percentageViewed = visibleHeight / contentHeight
pageTracker. updatePercentageViewed (percentageViewed)
}
}
}
RecyclerView Example:
recyclerView. addOnScrollListener ( object : RecyclerView . OnScrollListener () {
override fun onScrolled (recyclerView: RecyclerView , dx: Int , dy: Int ) {
val layoutManager = recyclerView.layoutManager as LinearLayoutManager
val totalItems = layoutManager.itemCount
val lastVisibleItem = layoutManager. findLastVisibleItemPosition ()
val percentageViewed = (lastVisibleItem + 1 ). toFloat () / totalItems
pageTracker. updatePercentageViewed (percentageViewed)
}
})
Tracking Additional Events
Track other events associated with the page using PageTracker’s track() methods:
class ArticleActivity : AppCompatActivity () {
private lateinit var pageTracker: PageTracker
override fun onCreate (savedInstanceState: Bundle ?) {
super . onCreate (savedInstanceState)
pageTracker = permutive. trackPage (...)
// Track page-related events
shareButton. setOnClickListener {
pageTracker. track (
"ArticleShared" ,
EventProperties. from (
"share_method" to "twitter"
)
)
}
commentButton. setOnClickListener {
pageTracker. track ( "CommentAdded" )
}
}
}
URL Schemes
Web Content
For web content, use full URLs:
pageTracker = permutive. trackPage (
title = "Article Title" ,
url = Uri. parse ( "https://www.example.com/articles/tech-news" ),
referrer = Uri. parse ( "https://www.example.com/home" )
)
App Screens
For native app screens, use custom URI schemes:
// Option 1: app:// scheme
pageTracker = permutive. trackPage (
title = "Home Screen" ,
url = Uri. parse ( "app://home" )
)
// Option 2: your-app:// scheme
pageTracker = permutive. trackPage (
title = "Profile Screen" ,
url = Uri. parse ( "myapp://profile/user/12345" )
)
// Option 3: https with app path
pageTracker = permutive. trackPage (
title = "Settings" ,
url = Uri. parse ( "https://app.example.com/settings" )
)
💡 URLs Enable Contextual Cohorts
When you provide URLs, Permutive can generate contextual cohorts based on content analysis. This works for publicly accessible web URLs.
Events Generated
PageTracker automatically generates these events:
Pageview Event
Tracked when PageTracker is created:
Event: Pageview
Properties:
- title: "Article Title"
- url: "https://www.example.com/article"
- referrer: "https://www.example.com/home"
- [your custom properties]
PageviewComplete Event
Tracked when PageTracker is closed:
Event: PageviewComplete
Properties:
- engaged_time: 45.2 (seconds)
- completion: 0.75 (75% viewed)
- [your custom properties]
PageviewEngagement Events
Periodic engagement events (if configured):
Event: PageviewEngagement
Properties:
- engaged_time: 15.0 (seconds)
- [your custom properties]
Use Cases
class ArticleActivity : AppCompatActivity () {
private lateinit var pageTracker: PageTracker
override fun onCreate (savedInstanceState: Bundle ?) {
super . onCreate (savedInstanceState)
val article = intent. getParcelableExtra < Article >( "article" )
pageTracker = permutive. trackPage (
title = article.title,
url = Uri. parse (article.url),
referrer = Uri. parse (intent. getStringExtra ( "referrer" )),
eventProperties = EventProperties. from (
"article_id" to article.id,
"category" to article.category,
"author" to article.author,
"published_at" to article.publishedDate,
"word_count" to article.wordCount
)
)
setupScrollTracking ()
}
}
class ProductActivity : AppCompatActivity () {
private lateinit var pageTracker: PageTracker
override fun onCreate (savedInstanceState: Bundle ?) {
super . onCreate (savedInstanceState)
val product = viewModel.product. value
pageTracker = permutive. trackPage (
title = product.name,
url = Uri. parse ( "app://product/ ${ product.id } " ),
eventProperties = EventProperties. from (
"product_id" to product.id,
"product_name" to product.name,
"category" to product.category,
"price" to product.price,
"in_stock" to product.inStock
)
)
addToCartButton. setOnClickListener {
pageTracker. track (
"ProductAddedToCart" ,
EventProperties. from (
"product_id" to product.id,
"quantity" to 1
)
)
}
}
}
Video App (Non-Video Content)
For video content itself, use MediaTracker . For video details/description pages: class VideoDetailsActivity : AppCompatActivity () {
private lateinit var pageTracker: PageTracker
override fun onCreate (savedInstanceState: Bundle ?) {
super . onCreate (savedInstanceState)
pageTracker = permutive. trackPage (
title = video.title,
url = Uri. parse ( "app://video/ ${ video.id } /details" ),
eventProperties = EventProperties. from (
"video_id" to video.id,
"video_title" to video.title,
"genre" to video.genre,
"duration_seconds" to video.durationSeconds
)
)
playButton. setOnClickListener {
pageTracker. track ( "VideoPlayButtonClicked" )
startVideoPlayer ()
}
}
}
When to Use PageTracker vs EventTracker
Use PageTracker When:
✅ Tracking article/content views
✅ Tracking screen views in your app
✅ Measuring time spent on a page/screen
✅ Tracking scroll depth
✅ You want contextual cohorts
✅ Events are associated with a page context
Use EventTracker Only When:
⚠️ Tracking standalone events (button clicks not tied to pages)
⚠️ Tracking background events
⚠️ Events have no page context
💡 Default to PageTracker
Most customer implementations should primarily use PageTracker. EventTracker is for specific use cases.
See Event Tracking for EventTracker documentation.
Troubleshooting
PageTracker Not Tracking Time
Problem: Engaged time is always 0.Cause: Not calling pause() and resume().Solution: Implement lifecycle callbacks:override fun onPause () {
super . onPause ()
pageTracker. pause () // IMPORTANT
}
override fun onResume () {
super . onResume ()
pageTracker. resume () // IMPORTANT
}
Problem: App memory usage grows over time.Cause: Not closing PageTracker.Solution: Always close in onDestroy():override fun onDestroy () {
super . onDestroy ()
pageTracker. close () // IMPORTANT
}
Contextual Cohorts Not Working
Problem: No contextual cohorts for pages.Causes:
Feature not enabled
Not providing URLs
URLs not publicly accessible
Solutions:
Contact Customer Success Manager to enable
Always provide URLs in trackPage()
Ensure URLs are publicly accessible for web content
See Contextual Data for details.
Problem: Pageview events not tracked.Solutions:
Enable debug logging: permutive.setDeveloperMode(true)
Check logs for “Published events with names (Pageview)”
Verify event properties match your schema
See Verification Guide
Best Practices
Create PageTracker in onCreate()
Call pause() in onPause()
Call resume() in onResume()
Call close() in onDestroy()
Provide URLs when possible for contextual cohorts
Track scroll depth for content pages
Use PageTracker for all page/screen views
Forget to close PageTracker
Create multiple PageTrackers for the same page
Skip pause/resume calls
Use EventTracker when PageTracker is more appropriate
API Reference
For complete API documentation, see the Javadocs .
PageTracker Interface
pause() - Pause engagement tracking
resume() - Resume engagement tracking
updatePercentageViewed(percentage: Float) - Update scroll depth
track(eventName: String) - Track event with this page context
track(eventName: String, properties: EventProperties?) - Track event with properties
close() - Complete page tracking and send PageviewComplete event