1. Shares and referrals
2. GIF customization
3. Save recordings
4. Testing referrals
5. Troubleshooting

1. Shares and referrals

1.1 Custom sharing text

Edit the default sharing text to make it more personal and customized for each place you use it.

Megacool.setSharingText("Try to beat my score");

1.2 Share config

If you want to customize a share, create a ShareConfig object and pass this as an argument to share.

Uri url = Uri.parse("level5/battle?username=nbroby");
Map<String, String> data = new HashMap<String, String>() {
{
put("welcomeText", "Nick has invited you");
}
};
ShareConfig shareConfig = new ShareConfig().data(data).url(url);
Megacool.share(shareConfig);

The example above will create a share link that looks like this: https://mgcl.co/appId/level5/battle?_m=12345678&username=nbroby

When a receiver opens the app from the link, a share object containing the data and url will be included in the EventType.RECEIVED_SHARE_OPENED event.

1.3 Handling shares and events

If you start Megacool with start(Context, String, OnEventsReceivedListener), you can specify a listener for receiving an array of Event from the server. An Event can contain a Share object.

You can implement event listeners to:

  • reward your users for referring friends
  • send users to a specific location in the app
  • automatically make two users friends

Check out the full docs for Event and Share to see all fields and methods available.

This is an example of how to implement the event listener:

Megacool.start(this, "YOUR_APP_CONFIG", new Megacool.OnEventsReceivedListener() {
@Override
public void onEventsReceived(List<Event> events) {
for (Event event : events) {
if (event.type == EventType.RECEIVED_SHARE_OPENED) {
// This device has received a share to the app and opened it
Log.d(TAG, "Event ReceivedShareOpened");
// Get the user id of the sender
final String senderUserId = event.getShare().getReferralCode().getUserId();
if (event.isFirstSession()) {
// First install on this device
Log.d(TAG, "First install, invited by user id: " + senderUserId);
} else {
// The app is already installed on this device
Log.d(TAG, "App opened, invited by user id: " + senderUserId);
}
Log.d(TAG, "Share data: " + event.getShare().getData());
}
}
}
});

This is an example of how to implement the event listener:

Megacool.start(this, "YOUR_APP_CONFIG", new Megacool.OnEventsReceivedListener() {
@Override
public void onEventsReceived(List<Event> events) {
for (Event event : events) {
if (event.type == EventType.SENT_SHARE_OPENED) {
// The receiver of the share has opened it
Log.d(TAG, "Event SentShareOpened");
// Get the user id of the receiver
final String receiverUserId = event.getReceiverUserId();
if (event.isFirstSession()) {
// Receiver installed the app for the first time
Log.d(TAG, "Receiver installed the app, user id: " + receiverUserId);
} else {
// The app was already installed
Log.d(TAG, "Receiver opened the app, user id: " + receiverUserId);
}
}
}
}
});

Note: The SDK occasionally communicates with the server, so this event is not triggered immediately. To reward a user right after a new install has occurred, you can add a "refresh" button that calls getShares(ShareCallback). The getShares(ShareCallback) method is used for checking the status of sent shares, but it also requests the server for new events.

void refreshButton() {
Megacool.getShares(new Megacool.ShareCallback() {
@Override
public void shares(List<Share> shares) {
}
});
}

You can improve the experience for existing users by sending them to the right location in your app immediately after they've opened the link. This is only for users that already have the app installed.

Megacool.start(this, "YOUR_APP_CONFIG", new Megacool.OnEventsReceivedListener() {
@Override
public void onEventsReceived(List<Event> events) {
for (Event event : events) {
if (event.type == EventType.LINK_CLICKED) {
// The app has been opened from a link. Send the user instantly to
// the right location if the URL path exists.
Log.d(TAG, "Event LinkClicked");
final String url = (String) event.getData().get("url");
final ReferralCode referralCode = (ReferralCode) event.getData().get("referralCode");
Log.d(TAG, "URL: " + url);
Log.d(TAG, "ReferralCode: " + referralCode);
}
}
}
});

1.4 Share state

Call getShares(ShareCallback) to get the latest state of a sent share. The locally cached shares will be returned immediately and are useful to determine the total number of shares. If the callback is non-null a request will be sent to the server to get the latest state update for each share.

States can be:

  • ShareState.SENT
  • ShareState.CLICKED
  • ShareState.OPENED
  • ShareState.INSTALLED
Megacool.getShares(new Megacool.ShareCallback() {
@Override
public void shares(List<Share> shares) {
for (Share share : shares) {
if (share.getState() == ShareState.SENT) {
Log.d(TAG, "Share sent on " + share.getCreatedAt() + "was clicked");
}
}
}
});

You can use the share state to display the number of friends that have been invited and what the state is:

To delete local shares that you don’t need updates from anymore, simply call deleteShares(shareFilter), where shareFilter is a callback with Share as an argument. Based on state, createdAt, updatedAt etc. you can return true to delete the share object or false to keep it. Make sure getShares(ShareCallback) has completed the request to the server before calling this, otherwise the deleted shares will just be re-added.

Megacool.deleteShares(new Megacool.ShareFilter() {
@Override
public boolean accept(Share share) {
return share.getState() == ShareState.INSTALLED;
}
});

1.7 Sharing strategies

Not all apps support sharing both a link and a GIF, and we have to choose which one to include. By default the SDK will prioritize links, to ensure referrals work, but if you only want to ensure the GIFs are spread as widely as possible you can customize this behavior:

Megacool.setSharingStrategy(SharingStrategy.MEDIA);

2. GIF customization

2.1 Types of recording

2.1.1 Time recording

Add the following line of code where you want to start a recording:

Megacool.startRecording(view);

The recording will by default keep a buffer of the last 5 seconds. Old frames get overwritten by new frames. See Max Frames for editing the default setting.

Note: If your app goes to the background, function () { return obj[val].apply(obj, arguments); }() is called automatically, but it's not automatically resumed when the app opens again.

To end a recording, add the following line of code:

Megacool.stopRecording();

2.1.2 Highlight recording

The highlight recording feature records the ‘most interesting’ part of your game--whether this is an awesome action sequence, or a hilarious ‘fail’. It does this by keeping track of the gameplay's intensity as determined by metrics specific to your game.

Whenever a critical event (such as scoring a point, landing a cool combo, failing in an epic way, etc.) occurs, call this function to make note of the event (and optionally, its intensity):

Megacool.registerScoreChange(); //(without intensity)
Megacool.registerScoreChange(42); //(with intensity of 42)

For easy integration, we suggest ‘piggy-backing’ off your game’s current score system by calling this function whenever the player’s score increases, with the amount that it increases by. We equate ‘intensity’ with a change in point value, but you can also experiment with assigning various intensities to events with subjective ‘cool’ value.

Add the following line of code at the beginning of your game to start capturing your highlight!

RecordingConfig config = new RecordingConfig()
.overflowStrategy(OverflowStrategy.HIGHLIGHT);
Megacool.startRecording(view, config);

2.1.3 Peak location

Different games have different paces: in one game, an awesome move might be followed immediately by bland downtime, while in another game, there might be some cool graphics or an interesting cutscene immediately afterwards. To let you adjust how much to include after the action dies down, try experimenting with the peakLocation parameter. This parameter is specific to highlight recording and controls where the ‘peak’ (or, maximum change in point value) of your highlight occurs. In the previous example, we would want the first game to have a late peak location so we don't record too much boring downtime, and the first game to have an early peak location so we make sure to record the cool after-graphics. The value of the peak location (between 0 and 1, inclusive), determines at what percentage through the recording the peak will be placed.

For example, in a recording with 10 frames, a peakLocation of 0.2 means that the peak will occur near the beginning around frame 2, and a peakLocation of 0.8 means that the peak will occur near the end around frame 8. The default peakLocation is 0.7. You can set the peak location by calling:

Megacool.setPeakLocation(0.7);

2.1.4 Capture individual frames

Capture each frame of the recording, e.g. on each user tap:

Megacool.captureFrame(view);

By default max 50 frames will be captured. If it exceeds 50 frames, old frames are overwritten until Megacool.stopRecording() gets called to reset it. Add the following line of code to stop a recording:

Megacool.stopRecording();

2.1.5 Timelapse

Timelapse is identical to the normal captureFrame, except timelapse has different behavior when overflowing maxFrames. If it's about to overflow, it will delete every other frame captured so far, and half the rate of capturing (thus only storing every second image on the first overflow, every fourth image on the next, and so on).

Megacool.captureFrame(self.view, new RecordingConfig()
.overflowStrategy(OverflowStrategy.TIMELAPSE));

If you want to guarantee that a frame is added to the timelapse (like an end screen or final state of the game), pass a forceAdd = true parameter in config like this:

Megacool.captureFrame(self.view, new RecordingConfig()
.overflowStrategy(OverflowStrategy.TIMELAPSE)
.forceAdd(true));

Important: Only pass this parameter once! Normally this function returns as soon as it has spawned a thread to do the actual capture, but if you pass forceAdd = true it will run synchronously (e.g. not return until it has actually stored the frame. This is because usually this is called as the last step before a session ends and the GIF is created, thus we need the call to complete before creating the GIF. This enables you to change visible elements on the screen after this call completes and be sure that it's not going to be included in the GIF.

2.2 Rendering preview

This creates a rendered preview of the GIF that can be displayed to the user before sharing.

GifImageView gifImageView = Megacool.renderPreviewOfGif();
if (gifImageView != null) {
layout.addView(gifImageView);
gifImageView.start();
}

2.3 Recording frame rate

Set numbers of frames to record per second. Recommended range: 1 - 10. The default is 10:

Megacool.setFrameRate(5);

Set the frame rate right before calling Megacool.startRecording(view). Note: This only applies to recordings made with Megacool.startRecording(view), not Megacool.captureFrame(view).

Frame rate examples (with Playback frame rate: 10):

Frame rate: 5
Frame rate: 10
Frame rate: 25

2.4 Playback frame rate

Set the number of frames per second when playing the GIF. The default is 10 frames / second. The GIF will be exported with this frame rate and it doesn't affect the recording frame rate.

Megacool.setPlaybackFrameRate(5);

Make sure to set this before calling Megacool.share()

Playback frame rate: 5
Playback frame rate: 10
Playback frame rate: 25

2.5 Max frames

Max number of frames to record, default is 50 frames. Change the maximum amount of captured frames by setting:

Megacool.setMaxFrames(25);
Max frames: 25
Max frames: 50
Max frames: 100

2.6 Delay last frame

Set a delay (in milliseconds) for the last frame of the GIF. The default is 1 second to give a natural break before the GIF loops again.

Megacool.setLastFrameDelay(2000);
No extra delay on the last frame
1000 ms delay (default)
2000 ms delay

2.7 Last frame overlay

With last frame overlay you can add a transparent image on top of the last GIF frame.

+ =

Megacool.setLastFrameOverlay(R.drawable.someoverlay);

2.8 Color table

A gif consists of 256 colors and there are three different ways we can compute those colors, with slightly varying quality and performance tradeoffs. Default is dynamic.

2.8.1 Dynamic

The frames are analyzed before encoding, yielding a good balance between quality and speed. This is the default.

2.8.2 Analyze first

The frames are analyzed before encoding to generate a representative color table. This requires more CPU to create the GIF, but often yields better results when a GIF contains many shades of the same color. This option is only available on iOS, on Android the dynamic color table will be used instead.


3. Save recordings

Being able to save and resume recordings is useful for combining multiple game sessions together. These recordings are stored to disk to be retrieved and potentially resumed later. This is a great case for turn-based games like Words with Friends where waiting for the other user to finish the round could take many hours, and still create a GIF of the whole game play.

3.1 Start and resume a recording

All completed recordings will by default be deleted whenever a new recording is started with either Megacool.captureFrame(view) or Megacool.startRecording(view). To save a recording you can set Megacool.setKeepCompletedRecordings(true);

To save multiple recordings you can set a recording id in a RecordingConfig object. The recording id can be anything you prefer, for instance a name of the current level like "level3".

A recording with a given RecordingId can be resumed until Megacool.stopRecording() is called. After calling Megacool.stopRecording() you can either share the recording or start a new one from scratch with the same RecordingId.

Megacool.startRecording(view, new RecordingConfig().id("level3"));

3.2 Pause recording

When you want to pause a recording that can be continued later, you call pauseRecording.

Megacool.pauseRecording();

3.3 Delete recording

deleteRecording will remove any frames of the recording in memory and on disk. Both completed and incomplete recordings will take space on disk, thus particularly if you're using Megacool.setKeepCompletedRecordings(true); you might want to provide an interface to your users for removing recordings they don't care about anymore to free up space for new recordings.

Megacool.deleteRecording("level3");

3.4 Show a GIF from a specific recording

To show a GIF from a specific recording:

GifImageView gifImageView = Megacool.renderPreviewOfGif("level3");
if (gifImageView != null) {
layout.addView(gifImageView);
gifImageView.start();
}

3.5 Share a GIF from a specific recording

To share a GIF from a specific recording you pass the RecordingId as a parameter to share.

ShareConfig shareConfig = new ShareConfig()
.recordingId("level3");
Megacool.share(shareConfig);

Note: Megacool.share() calls pauseRecording automatically, so if you haven't called Megacool.stopRecording() before next time you call Megacool.startRecording(view), the recording will continue from where you paused.


4 Testing referrals

For this you need two devices. Let’s call them Sender and Receiver. If you have your app installed already, please delete it from both devices.

4.1 iOS

Sender Receiver
Follow the Quickstart guide and build the app to this device.
Run the app and share a link to Receiver. Open the link and press “Play now” to simulate that you’re going to download the app.
Add Megacool.resetIdentity() before Megacool.start(). This will reset the device identity, so it acts as a new device every time the app starts. Remember to remove this after you’re done testing.
Build and run the app. Now this device will receive the EventType.RECEIVED_SHARE_OPENED event with Event.isFirstSession(): true and generate a new install.
Press the home button and re-open the app to instantly receive the EventType.RECEIVED_SHARE_OPENED event, like shown in the
Quickstart guide.

4.2 Android

Sender Receiver
Follow the Quickstart guide and build the app to this device.
Run the app and share a link to Receiver.
Add Megacool.resetIdentity() before Megacool.start(). This will reset the device identity, so it acts as a new device every time the app starts. Remember to remove this after you’re done testing.
Connect this device to your computer and run the following in the terminal, to simulate a new Google Play install: adb shell am broadcast -a "com.android.vending.INSTALL_REFERRER" --es referrer '?_m=REFERRAL_CODE' "YOUR_ANDROID_PACKAGE". Replace REFERRAL_CODE with the _m parameter value from your shared link.
Build and run the app. Now this device will receive the EventType.RECEIVED_SHARE_OPENED event with Event.isFirstSession(): true and generate a new install.
Press the home button and re-open the app to instantly receive the EventType.RECEIVED_SHARE_OPENED event, like shown in the
Quickstart guide.

5. Troubleshooting

When I find myself in times of trouble
Mother Mary comes to me
Speaking words of wisdom:
"submit a detailed debug log"
Paul McCartney

When things turn sour, and you're done running in circles screaming and shouting, you can swap the SDK over in debug mode to let it capture detailed call traces of what's happening, and automatically submit a debug report to us. Do this by calling Megacool.setDebug(true) as early as possible in the session, and when your problem has been reproduced (or you believe it is about to happen), call Megacool.submitDebugData("halp!") to share the issue with us. Please try to explain in the message what you expected to happen, and what happened instead. If you follow this procedure it will be much easier for us to reproduce the issue you're having and get it fixed ASAP.

We appreciate you taking the time to help us find the cause of the bugs, and we're very sorry that we didn't find them ourselves.