What is it ?

SDK stands for Software Development Kit. Basically it’s some code we wrote for you so you can use our technology easily !


Installation:

Since version 1.8, you can use maven central repository to retrieve the dependency:

  • Add the dependency on your project’s build.gradle:

      dependencies {
          compile 'io.apisense.sdk:apisense-android-sdk:1.11.0'
    
          # If you want to be able import more stings in APISENSE
          compile 'io.apisense.sdk:stings-network:1.11.0'
          compile 'io.apisense.sdk:stings-phone:1.11.0'
          compile 'io.apisense.sdk:stings-environment:1.11.0'
          compile 'io.apisense.sdk:stings-motion:1.11.0'
          compile 'io.apisense.sdk:stings-visualization:1.11.0'
      }
    

Add Manifest permissions

We intend to let you a total control on your application permissions so the SDK does not add permissions by default.

That being said, you may want to add those permisions in your manifest.xml:

  • If you need to communicate with the apisense server (most likely the case):
    • <uses-permission android:name="android.permission.INTERNET"/>
  • If you intend to use the WifiSting:
    • <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    • <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
  • If you intend to use the LocationSting:
    • <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    • <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    • <uses-permission android:name="android.permission.ACCESS_COARSE_UPDATES" />
  • If you intend to capture media with the FormSting:

      <!-- only needed for sound capture -->
      <uses-permission android:name="android.permission.RECORD_AUDIO" />
    
       <!-- for any kind of media -->
      <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="18" />
      <application ...>
          <!--[...]-->
          <!-- applicationId is the package of your application,
          this variable is automatically set when building with gradle -->
          <provider
              android:name="android.support.v4.content.FileProvider"
              android:authorities="${applicationId}.APSFileProvider"
              android:exported="false"
              android:grantUriPermissions="true" >
              <meta-data
                  android:name="android.support.FILE_PROVIDER_PATHS"
                  android:resource="@xml/aps_media_path"
              />
          </provider>
          <!--[...]-->
      </application>
    
  • If you intend to use the CallSting:
    • <uses-permission android:name="android.permission.READ_PHONE_STATE" />
  • If you intend to retrieve the foreground application with SystemSting:
    • <uses-permission android:name="android.permission.GET_TASKS"/>, under API 22.
    • <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" tools:ignore="ProtectedPermissions"/>, from API 22.

  • If you intend to monitor the current application on foreground with SystemSting:

      <!-- Declare an AccessibilityService to monitor the current foreground App -->
      <service
          android:label="@string/accessibility_service_name"
          android:name="io.apisense.sting.phone.system.WindowChangeDetectingService"
          android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
          <intent-filter>
              <action android:name="android.accessibilityservice.AccessibilityService"/>
          </intent-filter>
          <meta-data
              android:name="android.accessibilityservice"
              android:resource="@xml/accessibility_service_config"/>
      </service>
    
      <!-- Put this configuration in res/xml/accessibility_service_config.xml -->
      <accessibility-service
          xmlns:android="http://schemas.android.com/apk/res/android"
          android:accessibilityEventTypes="typeWindowStateChanged"
          android:accessibilityFeedbackType="feedbackGeneric"
          android:accessibilityFlags="flagIncludeNotImportantViews"
          android:description="@string/accessibility_service_description"/>
    

Initialize

In your Application class, you can initialise the APISENSE Sdk like this:

import io.apisense.sdk.APISENSE;

// Create the SDK instance
APISENSE apisense = new APISENSE(application); // Standard config

Authorize your SDK

Your sdk instance need to be authorized to contact our server, to do so you will have to call the following method:

apisense.useSdkKey("mySdkKey"); // Authorize your SDK to contact the server

Handle private crops

If you want to allow people to see your private crops, add the following line before creating the Sdk singleton:

apisense.useAccessKey("accessKey"); // Authorize the SDK to see your private crops

Configure your SDK

You can add custom Sting implementations using Dagger modules:

apisense.addStingsModules(new MyOwnStingModule()); // More Stings injected!
apisense.addStingsModules(new PhoneStingModule(), new NetworkStingModule())

Instanciate the SDK

When done with the configuration, you will have to call the method getSdk() at least one to initialize it, then the method will return the same instance at each call.

// Create the Sdk singleton and use it
APISENSE.Sdk sdk = apisense.getSdk(); // Initialisation
APISENSE.Sdk sdk2 = apisense.getSdk(); // Same instance as sdk

Install and start a specific crop

You may want to install a specific crop, here is a sample to do so:

// You can find this value by looking at your crop details menu on apisense.io
String cropIdentifier = "ZzQq5wW8zgIn3mJ3ylnw";

// Log your Bee user in if no session available.
if (sdk.getSessionManager().isConnected()) {
    installExperiment();
} else {
    sdk.getSessionManager().applicationLogin(new SimpleAPSCallback<Void>() {
        @Override
        public void onDone(Void aVoid) {
            installExperiment(); // You can now install the experiment.
        }
    });
}


// Install and start the collect, using your accessKey if the access is private
private void installExperiment() {
    sdk.getCropManager().installOrUpdate(cropIdentifier,  new SimpleAPSCallback<Crop>() {
        @Override
        public void onDone(Crop crop) {
            // Crop Installed, ready to be started.
            sdk.getCropManager().start(crop, new SimpleAPSCallback<Crop>() {
                @Override
                public void onDone(Crop crop) {
                    // Crop finally started.
                }
            });
        }
    });
}

Handle Runtime permissions

Android 6 comes with the support of permissions at runtime.

The permissions needed by a crop depends on the Stings used by your script, and this may change at runtime, after a crop update for instance).

To help you handle needed permissions on crop startup, the SDK provide 2 methods:

sdk.getCropManager().neededPermissions(crop);
sdk.getCropManager().deniedPermissions(crop);

So, before starting a crop, you may want to check for permissions:

private void start(Crop crop) {
    Set<String> deniedPermissions = sdk.getCropManager().deniedPermissions(crop);
    if (deniedPermissions.isEmpty()) {
        sdk.getCropManager().start(crop, callback);
    } else {
        // Request missing permissions before starting the crop
    }
}

The permissions needed at runtime are checked before usage on the official Stings, so if you don’t check for permissions, your application will not crash because of a SecurityException but you crop will not have the expected behavior.

Use a service to gather data

You can use an Android Service to avoid collect interruption when your application is shut down.

Configure the service

To use this feature, you will have to activate if in the SDK configuration:

apisense.useScriptExecutionService(true)

Then your Application class should extend our io.apisense.sdk.APSApplication class or implement the dagger interface HasDispatchingServiceInjector as follow:

package io.apisense.sdk;

import android.app.Application;
import android.app.Service;
import dagger.android.HasDispatchingServiceInjector;
import io.apisense.sdk.APISENSE;

public class MyApplication extends Application implements HasDispatchingServiceInjector {
    private APISENSE.Sdk sdk;

    public APISENSE.Sdk getSdk() {
        return sdk;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        sdk = new APISENSE(this)
            .useSdkKey("mySdkKey")
            .useScriptExecutionService(true)
            .getSdk();
    }

    @Override
    public dagger.android.DispatchingAndroidInjector<Service> serviceInjector() {
        return sdk.serviceInjector();
    }
}

Customize your service notification

The service being run in foreground in order to not being killed by Android O, we will display a notification for your application.

You can customize the content of this notification by overriding the following resources:

  • In res/values/strings.xml:
    • aps_execution_service_name: Notification title
    • aps_execution_service_description: Notification content
  • In res/drawable-{hdpi,mdpi,xhdpi,xxhdpi}{,-v9,-v11}:
    • aps_service_notification.png: Notification icon.

Download:

You need to create a Podfile, just run pod init in a terminal if you don’t already have one. Then you just add Apisense to it. The file should looks like:

source 'https://github.com/APISENSE/ios-sdk-specs' # Our private repo
source 'https://github.com/CocoaPods/Specs.git'

platform :ios, '9.3'
use_frameworks!

target 'yourTarget' do
    pod 'Apisense', '~> 1.0.0' # Current version released
end

Then run pod install. This will download it.

Usage

At this point, you should be able to create an instance of Apisense SDK using:

import Apisense

let custom = Sensor(name: "Custom", stingName: "custom", description: "Custom sting", iconID: "path/to/resources")
let newSensors = Set([custom])
let mapping = [ custom.getStingName() : CustomSting.self ]

sdk = APISENSE.sharedInstance
        .useAccessKey(key: accessKey) // Optional: Authorize the SDK to see your private crops
        .useSdkKey(key: sdkKey) // Required: Authorize your SDK to contact the server
        .addStings(sensors: newSensors, mapping: mapping) // Optional: Add custom stings
        .getSdk() // Required: Initialization

Note that everytime you’ll call APISENSE.sharedInstance.getSdk() you’ll get the same instance.

Run your application, if everything is ok you should see in the log section:

[Info]: APISENSE® SDK initialized