Context Sensing SDK Tutorial for Windows*
This tutorial guides you through the process of creating a simple C# application using Intel® Context Sensing SDK for Windows* in order to take advantage of the Context Sensing APIs. We will see some of the basic operations needed to start using Context (such as starting the sensing component, requesting updates for a particular activity, and how to stop sensing). Additionally, we will look at how to sync states with the Context Cloud.
For this example, let us use a simple use case: A console application that detects when the user changes focus to a different application and prints a status message. It will continue to run until the user presses the "cancel" keystroke: CTRL-C.
Sections
- Importing the Context Sensing SDK components
- Starting the Sensing component, and listening for events
- Removing listeners and stopping the Sensing component
- Signing in to the Context Cloud and pushing states
Importing the Context Sensing SDK Components
In order to utilize the referenced Context SDK for Windows components, you must add using directives in the .cs file. For this example, you need to add all of the following:
Observe that the above using directives mostly refer to Context SDK for Windows components. In addition to these, we require System.Threading in order to use a ManualResetEvent to wait until the user enters CTRL-C before exiting.
Starting the Sensing component and listening for events
- Prior to receiving any events, we must construct a new instance of the Sensing class and start sensing. The Sensing class is an application's primary entry point into the Context SDK components. Sensing must be started before registering to receive any events. Sensing must be stopped after the application is finished using Context SDK components.
- Construct an n instance of IContextListener. This is the interface from which all receivers of context information must derive. The listener is provided to the instance of Sensing, and its onSuccess method is invoked each time Context SDK has new information. Before implementing an IContextListener, however, let's briefly discuss the information that Context SDK will be providing to our listener.
- Add the routine to print out the change in focused application to the console as well, so we will need to implement the onSuccess method. A couple of notes before we proceed: many of the providers are implemented in a way that they collect their data internally by polling (with an adjustable frequency), so it is possible that the listener's onSuccess method may be triggered when there is no new information to display. For this reason, since we are interested only in changes to the focused application, we will want to store the "current" application name internally to the listener as a string, and before printing an update to the console, check to see if the current application has changed.
- Enable sensing for that particular context type with a default set of options. The code snip below shows the flow as we have discussed so far:
- Typically, the Sensing instance would be held in a class-level or a module-level variable (as one instance should last the entire lifecycle of the application), but due to the simplicity of this application, we will just construct it as a local variable in the main method. Use the following to initialize the instance:
- Regardless of the kind of information requested (what we call the "Context Type"), several things are provided in the data received:
- The Context Type (as a URN). For example, the Context Type URN for the Applications context type is "urn:x-intel:context:type:device:applications".
- The date/time that this information was recorded.
- Regardless of the kind of information requested (what we call the "Context Type"), several things are provided in the data received:
- Typically, the Sensing instance would be held in a class-level or a module-level variable (as one instance should last the entire lifecycle of the application), but due to the simplicity of this application, we will just construct it as a local variable in the main method. Use the following to initialize the instance:
- Register for notifications pertaining to the "Applications" context of the user. This Context Type represents running applications on the user's system, and it has two major components specific to Applications:
- The "Current" application is the application the user is interacting with at the moment. This is the application that has focus.
- Additionally, there is a collection of "Running" applications. These are all of the processes running on the system, and do include the current application.
Information provided about each application includes the name (For example "OUTLOOK") found in the AppName field, and the process ID (For example 14324) found in the PackageName field. An additional field ClassName is currently blank.
Given this background, we can go ahead and implement our listener. Create a new class (this can be in the same file as the Main method) called ApplicationListener, and make it implement the IContextListener interface:
We will start by implementing the onError method. This method is triggered if the specific context provider encounters an error when collecting data. In a real application, you would likely want to log this to a file and take some action to fix the problem, but for this simple example, we'll just print out the error information to the console as a string. So our onError method becomes:- Add a private string variable to the listener to track current app name:
- Update the onSuccess method to print any updates to the console. Then onSuccess method looks like this:
Our listener is complete. Now we need to register this listener with Context SDK to receive updates. As a first step, we need to start sensing. This is done via the Sensing.Start() method. Recall that we created an instance of Sensing in the Main method. Invoke the Start method on this instance. Next, construct the listener we just created and add it as a listener for the Applications Context Type. - Now we can start sensing and receive updates, but we are missing a couple of things:
- If we start sensing, we need to stop sensing.
- The application exits immediately after the code above – we need to wait until the user presses the key combination CTRL-C.
Removing listeners and stopping the Sensing component
Now, let's add the code to remove listeners and stop sensing. This is accomplished through the Sensing methods:
- removeContextTypeListener(), which removes a particular listener so it no longer receives updates
- Stop(), which stops all sensing activities.
One other thing to consider is that we want to stop the sensing service even if some unexpected exception is thrown while processing. Therefore, we should utilize a try/finally block to ensure that the sensing service is stopped in all cases. These changes will yield code like this:
Last thing to do is keep the application from exiting until the user invokes the Cancel keystroke. To do this, we will utilize the .NET construct "ManualResetEvent". See documentation on MSDN for more information about this class (https://msdn.microsoft.com/en-us/library/system.threading.manualresetevent%28v=vs.110%29.aspx)
Essentially, the ManualResetEvent has two methods that we care about: Set and WaitOne. When WaitOne is called, the thread will suspend until Set is called. So, to avoid a "while true" loop, we will call WaitOne() after setting up Context SDK to receive events, then we will call Set() when the user invokes the Cancel keystroke. We will detect user cancellation by adding an event handler to the Console.CancelKeyPress event. The following code snippet includes the full and complete implementation of the Main method, as well as the declaration of the ManualResetEvent:
After all the above steps are carried out, running the application (then clicking around to a few different windows) produces output similar to the following:Figure 1 - Running application
Signing in to the Context Cloud and pushing states
At this point, our application can detect changes in the focused application and display them to the user. This demonstrates how to use Context SDK for Windows in offline mode. Next, we will show how to integrate the Context Cloud into your application.
The Context Cloud allows you to authenticate your user and at the same time, take advantage of the cloud capabilities offered by cloud services, such as the possibility of obtaining context states like the user's semantic place (home/work). You must obtain a Context Cloud API Key, Secret, and Redirect URI by creating a developer account. [Link to account creation page goes here].
- Once you have obtained these, you can authenticate your user with any of the Identification Providers supported by Context Cloud (currently, these providers are Google and Facebook) using OAuth2. The complete process for OAuth2 authentication is not in scope for this guide. We recommend that you consult the documentation available from the identification providers you intend to support in your application.
- Context Cloud API Key
- Context Cloud Secret
- Context Cloud Redirect URI
- Identity Provider name ("google", "facebook")
- Access token from the Identity Provider.
- You must select a cloud scope. The scope you use when authenticating the application determines what resources it is allowed to access. The full list of available scopes are listed on the Context States Datasheet [link to datasheet goes here]. For this example, we will be using the scope: "context:device:applications:running context:post:device:applications:running", which includes permissions to read and push states regarding the current and running applications.
- Once all of these are acquired, you can authenticate with the Context Cloud with the following code snippet:
In order to proceed with this guide, you must obtain the following:
The code sample does the following:
- Acquire the identity provider access token (Please consult documentation from your identity provider of choice for instructions on how to acquire this).
- Construct a ClientCredentials object from the Context Cloud access information (API key, secret, and redirect URI).
- Construct an IdpToken instance from the idpAccessToken and the IdentityProvider (in this example, we are using Google).
- Execute the authorize() method on the CloudManager singleton Instance with the objects you created above and the requested cloud scope.
- After authorization takes place, the CloudManager's isAuthorized() method should return true.
- After authenticating with the Context Cloud, we are able to push states to the cloud with the CloudManager instance's setItem() method. A good place for this is in the onSuccess() method of our listener. The code sample below shows the updated onSucess() method from the listener; we are now pushing to the cloud every time the current application changes:
In our example, we have inserted the above code just before executing the Sensing class's Start() method.
- Once you have obtained these, you can authenticate your user with any of the Identification Providers supported by Context Cloud (currently, these providers are Google and Facebook) using OAuth2. The complete process for OAuth2 authentication is not in scope for this guide. We recommend that you consult the documentation available from the identification providers you intend to support in your application.