To make all of the features of the Playtime SDK available, you need to initialize it first. This initialization happens asynchronously in the background, with its completion indicated by a callback method. Make sure to initialize the SDK immediately after the app launches and each time it returns to the foreground.
Best practices for the SDK initialization:
Initialize early: Initialize the SDK as soon as possible after your app starts. This ensures all Playtime SDK features are ready for use right away.
Initialize after your app authentication: Once a user signs up or logs in, initialize the SDK again with the latest user details. This updates the userID within the SDK.
Initialize regularly: For the best user experience, call the init method every time your app comes into the foreground. Doing so will not affect your app's performance.
Initialization triggers: Call the init method at the app start and resume (when the app comes back into the foreground).
Initialization
// The initialization will run in the main application process asynchronously in the background and notify you when it is finished by invoking the PlaytimeInitializationListener's onInitialisationFinished or onInitialisationError method.
class MainActivity : AppCompatActivity {
override fun onCreate(savedInstanceState: Bundle?) {
Playtime.init(this, "sdkHash", options, object: PlaytimeInitialisationListener {
override fun onInitialisationFinished() {
// the Playtime SDK was initialized successfully
}
override fun onInitialisationError(exception: Exception?) {
// an error occurred while initializing the Playtime SDK.
// note that exception might be null
}
})
}
}
// The initialization will run in the main application process asynchronously in the background and notify you when it is finished by invoking the PlaytimeInitializationListener's onInitialisationFinished or onInitialisationError method.
public class MainActivity extends Activity {
@Override
protected void onCreate() {
super.onCreate();
Playtime.init(this, "sdkHash", options, new PlaytimeInitialisationListener() {
@Override
public void onInitialisationFinished() {
// the Playtime SDK was initialized successfully
}
@Override
public void onInitialisationError(Exception exception) {
// an error occurred while initializing the Playtime SDK.
// note that exception might be null
}
});
}
}
// Should be called during the app start, for example in the App.js's componentDidMount() method
Playtime.init('sdkHash', options).then(() => {
console.log('Initialized Playtime SDK');
}).catch((err) => {
console.log('Could not initilize Playtime SDK');
console.error(err);
});
// The init method will return a Future<void> which succeeds when the SDK has been initialized successfully and fails with an error when the initialization fails. Make sure to initialize in initState as well as didChangeAppLifecycleState.
import 'package:adjoe/adjoe.dart';
import 'package:adjoe/gender.dart';
import 'package:adjoe/options.dart';
import 'package:adjoe/params.dart';
import 'package:adjoe/user_profile.dart';
void initializePlaytime() {
Playtime.init(sdkHash, options).then((_) {
print('Init finished successful');
}, onError: (err) {
print('Init failed: $err');
});
}
using io.adjoe.sdk;
public class Main {
void Start()
{
Playtime.Init("sdkHash", options, PlaytimeInitialisationSuccess, PlaytimeInitialisationError);
}
public void PlaytimeInitialisationSuccess()
{
// the Playtime SDK was initialized successfully
}
public void PlaytimeInitialisationError(Exception exception)
{
// an error occurred while initializing the Playtime SDK.
// note that exception might be null
}
}
window.PlaytimePlugin.initialize(
'sdkHash','options',
function() {
// the Playtime plugin was initialized successfully
},
function(err) {
// an error occurred while initializing the Playtime plugin.
},
);
Important: Initialization must happen on the main thread since it's a UI-related action. Calling any of the init methods off the main thread could lead to app disruptions.
Playtime Options
Pass the following additional options to the Playtime SDK during initialization:
Parameter
Type
Explanation
User ID
String
A custom identifier you can assign to each user. If you haven't set one, the Playtime SDK generates it automatically. It is accessible through Playtime.getUserId(Context). This ID is necessary for S2S payouts, thus required. Ensure this value is url-safe and that the value is never empty.
PlaytimeOptions options = new PlaytimeOptions()
..userId = 'user_id'
..params = (new PlaytimeParams()
..uaNetwork = 'The user acquisition network'
..uaChannel = 'The user acquisition channel.'
..uaSubPublisherCleartext = 'The user acquisition publisher cleartext'
..uaSubPublisherEncrypted = 'The user acquisition publisher encrypted'
..placement = 'The catalog placement')
..userProfile = playtimeUserProfile
..extensions = (new PlaytimeExtensions()
..subId1 = 'flutter_subId1'
..subId2 = 'flutter_subId2'
..subId3 = 'flutter_subId3'
..subId4 = 'flutter_subId4'
..subId5 = 'flutter_subId5');
PlaytimeOptions options = new PlaytimeOptions()
.SetUserId(userId)
.SetPlaytimeParams(playtimeParams)
.SetPlaytimeUserProfile(playtimeUserProfile)
.SetPlaytimeExtensions(playtimeExtensions);
// A complete example
window.PlaytimePlugin.initialize(
'sdkHash',
{
'user_id': getUserId(),
'playtime_params': {
'uaNetwork': "uaNetwork_value",
'uaChannel': "uaChannel_value",
'uaSubPublisherCleartext': "uaSubPublisherCleartext_value",
'uaSubPublisherEncrypted': "uaSubPublisherEncrypted_value",
'placement': 'placement_value'
},
'playtime_extension': {
'subId1': "RN_subId1",
'subId2': "RN_subId2",
'subId3': "RN_subId3",
'subId4': "RN_subId4",
'subId5': "RN_subId5"
},
'user_profile': {
'gender': 'MALE',
'birthdate': "2023-07-17T22:25:00.000Z"
}
},
function() {
// the Playtime plugin was initialized successfully
},
function(err) {
// an error occurred while initializing the Playtime plugin.
},
);
Playtime Parameters
When initializing the SDK, you should include additional User Acquisition and Playtime placement parameters. These parameters allow you to track and analyze Playtime's performance within your app.
To make the most of the Playtime SDK, to maximize your revenue and provide the best user experience, use all of these parameters for data-driven optimizations. For instance, you can monitor the performance of different Playtime button placements or determine the effectiveness of different UA sources.
You can provide these parameters in any combination, both at initialization and at any point in the app's lifecycle—such as when a user launches the Playtime Catalog interacts with a campaign or requests a payout.
These values must be url-safe as they could potentially be returned as query parameters in the S2S Payout request.
Parameter
Type
Explanation
UA Network
String
User Acquisition Network. The network through which the user was acquired, e.g., Adwords, Facebook, or Organic.
UA Channel
String
User Acquisition Channel. The channel within the network from which the user was acquired, e.g. incentivized or video campaigns.
User Acquisition Sub Publisher Cleartext
String
The cleartext package name of the sub-publisher's app ID within the network from which the user was acquired, e.g., com.domain.appName.
User Acquisition Sub Publisher Encrypted
String
The encrypted package name or app ID of the sub-publisher's app from the network where the user was acquired.
Placement
String
The placement of the Playtime Catalog inside your app, e.g., "Main Screen", "Shop", "Interstitial", "More Diamonds Screen", etc.
// A complete example
window.PlaytimePlugin.initialize(
'sdkHash',
{
'user_id': getUserId(),
'playtime_params': {
'uaNetwork': "uaNetwork_value",
'uaChannel': "uaChannel_value",
'uaSubPublisherCleartext': "uaSubPublisherCleartext_value",
'uaSubPublisherEncrypted': "uaSubPublisherEncrypted_value",
'placement': 'placement_value'
},
'playtime_extension': {
'subId1': "RN_subId1",
'subId2': "RN_subId2",
'subId3': "RN_subId3",
'subId4': "RN_subId4",
'subId5': "RN_subId5"
},
'user_profile': {
'gender': 'male',
'birthdate': "2023-07-17T22:25:00.000Z"
}
},
function() {
// the Playtime plugin was initialized successfully
},
function(err) {
// an error occurred while initializing the Playtime plugin.
},
);
User Profile Information
Our game recommendations are personalized through algorithms that consider users' preferences and gender information. If a user consents to share their gender and/or birthday, you can share this information with adjoe to enhance the customization of game suggestions.
The code below is for demonstration purposes and can be modified to best suit your app's set-up. The important thing is that you get the user's birthday and gender information from your backend and pass in the data to adjoe when initializing.
The birthday data should be in ISO format. If your data doesn't already look like that, please make sure to format it so that it is. Example: 2023-07-17T22:25:00.000Z
fun playtimeUserProfile(): PlaytimeUserProfile {
val gender: PlaytimeGender = when (getUserGender()) {
"male" -> PlaytimeGender.MALE
"female" -> PlaytimeGender.FEMALE
else -> PlaytimeGender.UNKNOWN
}
// if you don't know the exact birthday, the year is enough
val birthday: Date = if (isExactBirthdayKnown()) {
getUserBirthday()
} else {
val calendar = Calendar.getInstance()
calendar.set(Calendar.YEAR, getYearOfBirth())
calendar.set(Calendar.MONTH, 0)
calendar.set(Calendar.DAY_OF_MONTH, 0)
calendar.time
}
return PlaytimeUserProfile(gender, birthday)
}
public class UserProfileUtils {
public static PlaytimeUserProfile playtimeUserProfile() {
PlaytimeGender gender = getPlaytimeGender(getUserGender());
Date birthday = getBirthday();
return new PlaytimeUserProfile(gender, birthday);
}
private static PlaytimeGender getPlaytimeGender(String userGender) {
switch (userGender) {
case "male":
return PlaytimeGender.MALE;
case "female":
return PlaytimeGender.FEMALE;
default:
return PlaytimeGender.UNKNOWN;
}
}
private static String getUserGender() {
// Implement this method to get the user's gender
return "male"; // Example placeholder
}
private static Date getBirthday() {
if (isExactBirthdayKnown()) {
return getUserBirthday();
} else {
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, getYearOfBirth());
calendar.set(Calendar.MONTH, Calendar.JANUARY);
calendar.set(Calendar.DAY_OF_MONTH, 1);
return calendar.getTime();
}
}
private static Date getUserBirthday() {
// Implement this method to get the user's birthday
return new Date(); // Example placeholder
}
private static boolean isExactBirthdayKnown() {
// Implement this method to determine if the exact birthday is known
return true; // Example placeholder
}
private static int getYearOfBirth() {
// Implement this method to get the user's year of birth
return 1990; // Example placeholder
}
}
function playtimeUserProfile() {
const userGender = getUserGender();
let gender;
if (userGender === "male") {
gender = "MALE";
} else if (userGender === "female") {
gender = "FEMALE";
} else {
gender = "UNKNOWN";
}
// if you don't know the month or day, you can set them to 01
let birthday;
if (isExactBirthdayKnown()) {
birthday = new Date(getUserBirthday());
} else {
const calendar = new Date();
calendar.setFullYear(getYearOfBirth());
calendar.setMonth(0);
calendar.setDate(1);
birthday = calendar;
}
birthday = birthday.toISOString()
return { gender, birthday };
}
import 'package:adjoe/user_profile.dart';
import 'package:adjoe/gender.dart';
PlaytimeGender gender;
switch (getUserGender()) {
case "male":
gender = PlaytimeGender.MALE;
break;
case "female":
gender = PlaytimeGender.FEMALE;
break;
default:
gender = PlaytimeGender.UNKNOWN;
break;
}
// set the birthday. If you don't know the user's exact birthday, the year is already enough.
DateTime birthday = getUserBirthday();
PlaytimeUserProfile playtimeUserProfile = PlaytimeUserProfile()
..gender = gender
..birthday = birthday;
PlaytimeGender gender;
string userGender = GetUserGender();
if (userGender == "male")
gender = PlaytimeGender.MALE;
else if (userGender == "female")
gender = PlaytimeGender.FEMALE;
else
gender = PlaytimeGender.UNKNOWN;
DateTime birthday = new DateTime();
birthday = DateTime.SpecifyKind(birthday, DateTimeKind.Utc);
if (isExactBirthdayKnown())
{
birthday = DateTime.Parse(getUserBirthday());
}
else
{
birthday = new DateTime(getYearOfBirth(), 1, 1);
}
string birthdayIso = birthday.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
PlaytimeUserProfile playtimeUserProfile = new PlaytimeUserProfile(gender, birthdayIso);
//Cordova has a different API that can be used to set the user profile information.
window.PlaytimePlugin.setProfile(
source,
gender,
birthday,
function() {
// successfully sent the profile information to adjoe
},
function(err) {
// an error occurred while sending the profile information to adjoe
},
);
// A complete example
window.PlaytimePlugin.initialize(
'sdkHash',
{
'user_id': getUserId(),
'playtime_params': {
'uaNetwork': "uaNetwork_value",
'uaChannel': "uaChannel_value",
'uaSubPublisherCleartext': "uaSubPublisherCleartext_value",
'uaSubPublisherEncrypted': "uaSubPublisherEncrypted_value",
'placement': 'placement_value'
},
'playtime_extension': {
'subId1': "RN_subId1",
'subId2': "RN_subId2",
'subId3': "RN_subId3",
'subId4': "RN_subId4",
'subId5': "RN_subId5"
},
'user_profile': {
'gender': 'male',
'birthdate': "2023-07-17T22:25:00.000Z"
}
},
function() {
// the Playtime plugin was initialized successfully
},
function(err) {
// an error occurred while initializing the Playtime plugin.
},
);
Checking Initialization
We recommend to verify the SDK is correctly initialized. Each wrapper provides an Playtime.isInitialized method for this purpose. When executed, this method returns true if the SDK is initialized, and false otherwise. Use IsInitialized for debugging or to verify SDK initialization before calling other methods.
Important: Do not call Playtime.init based on the result of isInitialized.The SDK already performs its own initialization checks. Doing so again may degrade the user experience.
Part 2: Launch the Catalog
The primary function of the Playtime SDK is showing the catalog to the users. The Playtime catalog lists various campaigns that the users can complete to receive a reward in the app's native in-app currency.
Add a Teaser Event
When the placement button for Playtime appears, whether clicked or not, please send us the teaser event. This notifies adjoe when and where the placement is shown, which is crucial for tracking user engagement and enhancing the user experience.
Use playtimeParams to pass in the placement information, i.e., where in the app the button was shown - for example: "Main Page".
try {
Playtime.sendUserEvent(context, Playtime.EVENT_TEASER_SHOWN, null, playtimeParams)
}
catch (e: PlaytimeNotInitializedException) {
// you have to initialize the Playtime SDK
}
try {
Playtime.sendUserEvent(context, Playtime.EVENT_TEASER_SHOWN, null, playtimeParams);
}
catch (PlaytimeNotInitializedException e) {
// you have to initialize the Playtime SDK
}
function() {
// show teaser button
window.PlaytimePlugin.sendUserEvent(window.PlaytimePlugin.EVENT_TEASER_SHOWN, '<subId1>', '<subId2>');
},
function() {
//can't show the catalog
}
Launch Activity
We recommend triggering the catalog via a button or UI element (within the SDK it is housed in a separate activity). Rather than launching the activity directly, we provide an Intent for you to initiate it, enabling, for example, transition animations for a smoother user experience.
Use the code below to display the adjoe Playtime Offerwall. It is recommended to send any updated PlaytimeParams when launching the Playtime Catalog.
try {
val playtimeParams = PlaytimeParams.Builder()
.setUaNetwork("network")
.setUaChannel("channel")
.setUaSubPublisherCleartext("SubPublisherCleartext")
.setUaSubPublisherEncrypted("SubPublisherEncrypted")
.setPlacement("placement")
.build()
val playtimeCatalogIntent = Playtime.getCatalogIntent(context, playtimeParams)
context.startActivity(playtimeCatalogIntent)
}
catch(notInitializedException: PlaytimeNotInitializedException) {
// you have not initialized the Playtime SDK
}
catch(exception: PlaytimeException) {
// the catalog cannot be displayed for some other reason
}
try {
PlaytimeParams playtimeParams = new PlaytimeParams.Builder()
.setUaNetwork("network")
.setUaChannel("channel")
.setUaSubPublisherCleartext("SubPublisherCleartext")
.setUaSubPublisherEncrypted("SubPublisherEncrypted")
.setPlacement("placement")
.build();
Intent playtimeCatalogIntent = Playtime.getCatalogIntent(context, playtimeParams);
context.startActivity(playtimeCatalogIntent);
}
catch (PlaytimeNotInitializedException notInitializedException) {
// you have not initialized the Playtime SDK
}
catch (PlaytimeException exception) {
// the catalog cannot be displayed for some other reason
}
window.PlaytimePlugin.showCatalog(
function() {
// the catalog was displayed to the user
},
function(err) {
// the catalog cannot be displayed
},
);
//You can pass two optional Sub-IDs when you launch Playtime, which will be logged every time a user views or clicks a campaign:
window.PlaytimePlugin.showCatalogWithSubIDs('<subId1>', '<subId2>', success, error).
Set Up a Listener (Native)
You can set up a listener to be notified when the Playtime Catalog is opened or closed, with the event type always being catalog. To remove the listener, simply call Playtime.removeCatalogListener().
Playtime.setCatalogListener(object:PlaytimeCatalogListener() {
fun onCatalogOpened(type:String) {
Log.d(TAG, "Catalog of type '" + type + "' was opened")
}
fun onCatalogClosed(type:String) {
Log.d(TAG, "Catalog of type '" + type + "' was closed")
}
})
Playtime.setCatalogListener(new PlaytimeCatalogListener() {
@Override
public void onCatalogOpened(String type) {
Log.d(TAG, "Catalog of type '" + type + "' was opened");
}
@Override
public void onCatalogClosed(String type) {
Log.d(TAG, "Catalog of type '" + type + "' was closed");
}
});
Additional Useful Methods
name
description
getVersion
Returns the internal version code of the Playtime SDK, for example 70.
getVersionName
Returns the version name of the Playtime SDK, for example 3.0.0
hasAcceptedTOS
Checks whether the user has accepted the adjoe Terms of Service (TOS). Returns true if the user has accepted the TOS and false otherwise.
getUserId
Returns the unique ID of the user by which the user is identified.
hasAcceptedUsagePermission
Checks whether the user has given access to the usage statistics. Returns true when the user has given access, false otherwise.
User Experience
Terms of Service
Access to Advertising Info
If the user rejects TOS, they will see the following screen. No campaigns will be shown.
After the user selects "Allow", they will be taken to the usage settings to allow tracking.
Users need to accept adjoe's Terms of Service (TOS) to use Playtime and access games. Agreeing to the TOS is mandatory before installing any partner apps. After users accept the TOS, we can identify the types of apps/games they have installed, allowing our algorithm to tailor game suggestions on the catalog based on their preferences.
We also ask the user to accept the App Usage Permissions. This allows us to monitor the time spent in each game or app. Without accepting these permissions, users won't be eligible to participate in Playtime campaigns and will be limited to only Advance or Advance+ campaigns.