Skip to content

Commit

Permalink
feat: BannerView (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
fcs-ts authored Sep 16, 2024
1 parent 708a0ee commit 0f9efd4
Show file tree
Hide file tree
Showing 27 changed files with 434 additions and 46 deletions.
10 changes: 3 additions & 7 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,9 @@
.cxx
.externalNativeBuild
.gradle
.idea
/.idea/assetWizardSettings.xml
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/navEditor.xml
/.idea/workspace.xml
.idea/caches
.idea/workspace.xml
.idea/shelf
/TopsortAnalytics/build
/build
/captures
Expand Down
3 changes: 3 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .idea/.name

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

123 changes: 123 additions & 0 deletions .idea/codeStyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions .idea/codeStyles/codeStyleConfig.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/compiler.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions .idea/deploymentTargetSelector.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/kotlinc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions .idea/migrations.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 47 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,4 +238,51 @@ private void reportImpressionWithResolvedBidId() {
}
```

#### Banners on Android

#### Kotlin
You should first add the `BannerView` into your activity `xml`. You can do so with
Android Studio's visual editor, but the end file should like like the following
```xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.topsort.analytics.banners.BannerView
android:id="@+id/bannerView"
android:layout_width="353dp"
android:layout_height="103dp"
android:layout_marginTop="40dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView"
tools:srcCompat="@tools:sample/backgrounds/scenic" />
</androidx.constraintlayout.widget.ConstraintLayout>
```

Then, you have to call the `BannerView.setup()` function with you auction parameters.
Notice that since this makes network calls, we need to `launch` it in a co-routine.
```kotlin
class SampleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.sample_activity)

this.lifecycleScope.launch {
val bannerView = findViewById<BannerView>(R.id.bannerView)
val bannerConfig =
BannerConfig.CategorySingle(slotId = "slot", category = "category")
bannerView.setup(
bannerConfig,
"sample_activity",
null,
{ id, entityType -> onBannerClick(id, entityType) })
}
}
}
```


[1]: ./LICENSE
3 changes: 3 additions & 0 deletions TopsortAnalytics/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ dependencies {
//JodaTime
implementation group: 'joda-time', name: 'joda-time', version: '2.12.5'

// Image Loading for Banners
implementation 'io.coil-kt:coil:2.7.0'

testImplementation 'junit:junit:4.13.2'
testImplementation 'org.json:json:20200518'
testImplementation 'org.assertj:assertj-core:3.26.0'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.topsort.analytics.banners

import android.content.Context
import android.util.AttributeSet
import android.widget.ImageView
import coil.load
import com.topsort.analytics.Analytics
import com.topsort.analytics.model.Placement
import com.topsort.analytics.model.auctions.EntityType


/**
* View for displaying banners powered by auctions.
*
* @constructor The constructor is meant to be called automatically from XML inflation.
* You can add this view to your layout by using a `com.topsort.analytics.banners.BannerView` element.
*
* @param context
* @param attrs AttributeSet for the view. Since this view inherits from `ImageView`
* you can set attributes as you would with a regular `ImageView`.
*/
class BannerView(
context: Context,
attrs: AttributeSet
) : ImageView(context, attrs) {

/**
* Setup the banner in the view by running an auction in the background.
*
* @param config a BannerConfig object that specifies the parameters for the auction
* @param path identifier for the activity where the banner is displayed. It's recommended to be the deeplink for the view.
* @param location optional name for the location within the view where the banner is displayed.
* @param onClick callback for when the banner is clicked. Usually this should navigate to an activity related to the banner (e.g. the product page for the product shown in the banner).
* @receiver
*/
suspend fun setup(
config: BannerConfig,
path: String,
location: String?,
onClick: (String, EntityType) -> Unit
) {
val result = runBannerAuction(config)
if (result != null) {
this.load(result.url)
this.viewTreeObserver.addOnGlobalLayoutListener {
Analytics.reportImpressionPromoted(
resolvedBidId = result.resolvedBidId,
placement = Placement(path = path, location = location)
)
}
this.setOnClickListener {
Analytics.reportClickPromoted(
resolvedBidId = result.resolvedBidId,
placement = Placement(path = path, location = location)
)
onClick(result.id, result.type)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,32 @@ package com.topsort.analytics.banners

import com.topsort.analytics.model.auctions.Auction
import com.topsort.analytics.model.auctions.AuctionRequest
import com.topsort.analytics.model.auctions.AuctionResponse
import com.topsort.analytics.service.TopsortAuctionsHttpService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

/**
* Run a banner auction with a single slot
*
* @param config the banner configuration that specifies which kind of banner auction to run
* @return A BannerResponse if the auction successfully returned a winner or null if not.
*/
fun runBannerAuction(config: BannerConfig): BannerResponse? {
suspend fun runBannerAuction(config: BannerConfig): BannerResponse? {
val auction = buildBannerAuction(config)
val request = AuctionRequest(listOf(auction))
val response = TopsortAuctionsHttpService.runAuctions(request)
var response: AuctionResponse? = null;
val auctionJob = CoroutineScope(Dispatchers.IO).launch {
response = TopsortAuctionsHttpService.runAuctions(request)
}
auctionJob.join()
if ((response?.results?.isNotEmpty() == true)) {
if (response.results[0].winners.isNotEmpty()) {
val winner = response.results[0].winners[0]
if (response!!.results[0].winners.isNotEmpty()) {
val winner = response!!.results[0].winners[0]
return BannerResponse(
id = winner.id,
url = winner.asset!!.url,
url = winner.asset!![0].url,
type = winner.type,
resolvedBidId = winner.resolvedBidId
)
Expand Down
Loading

0 comments on commit 0f9efd4

Please sign in to comment.