Skip to content

Implementation ​

This guide uses Android SDK 5.1.0. See installation and update instructions.

Create AudioRecognition instance ​

At the beginning of your app, you need to create an instance of SORIAudioRecognizer class. This class is responsible for audio recognition and provides methods to start and stop service.

Please add the following code to your app's lifecycle, for example in onCreate method of your main activity.

kotlin
import com.iplateia.sorisdk.SORIAudioRecognizer

val sori = SORIAudioRecognizer(
    "YOUR_SORI_APP_ID",
    "YOUR_SORI_SECRET_KEY",
)
java
import com.iplateia.sorisdk.SORIAudioRecognizer;

SORIAudioRecognizer sori = new SORIAudioRecognizer(
    "YOUR_SORI_APP_ID",
    "YOUR_SORI_SECRET_KEY"
);

TIP

You can get your API key and secret key from the SORI API dashboard. Please refer to the SORI API documentation for more information.

WARNING

Please make sure to keep your API key and secret key secure. DO NOT hardcode them in your app's source code. Instead, use a secure method to store and retrieve them, such as BuildConfig with local.properties or environment variables.

Implement your listener ​

ISORIListener interface defines the callback methods that will be called when the audio recognition is started, stopped, and when the result is received. You can implement this interface in your own class and override the methods to handle the events.

In many cases, the onCampaignFound event is needed by UI to update the detected materials. For convenience, you can use the SORIListener class as a base class and override only the methods you need.

kotlin
class MySORIListener : SORIListener() {
    override fun onStateChanged(state: String) {
        Log.d("TAG", "SORI State changed: $state")
    }
    override fun onCampaignFound(campaign: SORICampaign) {
        // Handle the campaign found event
        Log.d("TAG", "Campaign found: ${campaign.name}")
    }
}
java
public class MySORIListener extends SORIListener {
    @Override
    public void onStateChanged(String state) {
        Log.d("TAG", "SORI State changed: " + state);
    }
    
    @Override
    public void onCampaignFound(SORICampaign campaign) {
        // Handle the campaign found event
        Log.d("TAG", "Campaign found: " + campaign.getName());
    }
}

Or simply extend the SORIListener() directly in your activity or fragment. It is a good practice to use SORIListener() as a base class, so you can easily interact with your UI without passing the context or activity to the listener.

kotlin
class MyActivity : AppCompatActivity(), SORIListener() {
    private lateinit var adapter: CampaignTimelineAdapter
    // ...

    override fun onCampaignFound(campaign: SORICampaign) {
        // Handle the campaign found event
        Toast.makeText(this, "Campaign found: ${campaign.name}", Toast.LENGTH_SHORT).show()
        adapter.addItem(campaign)
    }

    // ...
}
java
public class MainActivity extends AppCompatActivity implements SORIListener {
    private CampaignTimelineAdapter adapter;
    // ...

    @Override
    public void onCampaignFound(SORICampaign campaign) {
        // Handle the campaign found event
        Toast.makeText(this, "Campaign found: " + campaign.getName(), Toast.LENGTH_SHORT).show();
        adapter.addItem(campaign);
    }

    // ...
}

Then, set the listener to the SORIAudioRecognizer instance.

kotlin
val listener = object : SORIListener() {
    override fun onCampaignFound(campaign: SORICampaign) {
        Log.d("TAG", "Campaign found: ${campaign.name}")
    }
}
// or instance of your own listener

sori.setListener(this, listener)
java
SORIListener listener = new SORIListener() {
    @Override
    public void onCampaignFound(SORICampaign campaign) {
        Log.d("TAG", "Campaign found: " + campaign.getName());
    }
};
// or instance of your own listener

sori.setListener(this, listener);

SORIAudioRecognizer will setup a BroadcastReceiver to listen for the recognition events and call the appropriate methods on your listener.

Enable Audio Marker recognition ​

Audio Marker recognition is disabled by default. In the default mode, the SDK only performs audio fingerprint recognition. Enable Audio Marker recognition only when your SORI Console setup uses Audio Markers.

kotlin
import com.iplateia.sorisdk.SORIConfig

sori.setConfig(
    SORIConfig(audiomarker = true)
)

For current-marker UI and later activity enrichment, use the SDK-managed state and activity result APIs. The SDK manages expiry, marker traits and updates to the same activity. Existing campaign and discovery callbacks remain compatible.

TIP

If your campaign should be delivered only when a marker is detected, configure SORIConfig(audiomarker = true, requirePatternMarkerForCampaign = true).

Render an ordered continuous-activity timeline ​

The following guidance is for existing campaign callbacks. For the new SDK-accepted activity results, use the exact-ID update rules in Audio Marker state and activity results; these results can update the matching historical activity.

onCampaignFound can first deliver a campaign for a material and then deliver a marker-refined campaign for that same activity. Show the first callback immediately, but keep an explicit current segment in application state. Use the public campaign.activityId, campaign.materialId, and campaign.trait?.marker fields when they are present.

  • A callback whose non-empty campaign.activityId matches the current row may replace that row's campaign presentation fields. Never search older rows by activity, campaign, or material ID.
  • If an older server omits campaign.activityId, a campaign.materialId fallback may apply only to the current row. It is not a global identity.
  • Keep the time, position, and other first-observation metadata stored for the current row. A refinement callback updates campaign and marker data, not the original observation.
  • Once campaign.trait?.marker or refined campaign fields are present, keep them when a later callback in the segment has no marker or less complete data.
  • When a different campaign.materialId is observed, insert a new current row and seal the previous segment. Ignore any delayed refinement for that older row.

For example, A(activity-1) -> A'(activity-1, marker) -> A(activity-1, marker miss) remains one row. By contrast, A(activity-1) -> B(activity-2) -> delayed A'(activity-1) keeps B current and leaves the historical A unchanged.

Start and stop recognition ​

To start the audio recognition, call the startRecognition method on the SORIAudioRecognizer instance.

kotlin
sori.startRecognition(this)
java
sori.startRecognition(this);

This will start the audio recognition service and show a notification to the user.

To customize your notifications, you can pass title, body, and icon parameters to the startRecognition method.

kotlin
sori.startRecognition(
    this,
    title = "Your APP Name",
    body = "Finding something special...",
    icon = R.drawable.your_app_icon // optional
)
java
sori.startRecognition(
    this,
    "Your APP Name",
    "Finding something special...",
    R.drawable.your_app_icon // optional
);

If you want to have more control over the notification, Please refer to the Customizing the Notification section.

To stop the audio recognition, call the stopRecognition method on the SORIAudioRecognizer instance.

kotlin
sori.stopRecognition(this)
java
sori.stopRecognition(this);

Handling the campaign action ​

When a campaign is found, you can implement the onCampaignFound method in your listener to handle the action. You can use the SORICampaign object passed to the method to get the details of the campaign and perform the action.

The SORICampaign object contains the property actionUrl which is a url that you can use to open the campaign in your app or in a web browser. The actionUrl property is a string that contains the url to open the campaign. So you can use it to open the campaign in your app or in a web browser. However, SORI SDK provides a convenient method SORIAudioRecognizer.shared().handleActionURL to open the campaign in user's default url handler.

It is useful to decide whether to open the campaign in your app or in a web browser because the actionUrl may contain a deep link to your app or a web link to open in a web browser.

kotlin
// in your ui
if (campaign.actionUrl != null) {
    SORIAudioRecognizer.shared().handleActionURL(this, campaign.actionUrl)
} else {
    // Handle the case where there is no action URL
}
java
// in your ui
if (campaign.getActionUrl() != null) {
    SORIAudioRecognizer.shared().handleActionURL(this, campaign.getActionUrl());
} else {
    // Handle the case where there is no action URL
}

Including metadata for Webhook ​

You can include custom metadata in the webhook events sent to your server. This metadata can be used to identify the user or device that triggered the event. To include metadata, you can use the setMetadataProvider method on the SORIAudioRecognizer instance.

kotlin
sori.setMetadataProvider(
    object : ISORIMetadataProvider {
        override fun getMetadata(): Map<String, String> {
            return mapOf(
                "user_id" to "12345",
                "device_id" to "67890"
            )
        }
    }
)
java
sori.setMetadataProvider(new ISORIMetadataProvider() {
    @Override
    public Map<String, String> getMetadata() {
        Map<String, String> metadata = new HashMap<>();
        metadata.put("user_id", "12345");
        metadata.put("device_id", "67890");
        return metadata;
    }
});

getMetadata method should return a map of key-value pairs that will be included in the webhook event. The method will be called every time a webhook event is sent, so you can update the metadata dynamically if needed.

API Documentation ​

Please refer to the API References for more details on the SORIAudioRecognizer class and other related classes such as SORIListener and SORICampaign.