Search…
Cortex Android
About the SDK Welcome to the Cortex Android SDK for Medable. This framework provides a wrapper for the Medable API as well as several helper tools to directly integrate those features to your Android application.
For basic integration and initialization steps, check the README.md file in the SDK repository.
Examples Check out the Code Samples for a few examples on how to get some specific things done with the Medable SDK.
The Cortex SDK makes it easy to work with your custom object in your mobile app. Below are examples on how to do so with the example custom prescription object, c_prescription, created in the Data Model Setup guide.

1. Creating the custom class

First, we need to create a custom class for our new custom prescription object as a subclass of ObjectInstance. ObjectInstance is the base class to model any custom object instance in Cortex.
The prescription class implementation, would look something like the following:JavaKotlin
1
public class Prescription extends ObjectInstance {
2
3
protected Prescription(JsonObject attributes) {
4
super(attributes);
5
}
6
7
Date getDate() {
8
return this.dateValueWithPropertyName("c_date");
9
}
10
11
Number getDispense() {
12
return this.numberValueWithPropertyName("c_dispense");
13
}
14
15
Reference getPatient() {
16
return this.referenceValueWithPropertyName("c_patient");
17
}
18
19
Reference getProvider() {
20
return this.referenceValueWithPropertyName("c_provider");
21
}
22
23
Number getRefills() {
24
return this.numberValueWithPropertyName("c_refills");
25
}
26
27
String getDetails() {
28
return this.stringValueWithPropertyName("c_rx");
29
}
30
31
}
Copied!

2. Registering the new object

Now that we have a new custom class Prescription we want to use for the c_prescription object instances, let's register it by doing the following:JavaKotlin
1
APIClient.sharedInstance().registerContextClass("c_prescription","c_prescriptions", null, Prescription.class);
Copied!
Now, whenever you query routes that return c_prescription instances, Cortex will return instances of your custom class Prescription:JavaKotlin
1
APIClient.sharedInstance().listObjects("c_prescriptions", null, new ObjectsListCallback<ObjectInstance>() {
2
@Override
3
public void call(List<ObjectInstance> results, boolean hasMore, Fault fault) {
4
List<Prescription> prescriptions = (List) results;
5
}
6
});
Copied!
This also works for expanded references to related objects you have registered.
For example, if in another custom object you have a reference property named c_prescription that relates to the c_prescription object and you query that custom object with expand=c_prescription, then the Reference @ expandedObjectReference will also be an instance of Prescription automagically.JavaKotlin
1
APIParameters expandedPrescription = APIParameterFactory.parametersWithExpandPaths(null, "c_prescription");
2
ObjectId identifier = new ObjectId("...");
3
4
APIClient.sharedInstance().getObject("c_someOtherCustomObjectWithAReferenceToAPrescription", identifier, expandedPrescription, new ObjectFaultCallback<ObjectInstance>() {
5
@Override
6
public void call(ObjectInstance object, Fault fault) {
7
Reference referenceToPrescription = object.referenceValueWithPropertyName("c_prescription");
8
Prescription myPrescriptionInstance = (Prescription) referenceToPrescription.getExpandedObjectReference();
9
}
10
});
Copied!

3. Using custom objects with different types

In Cortex, you can create a custom context and subclass it by using types. In this case the object name will be the same for both custom classes, however they will differ in type.
Let's suppose you have two types of prescriptions, both share all the same base properties but each one has additional properties only usable on each type of prescription.
For the simplicity's sake, let's suppose that we have two types of prescriptions, "A" and "B". PrescriptionA has all the properties from Prescription plus prescriptionAProperty, and PrescriptionB has all the properties from Prescription plus prescriptionBProperty...
You'd accomplish this in your app in the following manner:
    1.
    Create a PrescriptionA class that is a subclass of Prescription and add the prescriptionAProperty to the class.
JavaKotlin
1
public class PrescriptionA extends Prescription {
2
3
protected PrescriptionA(JsonObject attributes) {
4
super(attributes);
5
}
6
7
String getPrescriptionAProperty() {
8
return stringValueWithPropertyName("c_prescriptionAProperty");
9
}
10
}
Copied!
    1.
    Create a PrescriptionB class that is a subclass of Prescription and add the prescriptionBProperty to the class.
JavaKotlin
1
public class PrescriptionB extends Prescription {
2
3
protected PrescriptionB(JsonObject attributes) {
4
super(attributes);
5
}
6
7
String getPrescriptionAProperty() {
8
return stringValueWithPropertyName("c_prescriptionBProperty");
9
}
10
}
Copied!
    1.
    Then register both classes using the same context c_prescription while specifying the appropriate type.
JavaKotlin
1
APIClient.sharedInstance().registerContextClass("c_prescription","c_prescriptions", "c_A", PrescriptionA.class);
2
APIClient.sharedInstance().registerContextClass("c_prescription","c_prescriptions", "c_B", PrescriptionB.class);
Copied!
When working with lists, it is often important to page through results. The recommended approaches to working with lists through the API are outlined here.
However, the Cortex SDK makes this quite simple with pagination helpers.
The following examples illustrate how to paginate through a list of c_prescription instances.
For information on how to create custom cortex objects such as the example c_prescription object, see Data Model Setup.
For more information on working with your Cortex Objects, see Cortex Objects.

1. Using the Pagination Helper

This is how an instance of MDPaginationHelper is created and initialized:JavaKotlin
1
// Optionally, custom API parameters (query parameters) can be specified; and the paginator will include those in the queries as well.
2
JsonObject refills = new JsonObject();
3
refills.addProperty("$lt", 5);
4
JsonObject where = new JsonObject();
5
where.add("c_refills", refills);
6
7
//Create the paginator using the
8
ObjectPaginationHelper paginator = new ContextObjectPaginationHelper("c_prescriptions", 5, null, APIParameterFactory.parametersWithWhere(where, null));
9
10
11
paginator.loadNextPage(new ObjectsListCallback<Prescription>() {
12
@Override
13
public void call(List<Prescription> results, boolean hasMore, Fault fault) {
14
//..
15
}
16
});Java
17
Kotlin
18
19
APIClient.sharedInstance().registerContextClass("c_prescription","c_prescriptions", "c_A", PrescriptionA.class);
20
APIClient.sharedInstance().registerContextClass("c_prescription","c_prescriptions", "c_B", PrescriptionB.class);
Copied!

Login

JavaKotlin
1
APIClient.sharedInstance().login("your email here", "your password here", null, false, new ObjectFaultCallback<Account>() {
2
@Override
3
public void call(Account account, Fault fault) {
4
if(account != null) {
5
//logged in
6
}
7
}
8
});
Copied!

Logout

JavaKotlin
1
APIClient.sharedInstance().logout(new FaultCallback() {
2
@Override
3
public void call(Fault fault) {
4
//logged out
5
}
6
});
Copied!
You can listen to login or logout notifications just like this:JavaKotlin
1
NotificationCenter.defaultCenter().registerObserver(this, kMDNotificationUserDidLogin, new BroadcastReceiver() {
2
@Override
3
public void onReceive(Context context, Intent intent) {
4
//your logged in code
5
}
6
});
7
8
NotificationCenter.defaultCenter().registerObserver(this, kMDNotificationUserDidLogout, new BroadcastReceiver() {
9
@Override
10
public void onReceive(Context context, Intent intent) {
11
//your logged out code
12
}
13
});
Copied!

Register User

JavaKotlin
1
AccountInfo info = AccountInfo.Builder.infoType(AccountInfoType.Registration)
2
.firstName("Charles")
3
.lastName("Best")
4
5
.mobile("16505555555")
6
.password("somePassword")
7
.gender(Gender.Male)
8
.dob(Utils.dateFromString("1899-02-27", DateParseFormat.YYYY_MM_DD))
9
.build();
10
11
APIClient.sharedInstance().registerAccount(info, null, null, new ObjectFaultCallback<Account>() {
12
@Override
13
public void call(Account account, Fault fault) {
14
if (account != null) {
15
// Check whether activation is required before authentication
16
boolean requiresActivation = account.getActivationRequired();
17
// Handle the case when requiresActivation == YES here
18
}
19
}
20
});
Copied!

Create Object

Let's suppose we are using a custom Prescription object definition that was created using the Data Model Setup guide.
Now, we want to create instances of those objects using Cortex from the client side.
Create object using jSon + FastBodyPropertyJavaKotlin
1
JsonObject prescription = new JsonObject();
2
3
prescription.addProperty("c_date",Utils.stringFromDate(new Date(), DateParseFormat.YYYY_MM_DD));
4
prescription.addProperty("c_rx", "Prescription details");
5
prescription.addProperty("c_patient", "some patient Id reference");
6
prescription.addProperty("c_provider", "some provider Id reference");
7
prescription.addProperty("c_dispense", 3);
8
prescription.addProperty("c_refills", 3);
9
10
Body prescriptionBody = new Body();
11
prescriptionBody.addProperty(new FastBodyProperty(prescription));
12
13
APIClient.sharedInstance().createObject("c_prescription", prescriptionBody, new ObjectFaultCallback<ObjectInstance>() {
14
@Override
15
public void call(ObjectInstance object, Fault fault) {
16
17
}
18
});
Copied!
Create object using just Body + ObjectDefinitionJavaKotlin
1
ObjectDefinition prescriptionDef = SchemaManager.sharedInstance().getDefinition("c_prescription");
2
PropertyDefinition dateDef = prescriptionDef.propertyWithName("c_date", null);
3
PropertyDefinition rxDef = prescriptionDef.propertyWithName("c_rx", null);
4
PropertyDefinition patientDef = prescriptionDef.propertyWithName("c_patient", null);
5
PropertyDefinition dispenseDef = prescriptionDef.propertyWithName("c_dispense", null);
6
PropertyDefinition prescriptionType = prescriptionDef.propertyWithName("type", null );
7
PropertyDefinition propADef = prescriptionDef.propertyWithName("c_prescriptiona", "c_prescription_a");
8
9
Body prescriptionBody = new Body();
10
prescriptionBody.addProperty(new SimpleBodyProperty(dateDef, Utils.stringFromDate(new Date(), DateParseFormat.YYYY_MM_DD) ));
11
prescriptionBody.addProperty(new SimpleBodyProperty(dateDef, Utils.stringFromDate(new Date(), DateParseFormat.YYYY_MM_DD) ));
12
prescriptionBody.addProperty(new SimpleBodyProperty(rxDef, "some rx test"));
13
prescriptionBody.addProperty(new SimpleBodyProperty(patientDef, "575f58281d0c03a53ccc3ac6"));
14
prescriptionBody.addProperty(new SimpleBodyProperty(dispenseDef, 5));
15
prescriptionBody.addProperty(new SimpleBodyProperty(propADef, "some test string"));
16
prescriptionBody.addProperty(new SimpleBodyProperty(prescriptionType, "c_prescription_a"));
17
18
APIClient.sharedInstance().createObject("c_prescription", prescriptionBody, new ObjectFaultCallback<ObjectInstance>() {
19
@Override
20
public void call(ObjectInstance object, Fault fault) {
21
22
}
23
});
Copied!

Edit Object

Let's suppose we are using a custom Prescription object definition that was created using the Data Model Setup guide.
Now, we want to edit an existing instance of that type of object using Cortex from the client side.JavaKotlin
1
JsonObject propertiesToUpdate = new JsonObject();
2
3
propertiesToUpdate.addProperty("c_dispense",7);
4
propertiesToUpdate.addProperty("c_refills", 9);
5
propertiesToUpdate.addProperty("c_rx", "Updated prescription details");
6
7
Body propertiesToUpdateBody = new Body();
8
propertiesToUpdateBody.addProperty(new FastBodyProperty(propertiesToUpdate));
9
10
APIClient.sharedInstance().updateObject("c_prescription", new ObjectId("5c8a3b1fe8355a01009073b9"), propertiesToUpdateBody, new ObjectFaultCallback<ObjectInstance>() {
11
@Override
12
public void call(ObjectInstance object, Fault fault) {
13
//edited instance
14
}
15
});
Copied!

List Objects

Let's suppose we are using a custom Prescription object definition that was created using the Data Model Setup guide.
Now, we want to list existing instances of that type of object using Cortex from the client side.

📘Note

For paginated listing take a look the Pagination Helpers guide.
JavaKotlin
1
APIClient.sharedInstance().listObjects("c_prescriptions", null, new ObjectsListCallback<ObjectInstance>() {
2
@Override
3
public void call(List<ObjectInstance> results, boolean hasMore, Fault fault) {
4
if (fault != null) {
5
// handle fault
6
}
7
else
8
{
9
// hasMore is true if there are more objects to load --there's a limit in the amount of returned objects.
10
// objects contains the returned object instances
11
}
12
}
13
});
Copied!

📘Note

If you need to use a custom class instead of ObjectInstance and want Cortex to return instances of your class instead, follow the Cortex Objects guide.

Delete Object

JavaKotlin
1
APIClient.sharedInstance().deleteObject("c_prescription", new ObjectId("the object's id"), null, new FaultCallback() {
2
@Override
3
public void call(Fault fault) {
4
if (fault != null)
5
{
6
// handle fault
7
}
8
}
9
});
Copied!

File Download

Files Api can be downloaded using one of two methods.
Using a download path:JavaKotlin
1
String path = APIClient.routeFromComponents("path","to","file");
2
3
APIClient.sharedInstance().downloadFileAtPath(path, new ObjectFaultCallback<InputStream>() {
4
@Override
5
public void call(InputStream stream, Fault fault) {
6
if (fault != null) {
7
//handle fault
8
} else if (object != null) {
9
//process stream
10
}
11
}
12
});
Copied!
Or using a Facet of the File.JavaText
1
Facet aFileFacet = ...;
2
3
//sending a bitmap as data type
4
aFileFacet.getFileData(new DataWithSourceOrFaultCallback<Bitmap>() {
5
@Override
6
public void call(Bitmap data, DataSource source, Fault fault) {
7
//use the bitmap is no fault present
8
}
9
});
Copied!

📘

If you want to play with facets, you can get an object that has a property file, with some upload file in there (for instance property c_image) and you can get the facets from that property file: yourObject.getImage().getFacets()

File Upload

File uploads are a two step process as described in the File Uploads documentation.
    1.
    In the first step, a file property name and the file name is sent to the server. This could happen when creating an object and also when editing an object.
    2.
    In the response of the first step, that is, in the callback of whichever method was used in step 1 --create or update; is when the actual file upload takes place using upload information sent by the Cortex API.
For this sample, let's suppose we are creating an instance of a c_anObject object type that, has a c_image File property. We want to upload an image to the content facet of that File.Objective-CSwiftJavaKotlin
1
// STEP 1 - Create/Update the object and send a file property key/value pairs
2
UIImage *fileToUpload = ...;
3
// Note: fileToUpload can be an instance of NSData too.
4
5
// File values are always key = facet name, value = file name.
6
NSDictionary *filePropValue = @{ @"content": fileToUpload };
7
8
// --- Optinally the mime type can be set:
9
NSDictionary *filePropValue = @{ @"content": @{ @"mime": @"image/png", @"data": fileToUpload }};
10
// ---
11
12
// Create/Update bodies are always key = prop name, value = prop value or the right type.
13
NSDictionary *createBody = @{
14
@"c_aPropName": @"aPropValue",
15
@"c_image": filePropValue,
16
@"c_someOtherPropName": @55
17
};
18
19
[[Medable client]
20
createObjectWithContext:@"c_anObject"
21
body:createBody
22
callback:^(MDObjectInstance * _Nullable instance, MDFault * _Nullable fault)
23
{
24
if (fault)
25
{
26
// handle fault
27
}
28
else if (instance)
29
{
30
// STEP 2 - File Upload - Performed internally by Cortex. Read "Managing File Uploads" to know how to keep track of file uploads. Files are also cached and encrypted locally by Cortex.
31
}
32
}];
Copied!

📘File Uploads

For a deeper management of file uploads take a look at the Managing File Uploads documentation.
The Medable Cortex Android SDK features an upload operations manager, that takes care of the following tasks:
    Keep an up to date list of ongoing upload operations.
    Keep an up to date list of recently successfully completed operations.
    Keep an up to date list of recently failed operations.
    Keep a progress value (percentage expressed in the 0..1 range) for each operation.
    Emit notifications for every change in the manager: - Upload started. - Upload operation progress changed. - Upload completed or failed.

Notifications

When you're going to upload a file, you can subscribe to file uploads notifications in order to know what is happening, and when the job is complete.JavaKotlin
1
NotificationCenter.defaultCenter().registerObserver(this, kMDNotificationUploadOperationStarted, new BroadcastReceiver() {
2
@Override
3
public void onReceive(Context context, Intent intent) {
4
AssetUploader.UploadOperation fileUpload = (AssetUploader.UploadOperation) intent.getSerializableExtra(Constants.kObject);
5
}
6
});
7
8
NotificationCenter.defaultCenter().registerObserver(this, kMDNotificationUploadOperationProgress, new BroadcastReceiver() {
9
@Override
10
public void onReceive(Context context, Intent intent) {
11
float progress = intent.getBundleExtra(Constants.kObject).getFloat(kProgress);
12
String fileName = intent.getBundleExtra(Constants.kObject).getString(kFilename);
13
}
14
});
15
16
NotificationCenter.defaultCenter().registerObserver(this, kMDNotificationUploadOperationEndedSuccessfully, new BroadcastReceiver() {
17
@Override
18
public void onReceive(Context context, Intent intent) {
19
AssetUploader.UploadOperation fileUpload = (AssetUploader.UploadOperation) intent.getSerializableExtra(Constants.kObject);
20
}
21
});
Copied!
Within the notification, depending on the event, two different objects could be sent:
    A Bundle object is delivered inside the Notification.object property if it is notifying about upload progress. You can access progress and file information to get the upload info.
    A UploadOperations object is delivered inside the Notification.object property if it is notifying about the following events:
      A new UploadOperation was added to the queue (started).
      A UploadOperation succeeded.
      A UploadOperation failed.
      A UploadOperation was removed from completed/failed.

State

The state of all upload operations can be queried from the UploadOperations class by checking the the following class methods:Java
1
AssetUploader.sharedInstance().completedUploads();
2
3
AssetUploader.sharedInstance().ongoingUploads();
4
5
AssetUploader.sharedInstance().failedUploads();
Copied!
An ongoing operation is an upload task that is still uploading, i.e. it hasn't failed and it hasn't completed the upload yet.
Recently completed operations are short lived. After completion, the operation objects will stay in this list for at least 10 seconds. After this time, they'll be removed from the list.
Recently failed operations live a little longer. After failing, the operation objects will stay in this list for at least 1 minute. Again, after this time, they are be removed from the list.
Each operation is an instance of class UploadOperation. Here are some interesting features they provide:
    Progress: Use the progressNumber attribute to determine the progress percentage (measured in the 0..1 range) of the upload operation.
    File Name: If you are uploading several things at once, the fileName property contains the name of the file being uploaded.

Retry a Failed Operation

Any operation that has failed can be retried, it need not be in the failed operations set, but beware that the upload tokens used will eventually expire.
Here is code to retry an operation currently in the failed set:Java
1
AssetUploader.UploadOperation uploadOperation = AssetUploader.sharedInstance().failedUploads().get(x);
2
AssetUploader.sharedInstance().retryUpload(uploadOperation);
Copied!
Last modified 1mo ago