Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Documenting supported app<->web attribution flows #545

Open
johnivdel opened this issue Aug 24, 2022 · 1 comment
Open

Documenting supported app<->web attribution flows #545

johnivdel opened this issue Aug 24, 2022 · 1 comment

Comments

@johnivdel
Copy link
Collaborator

The app_to_web explainer doesn't explicitly document what conversion flows are supported on a single device, we should consider documenting explicit use-cases and how they are supported by this API.

The following table shows for impression conversion/events occurring in various contexts, what APIs are responsible for registering the reports. Rows indicate where the impression occurred, columns indicate where the conversion occurred, and table cells indicate which API is used to register.

In this table, all reports are handled by Android.

"Web" is the web API which registers with the underlying operating system described in app_to_web.md, and "OS" refers to the native API proposed by Android.

Browser App Webview (browser)
Browser Web, Web Web, OS Web, Web
App OS, Web OS, OS OS, Web
WebView (rendered ad) OS, Web OS, OS OS, Web
WebView (browser) Web, Web Web, OS Web, Web

"Browser" includes various interfaces for the browser app, for example CCT in the case of Google Chrome as discussed in #239.

"WebView (rendered ad)" refers to ads where the content was rendered using a Webview, but the ad was served by the app.

A flow which doesn't fall into these categories would be:

  1. ad is displayed within an app
  2. ad is clicked
  3. ad link opens the browser
  4. impressions are registered within the browser as part of opening the link

In this case, the source event (user clicking the ad) occurred in a native app which would require the app to register the event directly with the OS. Whereas the browser is responsible for doing so today.

If there are any other flows which don't fall into these categories, we should document them as well.

@johnivdel
Copy link
Collaborator Author

johnivdel commented Sep 20, 2022

It is also possible that an ad impression and a click may happen in a different context from where the link is opened.

Consider the following example:

  • An app displays a page within a webview
  • The user clicks on an ad in the webview
  • The app overrides the link opening a page using WebView APIs
  • The app opens the ad link

In this case, the app will need to register the ad impression with the Android system API directly. However, because the click happened within the Webview, the app will need to be sure to track InputEvents in order to properly register a click with API.

Here is one example of how that could be achieved:

MainActivity.kt

import android.annotation.SuppressLint
import android.os.Bundle
import android.util.Log
import android.webkit.WebResourceRequest
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {

  @SuppressLint("SetJavaScriptEnabled")
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    val webView: WebView = requireViewById(R.id.webview)
    webView.settings.javaScriptEnabled = true
    webView.webViewClient = EventLoggingWebViewClient()
    webView.loadUrl("https://publisher.example")
  }

  class EventLoggingWebViewClient : WebViewClient() {
    private val tag = "LastEventWebViewClient"

    override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
      Log.i(tag, request?.url.toString())
      if (view is EventCapturingWebView){
        Log.i(tag, "Captured events:")
        for (event in view.lastTouch()){
          Log.i(tag, event.toString())
        }
      }

      // App may register with the Attribution Reporting API directly, or pass 
      // view.lastTouch().get(view.lastTouch().size() - 1) elsewhere to register.

      return super.shouldOverrideUrlLoading(view, request)
    }
  }
}

EventCapturingWebView.kt

import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.MotionEvent.ACTION_DOWN
import android.webkit.WebView
import java.util.LinkedList

class EventCapturingWebView @JvmOverloads constructor(
  context: Context, attrs: AttributeSet? = null
) : WebView(context, attrs) {

  private val tag = "EventCapturingWebView"

  private val eventBuffer = LinkedList<MotionEvent>()

  @SuppressLint("ClickableViewAccessibility")
  override fun onTouchEvent(event: MotionEvent?): Boolean {
    if (event != null) {
      if (event.action == ACTION_DOWN) {
        // Reset the event buffer on each ACTION_DOWN (press)
        eventBuffer.clear()
      }
      // Obtain a clone of the event, since the framework reuses the event it passes
      // to this method
      val newEvent = MotionEvent.obtain(event)
      eventBuffer.add(newEvent)
    }
    return super.onTouchEvent(event)
  }

  fun lastTouch() : List<MotionEvent> {
    @Suppress("UNCHECKED_CAST")
    return eventBuffer.clone() as List<MotionEvent>
  }

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants