Knowledgebase
Android - SDK & Implementation Guide
Posted by Innometrics Profile Cloud on 11 Feb 2016 17:59

Below guide can be downloaded here Android - SDK & Implementation Guide.pdf

Method References

http://public.innomdc.com/android-library/method-references/

http://public.innomdc.com/iql-library/

Requirements

  • Android 2.2 (API 8) and above
  •         Permissions in the AndroidManifest.xml

 <uses-permission android:name="android.permission.INTERNET" />

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> 

 

Include library

You can do it in two ways:

  1. Download it directly from a link (add jar in Eclipse, Intellij Idea/AndroidStudio). This build contains all required libraries:
    1. Innometrics library
    2. IQL 1.0
    3. com.google.code.gson 2.2.4
    4. antlr-runtime 3.5
  1.       Include maven dependencies into pom.xml:

 ...<repositories>

<repository>

...

<id>innometrics</id>

<url>http://nexus.innomdc.com/nexus/content/groups/public/</url></repository>

...

</repositories>

...

<dependencies>

...

<dependency>

<groupId>com.innometrics.android</groupId>

<artifactId>Innometrics</artifactId>

<version>1.0.0</version></dependency>

...

</dependencies> 

Configure parameters

Static config

Create config file innometrics.xml and place it in res/value folder.

Values for config you can find in the GUI: Install "Android Data Collection" app, create first section (name of your android app), go to Settings tab.

              

 <?xml version="1.0" encoding="utf-8"?>

<resources>

<string name="innometrics_company_id">1</string>

<string name="innometrics_app_key">appkey</string>

<string name="innometrics_bucket">bucketname</string>

<string name="innometrics_section">sectionname</string>

<bool name="innometrics_debug">false</bool>

<string name="innometrics_events_limit">1000</string>

</resources> 

"innometrics_events_limit" - is not mandatory, but it's important setting that specifies the maximum size of the profile (in number of events) that will be stored locally and controls RAM occupied by the library.

Run-time config

In case you want to separate tracked data to different section, for example based on user's country set in app settings, you can init library with dynamic config (only once during initialization), see reference for available methods.

Create instance of library

In code, before using InnometricsAPI you need to get instance of InnometricsAPI class. It could be done in few ways:

Method returns instance of the class:

              

 InnometricsAPI innometricsAPI = InnometricsAPI.getInstance(context); 

 Method returns instance of the class and synchronize profile with Backend:              

 InnometricsAPI innometricsAPI = InnometricsAPI.getInstance(context,true);

 //Equal to

InnometricsAPI innometricsAPI = InnometricsAPI.getInstance(context);

innometricsAPI.sync(InnometricsAPI.SyncStrategy.ONLY_PULL); 

  Method sets "userProfileID" (by default profileId is hashed ANDROID_ID) as current userprofile ID and returns instance of InnometricsAPI: 

 InnometricsAPI innometricsAPI = InnometricsAPI.getInstance(context,"userProfileID");

 //Equal to

InnometricsAPI innometricsAPI = InnometricsAPI.getInstance(context);

innometricsAPI.setCanonicalProfileId(userProfileID) 

 

Example of usage:  

 @Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

innometricsAPI = InnometricsAPI.getInstance(this);

}}

Send data to profile

Track user events 

Call trackEvent(). It takes two arguments: eventDefinition - string codename of the event, event - hash containing parameters of this event. For instance to collect data about searches done by user you can use this code: 

 btnSearch.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View view) {

HashMap<String, Object> event = new HashMap<String, Object>();

event.put("search type", searchType.isChecked() ? "advanced" : "Normal");

event.put("search string", searchString.getText().toString());

innometricsAPI.trackEvent("eventSearch", event);

}

}); 

 

Track exceptions

Use trackError(Throwable throwable):

 try {

// Some code

} catch (Exception e ){

InnometricsAPI.getInstance(context).trackError(e);

// Your exception handling

 

Store profile attributes

To set profile attribute use setAttribute(String key, String value). This command will create new attribute in current collect application and section, or override it if attribute with the same key already exists. 

To get attribute use method getAttributes(String collectApp, String section, GetAttributeListener listener). Parameters collectApp and section - define collection application and section from which you want to get attributes. Listener - callback function to be executed when requested attribute obtained. Listener takes as an argument object carrying list of all attributes. You can get a particular attribute by key using method attributes.getDataByName(String Key);

 innometricsAPI.getAttribute("web","mondify",new GetAttributeListener() { 

@Override

  public void getAttribute(Attribute attributes) {

    System.out.println("Profile web status = " + attributes.getDataByName("Status"));

    }

}); 

Set session data

Default session data

These session data are set by default to each session:

  •         "device-model" 
  •         "display-density"
  •         "phone-type"
  •         "display-height"
  •         "display-width"
  •         "network-operator-name"
  •         "app-version-code"
  •         "app-version-name"
  •         "version-os"
  •         "android-device-id"

You can change default set of session data with setDefaultSessionData(int[] dataFlags), argument is array of constants:

  • APP_CODE - app-version-code, app-version-name
  • DYSPLAY - display-height, display-width
  • DEVICE_ID - android-device-id
  • DEVICE_MODEL - device-model
  • VERSION_OS - version-os
  • NETWORK_OPERATOR_NAME -network-operator-name
  • PHONE_TYPE -phone-type


In this example is default session data will be only "display-height", "display-width" and "phone-type": 

 api.setDefaultSessionData(new int []{InnoNames.PHONE_TYPE,InnoNames.DYSPLAY}); 

              

Custom session data

You can add your custom session data with setSessionData(String key, Object value):

 api.setSessionData("key","valueObject"); 

Profiles identification and merging

Terms:

  • temporary profile ID used in case user isn't authenticated
  • canonical profile ID - user is authenticated in your system.

By default, when you init library (first time get instance of InnometricsAPI) it generates temporary profile ID with hashed from ANDROID_ID.

Once your user is authenticated, you need to set canonical id with mergeProfile(String canonicalProfileID) and then merge data of temporary profile into canonical with mergeProfile():

 

 InnometricsAPI innometrics;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

Button btnLogin = (Button) findViewById(R.id.buttonLogIn);

innometrics = InnometricsAPI.getInstance(this);

 // All events are collected in temporary profile now.

 // You can set temporary ID with your own algorithm with this method:

    // innometrics.setTempProfileID(generateTempId());

 btnLogin.setOnClickListener(new View.OnClickListener() {

        @Override

        public void onClick(View view) {

 // After user have passed authorization you can set his login as profile ID and make it canonical

name = textLogin.getText().toString();

innometrics.setCanonicalProfileId(name);

//profileId will be hashed nameinnometrics.mergeProfile();

 //Now all events from temporary profile will be merged to canonical one

}

}); 

Manual profile synchronization

Besides automatic periodical profile synchronization with cloud (frequency could be adjusted, see xml config description), you can do it manually with sync(SyncStrategy strategy).

Strategy is a enumeration defined inside InnometricsAPI class. Possible values: 

  • PUSH_AND_PULL - first send all not synced data to server and then get the entire profile.
  • PULL_AND_PUSH - first get the entire profile from server and then send not synced data.
  • ONLY_PUSH - data only sends, nothing receives.
  • ONLY_PULL - profile only receives, nothing sends.

Since synchronization is running in separate thread it's not guaranteed that data will be updated immediately. But you can register listener - a function to be executed when synchronization is completed. To do that you need to implement ApiListener interface and its single method handlResult(int type).Type could be: 

  • InnoNames.SYNC_SUCCESS if synchronization succeeds,
  • InnoNames.SYNC_FAIL if it fails.

To register listener you need to call addInnometricsListener(ApiListener listener) method and pass instance of ApiListener class.

 

 @Override

protected void onCreate(Bundle savedInstanceState) {

innometrics = InnometricsAPI.getInstance(getActivity(), userName);

// API initializationinnometrics.addApiListener(new TestListener());// register listener in API

}

// Listener implementation

class TestListener implements ApiListener {

@Override

public void handleResult(int type) {

switch (type){

case InnoNames.SYNC_FAIL:{

System.out.println("Synchronization failed");

break;

}

case InnoNames.SYNC_SUCCESS:{

System.out.println("Synchronization succeed");

break;

}

}

}

}

api.sync(InnometricsAPI.SyncStrategy.ONLY_PULL); 

IQL

IQL - Innometrics Query Language is built to:

  • evaluate segments (return true or false for IQL expression)
  • and to search and filter data in profile

IQL is also running in a separate thread so it needs registered listener to handle query result. For this purpose IqlListener interface can be used which works same way as ApiListener but gets IqlResult as an argument. To run IQL query call method executeIQL. It takes three arguments: 

  • query - query string, 
  • listener - instance of IqlListener class, 
  • filtrate - boolean flag which defines whether to filter profile or not. If filtrate is false then IqlResult will contain indication if something were found or not (true or false) and the whole profile - in that case IQL query will execute faster. If set to true - IqlResult will contain corresponding to the query part of the profile.

 public boolean executeIQL(String query, IqlListener listener, boolean filtrate) 

After query execution handleResult(IQLResult result) will be called for all registered listeners and result argument will contain query result.

IQLResult has following methods:   

 

 public boolean isFound() // returns true if at least one record was found

public long getCount() // returns number of events which satisfy the condition

public JSONObject getFiltredProfile() // returns filtered profile in JSON format

public ArrayList getEventData(String name) // returns ArrayList of events filtered by "name"

public ArrayList getSessionData(String name) // returns ArrayList of sessions filtered by "name" 

 Example:

 class MyListener implements IQLListener,ApiListener {

private volatile boolean firstPass = true;

         // implementing two interfaces

@Override

public void handleResult(int type) {

switch (type) {

case InnoNames.SYNC_FINISH:{

 // running query execution after successful synchronization and pass this object as listener

 innometricsAPI.executeIQL("collectApp(\"web\").section(\"web\").event(\"140013\").eventData(\"dd-6147\").neq(\"\").inLast(\"events.minute\",5)",this);

break;

  }

case InnoNames.SYNC_FAIL:{

Toast.makeText(LoginActivity.this,"Connection Error",Toast.LENGTH_LONG).show();

handler.sendMessage(handler.obtainMessage(2));

break;

}

}

}

@Override

public void handleIQLResult(IQLResult iqlResult) {

 // if there are events that match IQL query

if (iqlResult.isFound()) {

  // Getting needed event data from IQL result object

ArrayList productIdList = iqlResult.getEventData("dd-6147");

ArrayList groupIdList = iqlResult.getEventData("dd-3795");

Object productID = productIdList.isEmpty() ? null :productIdList.get(0);

Object groupID = groupIdList.isEmpty() ? null :groupIdList.get(0);

System.out.println("productID = " + productID);

System.out.println("groupID = " + groupID);

}

}

}

 

Example of filtered profile:

 

 {

    "profile": {

        "attributes": [],

        "id": "ae0ccae3268f91c5f27a5997e37a17d5",

        "services": [],

        "sessions": [

            {

                "collectApp": "web",

                "services": [

                    {

                        "data": {

                            "countryCode": 0,

                            "countryName": "Russian Federation",

                            "region": "61",

                            "city": "Rostov-na-donu",

                            "latitude": 47.231293,

                            "longitude": 39.723297,

                            "dmaCode": 0,

                            "areaCode": 0,

                            "metroCode": 0

                        },

                        "id": "geo"

                    }

                ],

                "data": {},

                "events": [

                    {

                        "services": [],

                        "data": {

                            "dd-2310": "---",

                            "dd-4739": "Garden Easy Chair. Anthracite",

                            "dd-1636": 3,

                            "dd-6147": "201",

                            "dd-5810": "201",

                            "dd-5337": 0,

                            "dd-13746": "54"

                        },

                        "definitionId": "140013",

                        "id": "nl5brzhf",

                        "createdAt": 1387789099138

                    },

                  {

                        "services": [],

                        "data": {

                            "dd-2310": "---",

                            "dd-1636": 3,

                            "dd-6147": "222",

                            "dd-5810": "201",

                            "dd-5337": 0,

                            "dd-13746": "54"

                        },

                        "definitionId": "140013",

                        "id": "nl5brzhf",

                        "createdAt": 1387789099138

                    }

                ],

                "id": "3jclpn76c1",

                "section": "676",

                "modifiedAt": 1387789109962,

                "createdAt": 1387789094868

            }

        ],

        "version": "1.0"

    }

}

 

For instance call of  iqlResult.getEventData("dd-6147"); on this profile will return List<JsonPrimitive> containing two JSON primitive objects with values "201" and "222".

Saving a battery

There are periodical network synchronization process work in the library. You can suspend them when your app minimizes, goes to sleep or display turns off and resume it when application is active again. In order to do that use methods innometricsAPI.suspendBackgroundSync() andinnometricsAPI.resumeBackgroundSync():

 abstract public class AbstractMyActivity extends Activity {

    InnometricsAPI innometricsAPI;

    Random r = new Random();

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        innometricsAPI = InnometricsAPI.getInstance(this);

    }   

@Override

    protected void onStart() {

        super.onStart();

        innometricsAPI.resumeBackgroundSync();

    }   

@Override

    protected void onStop() {

        super.onStop();

        innometricsAPI.suspendBackgroundSync();

    }

In this example defined abstract class extends Activity class and inherits all other Activity classes. By doing this you able to override methods onCreate, onStart and onStop.

onCreate - contains initialization of the library. Since this is singleton initialization it will be called only once during first Activity creation.

onStart and onStop - contain sleep and wake up methods of - this allows to reduce energy consumption in minimized application.

  

Debug

Debug messages can be enabled in innometrics.xml with  

 <bool name="innometrics_debug">true</bool> 

 

Now you can see in console log what data is sent to Profile Cloud, what profile ID is used and API URLs:

05-27 08:42:50.169    1320-1357/? D/Inno﹕Send Url https://api.innomdc.com/v1/companies/<companyID>/buckets/<bucketName>/profiles/<profileID>?app_key=<appKey>&max_return_events=1000

 

(0 vote(s))
Helpful
Not helpful

Comments (0)