#Datastreams for android (Android library)

###Table of contents
**About**  
**How does it work**   
**Initialisation**   
**Deployment**  
**Constraints**  
**Known issues**  
**Class diagram**  
**Application flow**

**Repository**  
<https://bitbucket.org/nromeijn/datastreamsforandroid>

## About
Datastreams for android is an addon on the already existing application Datastreams. (internally developed at Adversitement). It is a data analasys application written in java, scala and AngularJS.

The 'Android' addon to datastreams makes it possible to add native android apps to datastreams. After an Android app has been added it can be navigated and configured through a webinterface. This is done by including an Android module in said Android native application.

With the live configurator users can then select every view element of the android app and navigate through the available screens. After selecting a view element the user can specifiy how it should be followed (click/view). The data of the selected view element is then sent to the specified endpoint.
![usecases](assets/ContextDiagram.png)  
**Appflow**  

![showcase](assets/live-conf-live.png)  
**Live configurator** which is currently in the process of linking views
##How does it work?
Dynamic updates through a configuration object.
Within the android live configurator in datastreams the user defines which elements he or she wants to track (click/view). The moment the user saves these changes the application generates a new 'config object' (json) and publishes this to the configstore (an external database) The next time the android app is booted it will retrieve the latest version of this 'config object' and the app will start tracking the newly defined elements for data analasys.

###Configstore
The configstore is the database where all generated configuration objects are saved. Based on these configuration objects the app knows which elements to track and when to track them. At the moment of writing the database being used is ```dbdevo2mcio``` located @  
```debdevo2mcio@db-dev.o2mc.io``` For credentials ask Peter Lem or see PassPack.

#### Overriding the config object
Within the library it's possible to override settings set in the Datastreams manager application. The developer can actually enforce certain settings such as GPS location tracking, only sending data when wifi is available and tracking unique users.

**WARNING** Overriding the settings within the library has as result that no settings from the datastream manager will work anymore and it will use system defaults instead.   
(Make sure when overriding certain settings that the application also has the correct **permissions** set in the **Androidmanifest.xml** file)

**Available settings**  
settings can be accessed by calling ```getSettings()``` on a Datastreams intance (the library)

  
|Setting        | default value| description |
| ------------- |:-------------:| :-----|
| trackLocation     | false| If enabled this will track the userlocation and send GEO points every dataDispatch |
| trackNetworkstate      |false      | If enabled it will track the network type of the phone (4g/3g/lts/wifi/gprs)|
| trackUnique | true     | If enabled it will track users by a uniqueId|
| onlySendWhenWifi | false | If enabled it will only dispatch gathered data when wifi is available
|endpoint|false| Attribute in which the DimML endpoint resides where gathered data is dispatched to|
|mapViews|false| If enabled the application will map the views of each activity (take screenshots)|


## Initialisation (Nearly a SLOC!)
To initialise the library the app needs to have a class which extends from ```Application```. In the constructor of that class it's possible to initialise the library the following way:  

```public App(){```  
```tracker = new Datastream(this);```  
```tracker.getSettings().setMapViews(false);```  
```}```

setMapViews is the setting that defines if the app needs to 'map / take screneshots of every screen'. This is only used once when the app needs to be added to Datastreams or if there are new view elements added after an update.

## Deployment

Currently (17-05-2016) there is no way of structured deployment for the library. As for now it has to be manually included into the application by compiling it against the android app as 'module'. 

1. In Android studio right click the application package.
![example1](assets/androidModuleExample1.png)

2. Select the **tab** dependecies in the window that opens  ![example1](assets/androidModuleExample2.png)

3. Press the **+** sign in the bottom of the window and select '**module dependency**'. Next add the AndroidDSmodule as dependency ![example1](assets/androidModuleExample3.png)

4. Make sure the dependency is added to the list and set the dropdown option to compile (should be by default). After this the module should compile togheter with the app. ![example1](assets/androidModuleExample4.png)

##Constraints
Android 4.3+  
Active internet connection  
Current library relies on a hack to inject clicklisteners in the application code. **This may stop working in future android versions**

##Known issues
Viewmapper does not work automatically  
Viewmapper does not stitch togheter screenshots. It only takes a snapshot of the activity as it is when the 'activity' opens.  

**Possible solution:** This might be resolved by adding a listener on the view unload event (when a view goes out of the viewport then: retake screenshot because new views 'probably' have been loaded)



## Class diagram
![classdiagram](assets/classdiagramdsandroid.png)

## Flow
![appflow](assets/appflowdsandroid.png)


#Datastreams for android (Datastreams implementation)
## Components
**Datastreams**  
AndroidTrackingController  
Publisher

**Angular app**   
Main  
Sidebar  
Tracker

## Constraints

_Angular, GRUNT, scala, java_

For datastreams to work 'as' android manager the company coupled with the 'logged in' account needs to have an Android app added to it's ownership. (Compareable with how it currently works with 'domains').  

An Android app can be (and is currently) added by using ```company.addAndroidApp(AndroidApp.create("hema.android", "o2mc.io.dimmldependency.dimmldependency"));``` (**In global.java**)
In which the first parameter is the name of the app. (**_Must be a domain identifier_**) and the second parameter is the Application identifier (package name of the android application).

## Deployment
Datastreams uses the deployment method defined by _Zeki_. Nothing has been added or changed in that regard. The only thing of notice for deploying Datastreams for Android is that when changes are being made to the Angular app; it will need to be recompiled by **GRUNT**  

To do this:  
Navgate to the ```public/app``` directory  
Run ```npm install```  
Run GRUNT ```Grunt start``` it should auto pick the default build configuration


## Known issues
The viewmapping is not realtime. (It should be possible and easily done though with the implementation of websockets)  

Viewmapping currently only works with very simple apps. (e.g.) static views. This could possibly be solved by streaming realtime view data to the live configurator with the use of websockets.

## Usage
When an Android app is coupled to the logged-in user account it can be accessed by:  
Creating a source connection with the type: _**Android native**_  
After creating the source connection select the android app from the appearing dropdown.  
(Make sure said android app has the Datastreams for Android dependency included in it's source and the entire app has been mapped. _See viewmap documentation from the library._)  

When done creating the source connection add it to a stream and navigate to the live configurator.  The configurator will take a moment to retrieve the application data and present it when done.  

It's possible to navigate through the application screens by pressing the arrow buttons. Each element within the application screen is clickable and registered as 'view-element'. On each of these view elements it's possible to attach a viewlistener, clicklistener or both.

**Alternatively** it's possible to link events togheter. e.g. gather data from view 1,2,3,4 when radiobutton is pressed. This is done by pressing the 'link' button (Magnet icon). When the magnet is active you're able to link elements togheter by clicking on them.

When done defining clicklisteners & viewlisteners you're ready to deploy the tracking configuration. When pressing save and continue the Angular app will deploy the configuration to the datastreams for android configstore. This is a database where all configurations for Android apps reside. The android apps can then retrieve these configurations by a REST api.
The configstore is located @ ``DBDEVO2MCIO@DB-DEV.O2MC.IO`` database ``dsAndroid``.

At he moment of publishing yourstream it uses the already existing concepts of datastreams. The system will automatically generate a DimML script and passes down the path to this script to the configstore so the app knowsn where to send its data.

## Configstore REST Api
The configstore rest API is currently published on my own private DimML environment.   
Endpoint: http://dynamic.dimml.io/?dimml.concept=//nromeijn@datastreams.configstore/API&appid=<APP PACKAGE NAME>

Contents:

```
concept API {
	rest '
		POST / => StoreConfig
		GET / => LoadConfig  
	'
}

concept StoreConfig extends DB {
	flow
		=> sql['insert into `dsandroid`.`config` (json, appid) values(:json, :appid) ON DUPLICATE KEY UPDATE json = :json', `db`]
		=> console
		=> out
}

concept LoadConfig extends DB {
	flow
		=> sql['select json from `dsandroid`.`config` where appid = :appid', `db`, batch = '1']
		=> addjson['json']
		=> expand['json']
		=> filter['-appid']
		=> filter['-json']
		=> console
		=> out
}

concept DB {
	rest:json[omitList = 'true']
	const db = `{
		type: 'mysql',
		credentials: <Hidden>
	}`
}
```

### Datastreams for Android trackerconfig
The following file is an example of how the configuration looks that the Android library uses to track defined views / elements.  

```
{  
   "settings":{  
      "location":true,
      "networkstate":false,
      "unique":false,
      "onlySendWhenWifi":false,
      "endpoint":"https://dynamic.dimml.io/img.gif?dimml.concept=//teradata.dev.1@hema.android/AndroidNative"
   },
   "trackers":[  
      {  
         "activity":"MainActivity",
         "settings":{  

         },
         "track":[  
            {  
               "id":"testbtn",
               "type":"click",
               "alias":"This is a btn has been pressed",
               "viewType":"Button",
               "indexWithinActivity":1,
               "tag":null
            },
            {  
               "id":"switch1",
               "type":"click",
               "alias":"switch has been switched",
               "viewType":"Switch",
               "indexWithinActivity":3,
               "tag":null
            },
            {  
               "id":"radioButton",
               "type":"click",
               "alias":"Radio button has been pressed",
               "viewType":"RadioButton",
               "indexWithinActivity":5,
               "tag":null,
               "chainLinks":[  
                  {  
                     "id":"switch1",
                     "viewType":"Switch",
                     "indexWithinActivity":3,
                     "chainNumber":1
                  },
                  {  
                     "id":"toggleButton",
                     "viewType":"ToggleButton",
                     "indexWithinActivity":4,
                     "chainNumber":2
                  },
                  {  
                     "id":"testbtn",
                     "viewType":"Button",
                     "indexWithinActivity":1,
                     "chainNumber":2
                  }
               ]
            },
            {  
               "id":"switch1",
               "type":"click",
               "alias":"New switch has been toggled",
               "viewType":"Switch",
               "indexWithinActivity":3,
               "tag":null
            },
            {  
               "id":"toggleButton",
               "type":"click",
               "alias":"Togglebutton press",
               "viewType":"ToggleButton",
               "indexWithinActivity":4,
               "tag":null
            },
            {  
               "id":"radioButton",
               "type":"click",
               "alias":"naaamRadioButton",
               "viewType":"RadioButton",
               "indexWithinActivity":5,
               "tag":null
            }
         ]
      },
      {  
         "activity":"TestActivity",
         "settings":{  

         },
         "track":[  
            {  
               "id":"anotherbtn",
               "type":"click",
               "alias":"bigBtn has been pressed",
               "viewType":"Button",
               "indexWithinActivity":0,
               "tag":null
            }
         ]
      },
      {  
         "activity":"AnotherTestActivity",
         "settings":{  

         },
         "track":[  
            {  
               "id":"undefined",
               "type":"click",
               "alias":"does nothing; maybe break?",
               "viewType":"Button",
               "indexWithinActivity":1,
               "tag":null
            },
            {  
               "id":"testbtn",
               "type":"click",
               "alias":"back to home we go",
               "viewType":"Button",
               "indexWithinActivity":0,
               "tag":null,
               "chainLinks":[  
                  {  
                     "id":"undefined",
                     "viewType":"Button",
                     "indexWithinActivity":1,
                     "chainNumber":1
                  },
                  {  
                     "id":"taylor",
                     "viewType":"ImageView",
                     "indexWithinActivity":2,
                     "chainNumber":2
                  }
               ]
            },
            {  
               "id":"taylor",
               "type":"view",
               "alias":"taylor has been viewed",
               "viewType":"ImageView",
               "indexWithinActivity":2,
               "tag":null
            },
            {  
               "id":"undefined",
               "type":"click",
               "alias":"nothing happens",
               "viewType":"Button",
               "indexWithinActivity":1,
               "tag":null
            },
            {  
               "id":"testbtn",
               "type":"click",
               "alias":"Back to home",
               "viewType":"Button",
               "indexWithinActivity":0,
               "chainLinks":[  
                  {  
                     "id":"undefined",
                     "viewType":"Button",
                     "indexWithinActivity":1,
                     "chainNumber":1
                  },
                  {  
                     "id":"taylor",
                     "viewType":"ImageView",
                     "indexWithinActivity":2,
                     "chainNumber":2
                  }
               ],
               "tag":null
            }
         ]
      }
   ],
   "info":{  
      "app":"o2mc.io.dimmldependency.dimmldependency"
   }
}
```

# O2mc Android Tracker

## Useage
Useage instructions in combination with the datastreams-manager are available at the private application repository.

## React-Native bindings
This version of the library is pushed on NPM because of it's React-native bindings to the app-tagging part of the android module.

(Excuse me for the package-naming convention, this is something that should be changed in the near future ;))
###Useage in React native
Edit `android/settings.gradle` to declare the project directory:

```
include ':O2MCTracker', ':app'
project(':O2MCTracker').projectDir = new File(rootProject.projectDir, '../node_modules/o2mc-android-tracker/android')
```

Edit `android/app/build.gradle` to declare the project dependency:
```
dependencies {
  ...
compile project(':O2MCTracker')
}
```

Edit `android/app/src/main/java/.../MainActivity.java` to register the native module:

```
...
import o2mc.io.dimmldependency.ReactBindings.ReactTrackerPackage; // <-- New
...

public class MainActivity extends ReactActivity {
  ...
  @Override
  protected List<ReactPackage> getPackages() {
    return Arrays.<ReactPackage>asList(
        new MainReactPackage(),
        new new ReactTrackerPackage(<AppName>,<DataEndPoint>,<DispatchInterval>)() // <-- New
    );
  }
```

## React native bindings (Public API)

**Available** react methods  
**All events automatically get a timestamp assigned**  
**The system automatically gathers general phone data.** (Screensize, Android OS version, Phone type)

**Send single string to endpoint**  
```Void Track(String eventName)```  

**Send key value pair to endpoint (make sure second argument is a string and not a javascript object (Use JSON.stringify())**
```Void trackWithproperties(String eventName, String propertiesAsJson)```  

**Give the user an alias, can be used to match an anonymous user.**  
```Void createAlias(String alias)```  

**Couple an identifier to a user. (Only set once. for example when an user authenticates) When not set the system automatically assigns an uuid at initialisation).**  
```Void identify(String alias)```  

**Start an eventtimer coupled to an eventName.**  
```Void timeEventStart(String eventName)```  

**Stop an eventtimer coupled to eventname. Must be the same as the eventName used at timeEventStart**
```Void timeEventStop(String eventName)```  

**Start an eventtimer coupled to an eventName.**  
```Void timeEventStartWithProps(String eventName, String propertiesAsJson)```  

**Stop an eventtimer coupled to eventname. Must be the same as the eventName used at timeEventStart**
```Void timeEventStopWithProps(String eventName, String propertiesAsJson)```  

**Reset the datafunnel. Removes all gathered data from memory. (Be carefull using this method. it might remove unsent data)**
```Void reset()```  

**Set an endpoint for the data funnel. (Http url)**  
```Void setEndpoint(String endpoint)```  

**Sets the dispatchinterval**  
```Void setDispatchInterval(int interval)```









