From 64b877f01dc8c96022e5abfc4b288b9be736de55 Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Tue, 30 Jan 2024 12:40:02 +0100 Subject: [PATCH 1/7] docs: add and improve javadoc comments on ParselyTracker --- .../main/java/com/example/MainActivity.java | 2 +- .../parsely/parselyandroid/ParselyTracker.kt | 109 ++++++++++-------- 2 files changed, 59 insertions(+), 52 deletions(-) diff --git a/example/src/main/java/com/example/MainActivity.java b/example/src/main/java/com/example/MainActivity.java index 8b45e35..dec5a4a 100644 --- a/example/src/main/java/com/example/MainActivity.java +++ b/example/src/main/java/com/example/MainActivity.java @@ -142,7 +142,7 @@ public void trackReset(View view) { private boolean engagementIsActive() { return (boolean) invokePrivateMethod("engagementIsActive"); } - + @Nullable private Double getEngagementInterval() { return (Double) invokePrivateMethod("getEngagementInterval"); diff --git a/parsely/src/main/java/com/parsely/parselyandroid/ParselyTracker.kt b/parsely/src/main/java/com/parsely/parselyandroid/ParselyTracker.kt index e6adbe8..1494f65 100644 --- a/parsely/src/main/java/com/parsely/parselyandroid/ParselyTracker.kt +++ b/parsely/src/main/java/com/parsely/parselyandroid/ParselyTracker.kt @@ -21,21 +21,20 @@ import org.jetbrains.annotations.TestOnly /** * Tracks Parse.ly app views in Android apps * - * Accessed as a singleton. Maintains a queue of pageview events in memory and periodically + * Accessed as a singleton. Maintains a queue of events in memory and periodically * flushes the queue to the Parse.ly pixel proxy server. */ public interface ParselyTracker { /** - * Register a pageview event using a URL and optional metadata. + * Register a pageview event using a URL and optional metadata. You should only be call this once per page view. * - * @param url The URL of the article being tracked - * (eg: "http://example.com/some-old/article.html") - * @param urlRef Referrer URL associated with this video view. + * @param url The URL of the article being tracked (eg: "http://example.com/some-old/article.html") + * @param urlRef The url of the page that linked to the viewed page. Analogous to HTTP referer * @param urlMetadata Optional metadata for the URL -- not used in most cases. Only needed - * when `url` isn't accessible over the Internet (i.e. app-only - * content). Do not use this for **content also hosted on** URLs Parse.ly - * would normally crawl. + * when `url` isn't accessible over the Internet (i.e. app-only + * content). Do not use this for **content also hosted on** URLs Parse.ly + * would normally crawl. * @param extraData A Map of additional information to send with the event. */ public fun trackPageview( @@ -48,13 +47,18 @@ public interface ParselyTracker { /** * Start engaged time tracking for the given URL. * + * Start engaged time tracking for the given URL. Once called, the Parse.ly tracking script + * will automatically send `heartbeat` events periodically to capture engaged time for this url + * until engaged time tracking stops. * - * This starts a timer which will send events to Parse.ly on a regular basis - * to capture engaged time for this URL. The value of `url` should be a URL for - * which `trackPageview` has been called. + * This call also automatically stops tracking engaged time for any urls that are not + * the current url. * - * @param url The URL to track engaged time for. - * @param urlRef Referrer URL associated with this video view. + * The value of `url` should be a URL for which [trackPageview] has been called. + * + * @param url The URL of the tracked article (eg: “http://example.com/some-old/article.html“) + * @param urlRef The url of the page that linked to the page being engaged with. Analogous to HTTP referer + * @param extraData A map of additional information to send with the generated `heartbeat` events */ public fun startEngagement( url: String, @@ -63,38 +67,36 @@ public interface ParselyTracker { ) /** - * Stop engaged time tracking. - * - * * Stops the engaged time tracker, sending any accumulated engaged time to Parse.ly. - * NOTE: This **must** be called in your `MainActivity` during various Android lifecycle events - * like `onPause` or `onStop`. Otherwise, engaged time tracking may keep running in the background - * and Parse.ly values may be inaccurate. + * + * NOTE: This **must** be called during various Android lifecycle events like + * `onPause` or `onStop`. Otherwise, engaged time tracking may keep running + * in the background and Parse.ly values may be inaccurate. */ public fun stopEngagement() /** * Start video tracking. * - * - * Starts tracking view time for a video being viewed at a given url. Will send a `videostart` - * event unless the same url/videoId had previously been paused. + * This starts tracking view time for a video that someone is watching at a given url. + * It will send a `videostart` event unless the same url/videoId had previously been paused. * Video metadata must be provided, specifically the video ID and video duration. * - * * The `url` value is *not* the URL of a video, but the post which contains the video. If the video * is not embedded in a post, then this should contain a well-formatted URL on the customer's - * domain (e.g. http:///app-videos). This URL doesn't need to return a 200 status + * domain (e.g. http://example.com/app-videos). This URL doesn't need to return a 200 status * when crawled, but must but well-formatted so Parse.ly systems recognize it as belonging to * the customer. * - * @param url URL of post the video is embedded in. If videos is not embedded, a - * valid URL for the customer should still be provided. - * (e.g. http:///app-videos) - * @param urlRef Referrer URL associated with this video view. - * @param videoMetadata Metadata about the video being tracked. - * @param extraData A Map of additional information to send with the event. - */ + * If a video is already being tracked when this method is called, unless it's the same video, + * the existing video tracking will be stopped and a new `videostart` event will be sent for the new video. + * + * @param url URL of post with the embedded video. If you haven’t embedded the video, + * then send a valid URL matching your domain. (e.g. http://example.com/app-videos) + * @param urlRef The url of the page that linked to the page being engaged with. Analogous to HTTP referer + * @param videoMetadata Metadata about the tracked video + * @param extraData A Map of additional information to send with the event + */ public fun trackPlay( url: String, urlRef: String = "", @@ -103,30 +105,23 @@ public interface ParselyTracker { ) /** - * Pause video tracking. + * Pause video tracking for an ongoing video. If [trackPlay] is immediately called again + * for the same video, a new `videostart` event will not be sent. This models a user pausing + * a playing video. * * - * Pauses video tracking for an ongoing video. If [.trackPlay] is immediately called again for - * the same video, a new video start event will not be sent. This models a user pausing a - * playing video. - * - * - * NOTE: This or [.resetVideo] **must** be called in your `MainActivity` during various Android lifecycle events + * NOTE: This or [resetVideo] **must** be called during various Android lifecycle events * like `onPause` or `onStop`. Otherwise, engaged time tracking may keep running in the background * and Parse.ly values may be inaccurate. */ public fun trackPause() /** - * Reset tracking on a video. - * - * - * Stops video tracking and resets internal state for the video. If [.trackPlay] is immediately - * called for the same video, a new video start event is set. This models a user stopping a - * video and (on [.trackPlay] being called again) starting it over. - * + * Stops video tracking and resets internal state for the video. + * If [trackPlay] is immediately called for the same video, a new `videostart` event is set. + * This models a user stopping a video and (on [trackPlay] being called again) starting it over. * - * NOTE: This or [.trackPause] **must** be called in your `MainActivity` during various Android lifecycle events + * NOTE: This or [trackPause] **must** be called during various Android lifecycle events * like `onPause` or `onStop`. Otherwise, engaged time tracking may keep running in the background * and Parse.ly values may be inaccurate. */ @@ -143,13 +138,16 @@ public interface ParselyTracker { } /** - * Singleton instance factory Note: this must be called before [.sharedInstance] + * Singleton instance factory. This **must** be called before [sharedInstance] + * If this method is called when an instance already exists, a [ParselyAlreadyInitializedException] will be thrown. * - * @param siteId The Parsely public site id (eg "example.com") + * + * @param siteId The Parsely public site id (eg "example.com") * @param flushInterval The interval at which the events queue should flush, in seconds - * @param context The current Android application context - * @param dryRun If set to `true`, events **won't** be sent to Parse.ly server - * @return The singleton instance + * @param context The current Android application context + * @param dryRun If set to `true`, events **won't** be sent to Parse.ly server + * @return The singleton instance + * @throws ParselyAlreadyInitializedException if the ParselyTracker instance is already initialized. */ @JvmStatic @JvmOverloads @@ -165,6 +163,15 @@ public interface ParselyTracker { instance = ParselyTrackerInternal(siteId, flushInterval, context, dryRun) } + /** + * Returns the singleton instance of the ParselyTracker. + * + * This method **must** be called after the [init] method. + * If the [init] method hasn't been called before this method, a [ParselyNotInitializedException] will be thrown. + * + * @return The singleton instance of ParselyTracker. + * @throws ParselyNotInitializedException if the ParselyTracker instance is not initialized. + */ @JvmStatic public fun sharedInstance(): ParselyTracker = ensureInitialized() From 626c529b9b6fef82ab7fb35d5f4714d89f33268a Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Tue, 30 Jan 2024 15:59:12 +0100 Subject: [PATCH 2/7] feat: introduce Dokka and add README.md to the parsely module --- parsely/README.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ parsely/build.gradle | 9 +++++++++ settings.gradle | 4 +++- 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 parsely/README.md diff --git a/parsely/README.md b/parsely/README.md new file mode 100644 index 0000000..b44395d --- /dev/null +++ b/parsely/README.md @@ -0,0 +1,44 @@ +# Module parsely + +The Parse.ly Android SDK is a library providing Parse.ly tracking functionality to native Android apps. + +## Initialization + +The SDK must be initialized before it can be used. +It is recommended to initialize the SDK as early as possible in the app lifecycle. +The SDK can be initialized with the following code: + +```kotlin +ParselyTracker.init("example.com", /*site id*/ 30, /*flush interval*/ this, /*context*/ false, /*dry-run*/) +``` + +## Usage + +The SDK can be used in two ways, depending on the needs of the consuming app + +### Via a static call +To obtain a reference to the SDK statically, one can use `sharedInstance()` method: + +```kotlin +ParselyTracker.sharedInstance().trackPageView("example.com") +``` + +### Via an interface + +It's possible to use the SDK via an interface, which allows for easier testing and mocking of the SDK. + +```kotlin +fun onCreate(...) { + ParselyTracker.init(...) + val tracker = ParselyTracker.sharedInstance() + + val someClass = SomeClass(tracker) + someClass.openArticle() +} + +class SomeClass(val tracker: ParselyTracker) { + fun openArticle() { + tracker.startEngagement() + } +} +``` diff --git a/parsely/build.gradle b/parsely/build.gradle index 51624b9..8055817 100644 --- a/parsely/build.gradle +++ b/parsely/build.gradle @@ -5,6 +5,7 @@ plugins { id 'org.jetbrains.kotlin.android' id 'org.jetbrains.kotlinx.kover' id 'org.jetbrains.kotlinx.binary-compatibility-validator' + id 'org.jetbrains.dokka' } ext { @@ -67,6 +68,14 @@ android { kotlin { explicitApi(ExplicitApiMode.Strict) } + + dokkaHtml.configure { + dokkaSourceSets { + named("main") { + includes.from("README.md") + } + } + } } dependencies { diff --git a/settings.gradle b/settings.gradle index edc8ba3..674b9b6 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,12 +1,14 @@ pluginManagement { gradle.ext.agpVersion = '7.2.2' + gradle.ext.kotlinVersion = '1.9.10' plugins { id 'com.android.application' version gradle.ext.agpVersion id 'com.android.library' version gradle.ext.agpVersion - id 'org.jetbrains.kotlin.android' version '1.9.10' + id 'org.jetbrains.kotlin.android' version gradle.ext.kotlinVersion id 'org.jetbrains.kotlinx.kover' version '0.7.4' id 'org.jetbrains.kotlinx.binary-compatibility-validator' version '0.14.0' + id 'org.jetbrains.dokka' version gradle.ext.kotlinVersion } repositories { From c68e6eceeb07734c3d6f3e5de6441f7264676308 Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Tue, 30 Jan 2024 16:01:26 +0100 Subject: [PATCH 3/7] feat: suppress all inherited members that were not overriden in a given class. --- parsely/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/parsely/build.gradle b/parsely/build.gradle index 8055817..276bb2d 100644 --- a/parsely/build.gradle +++ b/parsely/build.gradle @@ -70,6 +70,7 @@ android { } dokkaHtml.configure { + suppressInheritedMembers.set(true) dokkaSourceSets { named("main") { includes.from("README.md") From a68c8824eb7d265af2bbbed3bf7f93993de84431 Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Tue, 30 Jan 2024 16:02:12 +0100 Subject: [PATCH 4/7] ci: generate documentation and publish to github pages --- .github/workflows/generate-docs.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/workflows/generate-docs.yml diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml new file mode 100644 index 0000000..f18c342 --- /dev/null +++ b/.github/workflows/generate-docs.yml @@ -0,0 +1,24 @@ +name: Generate documentation + +on: + release: + types: [ released ] + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: set up JDK + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + - name: Generate API documentation + run: ./gradlew dokkaHtml + - name: Deploy API documentation to Github Pages + uses: JamesIves/github-pages-deploy-action@v4 + with: + branch: gh-pages + folder: parsely/build/dokka/html From 7c711aa8ee1100cae0ec5f1fb5bb27be8fc309d4 Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Thu, 1 Feb 2024 14:43:38 +0100 Subject: [PATCH 5/7] docs: revert adding dokka --- .github/workflows/generate-docs.yml | 37 ----------------------------- parsely/build.gradle | 10 -------- settings.gradle | 4 +--- 3 files changed, 1 insertion(+), 50 deletions(-) delete mode 100644 .github/workflows/generate-docs.yml diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml deleted file mode 100644 index f55147c..0000000 --- a/.github/workflows/generate-docs.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Generate and publish documentation - -on: - release: - types: [ released ] - workflow_dispatch: - -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages -permissions: - contents: read - pages: write - id-token: write - -jobs: - deploy: - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: set up JDK - uses: actions/setup-java@v3 - with: - java-version: '17' - distribution: 'temurin' - - name: Generate API documentation - run: ./gradlew dokkaHtml - - name: Setup Pages - uses: actions/configure-pages@v4 - - name: Upload artifact - uses: actions/upload-pages-artifact@v3 - with: - path: 'parsely/build/dokka/html' - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 diff --git a/parsely/build.gradle b/parsely/build.gradle index b9488a6..afe050f 100644 --- a/parsely/build.gradle +++ b/parsely/build.gradle @@ -5,7 +5,6 @@ plugins { id 'org.jetbrains.kotlin.android' id 'org.jetbrains.kotlinx.kover' id 'org.jetbrains.kotlinx.binary-compatibility-validator' - id 'org.jetbrains.dokka' } ext { @@ -70,15 +69,6 @@ android { explicitApi(ExplicitApiMode.Strict) jvmToolchain(17) } - - dokkaHtml.configure { - suppressInheritedMembers.set(true) - dokkaSourceSets { - named("main") { - includes.from("README.md") - } - } - } } dependencies { diff --git a/settings.gradle b/settings.gradle index e9463ad..3a08fd1 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,14 +1,12 @@ pluginManagement { gradle.ext.agpVersion = '8.2.2' - gradle.ext.kotlinVersion = '1.9.10' plugins { id 'com.android.application' version gradle.ext.agpVersion id 'com.android.library' version gradle.ext.agpVersion - id 'org.jetbrains.kotlin.android' version gradle.ext.kotlinVersion + id 'org.jetbrains.kotlin.android' version '1.9.10' id 'org.jetbrains.kotlinx.kover' version '0.7.4' id 'org.jetbrains.kotlinx.binary-compatibility-validator' version '0.14.0' - id 'org.jetbrains.dokka' version gradle.ext.kotlinVersion } repositories { From cfc30c1b6bc3b48904f4b3c1bba988b2daece3ff Mon Sep 17 00:00:00 2001 From: Wojtek Zieba Date: Thu, 1 Feb 2024 14:45:05 +0100 Subject: [PATCH 6/7] docs: remove module-level README The content of this file will be added to the documentation at https://docs.parse.ly/android-sdk/ --- parsely/README.md | 44 -------------------------------------------- 1 file changed, 44 deletions(-) delete mode 100644 parsely/README.md diff --git a/parsely/README.md b/parsely/README.md deleted file mode 100644 index b44395d..0000000 --- a/parsely/README.md +++ /dev/null @@ -1,44 +0,0 @@ -# Module parsely - -The Parse.ly Android SDK is a library providing Parse.ly tracking functionality to native Android apps. - -## Initialization - -The SDK must be initialized before it can be used. -It is recommended to initialize the SDK as early as possible in the app lifecycle. -The SDK can be initialized with the following code: - -```kotlin -ParselyTracker.init("example.com", /*site id*/ 30, /*flush interval*/ this, /*context*/ false, /*dry-run*/) -``` - -## Usage - -The SDK can be used in two ways, depending on the needs of the consuming app - -### Via a static call -To obtain a reference to the SDK statically, one can use `sharedInstance()` method: - -```kotlin -ParselyTracker.sharedInstance().trackPageView("example.com") -``` - -### Via an interface - -It's possible to use the SDK via an interface, which allows for easier testing and mocking of the SDK. - -```kotlin -fun onCreate(...) { - ParselyTracker.init(...) - val tracker = ParselyTracker.sharedInstance() - - val someClass = SomeClass(tracker) - someClass.openArticle() -} - -class SomeClass(val tracker: ParselyTracker) { - fun openArticle() { - tracker.startEngagement() - } -} -``` From 5742c39090f2815c7fde890b5f0605f4928bc209 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Zi=C4=99ba?= Date: Thu, 1 Feb 2024 16:48:36 +0100 Subject: [PATCH 7/7] Update parsely/src/main/java/com/parsely/parselyandroid/ParselyTracker.kt Co-authored-by: Ian Guedes Maia --- .../src/main/java/com/parsely/parselyandroid/ParselyTracker.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parsely/src/main/java/com/parsely/parselyandroid/ParselyTracker.kt b/parsely/src/main/java/com/parsely/parselyandroid/ParselyTracker.kt index 1494f65..3ee7a63 100644 --- a/parsely/src/main/java/com/parsely/parselyandroid/ParselyTracker.kt +++ b/parsely/src/main/java/com/parsely/parselyandroid/ParselyTracker.kt @@ -27,7 +27,7 @@ import org.jetbrains.annotations.TestOnly public interface ParselyTracker { /** - * Register a pageview event using a URL and optional metadata. You should only be call this once per page view. + * Register a pageview event using a URL and optional metadata. You should only call this method once per page view. * * @param url The URL of the article being tracked (eg: "http://example.com/some-old/article.html") * @param urlRef The url of the page that linked to the viewed page. Analogous to HTTP referer