contentpass supports Android 5.0 (Lollipop, sdk api version 21) and above
Our SDK is available on Maven Central.
implementation 'de.contentpass:contentpass-android:2.1.1'
Add this to your app's build.gradle
file's dependencies
element.
You will be provided a contentpass_configuration.json
. You will need this later to instantiate the contentpass object.
You will also need your redirect_uri
that is specified in this json file to setup the capturing of the authentication redirect.
Since we use AppAuth for the OAuth process, the easiest way to properly set this up is in your app's build.gradle
file.
An example contentpass_configuration.json
:
{
"schema_version": 2,
"api_url": "https://api.example.com",
"oidc_url": "https://pur.example.com",
"redirect_uri": "com.example.app://oauth",
"property_id": "YOUR_PROPERTY_ID"
}
Extract the redirect_uri
and place the scheme in your build.gradle
:
android.defaultConfig.manifestPlaceholders = [
'appAuthRedirectScheme': 'com.example.app'
]
If that's not to your liking, please refer to this documentation for more options on how to register for capturing the redirect.
We provide an example application in the /app
app module. If you're unclear about the usage of some of our features, have a look and tinker around with the code. Refer to the example's README for more information.
We recommend to put this somewhere in your res
folder, e.g. res/raw/
or res/json
.
You should instantiate and hold one instance of the ContentPass
class in one of your top level state holding objects.
Even better would be providing the same instance to your view models via dependency injection.
The only way to instantiate the ContentPass
class is via its Builder
. Currently there's two configuration functions which are both required:
val config = context.resources
.openRawResource(R.raw.contentpass_configuration)
val contentPass = ContentPass.Builder()
.context(context)
.configurationFile(config)
.build()
If you want to be notified of changes in the authentication state, you should register a ContentPass.Observer
:
contentPass.registerObserver(object : ContentPass.Observer {
override fun onNewState(state: ContentPass.State) {
// handle the new ContentPass.State
}
})
To avoid memory leaks, don't forget to call unregisterObserver
once you're done observing.
We use AppAuth for the OAuth 2.0 process. AppAuth uses web browsers to present the authentication flow to the user.
That means, that the user will - in most cases - temporarily leave your app, authenticate with the contentpass servers and finally return to your app. We then use the OAuth result to validate whether the user has any active contentpass subscriptions that are applicable.
Since you have setup the redirection scheme, your app will open again after the authentication due to an intent. Since this intent contains data that is necessary to validate the contentpasse subscriptions and authentication, the sdk needs to be registered as a callback for this intent data.
Therefore, you will need to register the sdk in every Activity or Fragment that you plan to start the authentication from. This needs to happen in the Activity's or Fragment's onCreate
method since that's the only time and place where this is possible.
You just pass the activity or fragment via on of the two registerActivityResultLauncher
functions:
override fun onCreate(savedInstanceState: Bundle?) {
contentPass.registerActivityResultLauncher(this)
}
Then at any time during the fragment's or activity's lifecycle you can call authenticate
.
We supply two authentication methods:
authenticateSuspending
for everyone that is able to use Kotlin coroutinesauthenticate
for compatibility with Java or if coroutines are possible with your project
Both functions will need a Context
instance from which to start the authentication flow from.
val state: ContentPass.State? = null
coroutineScope.launch {
state = contentPass.authenticateSuspending(context)
}
or
contentPass.authenticate(fromActivity, object : ContentPass.AuthenticationCallback {
override fun onSuccess(state: ContentPass.State) {
// handle new state
}
override fun onFailure(exception: Throwable) {
// handle exception
}
})
If the authentication was a success, we will poll our servers for subscription plans in the background.
Any registered Observer
will be called with the final authentication and subscription state.
Be aware that a successfully authenticated user may have no active subscription plans and act accordingly!
- We store tokens that anonymously identify the logged in user's session to our servers in the app's SharedPreferences.
- This token data is encrypted and the keys are stored securely in the hardware backed (if available) KeyStore.
- We refresh these tokens automatically in the background before they're invalidated.
- The subscription information gets validated as well on every token refresh.
To count an impression, call either the suspending function countImpressionSuspending(context: Context)
or
the compatibility function countImpression(context: Context, callback: CountImpressionCallback)
.
In both cases you'll need to supply a Context
(i.e. an Activity
or Fragment
) so the login process can
be started again in case the user needs to login again.
These methods count impressions for billing purposes. This method must be invoked whenever a user views a piece of content, independently of authentication state. If the current user is authenticated the impression will automatically be logged as paid ad-free impression to calculate the publisher compensation. As the total amount of impressions is required for billing as well, this method also counts sampled impressions of non-subscribers.
try {
contentPass.countImpressionSuspending(context)
// handle success
} catch (impressionException: ContentPass.CountImpressionException) {
// impressionException.message contains the http error code as a string
// if this is "404", this most likely means that the user has no applicable subscription
} catch (ex: Throwable) {
// Handle the exception.
// This might be network related but could also happen because the ContentPass object wasn't initialized properly.
}
or for compatibility reasons:
contentPass.countImpression(context, object : ContentPass.CountImpressionCallback {
override fun onSuccess() {
// handle success
}
override fun onFailure(exception: Throwable) {
// handle the exception.
// If this is a ContentPass.CountImpressionException, parse its message for the http error code.
// if this is "404", this most likely means that the user has no applicable subscription
}
})
Since we persist the user's session, you need a way to log the user out. Simply call logout
and we remove all stored token data.
contentPass.logout()
The user will of course have to log in again afterwards.
You can also call authenticate
again and all previous user information will get overwritten.
We only store one user session at any one time.
We use the following open source packages in this SDK: