This tutorial describes how to launch other apps using Android intents and how to share information between other apps and the agent. The following topics are covered:
- Constructing intents with extras
- Starting activities and services and sending broadcasts
- Starting activities for result
- Sending information to and receiving information from custom apps
- File sharing
Starting Activities with Intents
Let's take a look at a typical start activity example, taken from Android Common Intents. The script launches the dialer or phone app on the device, with the phone number 212-555-1212 entered:
const ACTION_DIAL = "android.intent.action.DIAL";
const URI_DIAL_NUMBER = "tel:2125551212";
var intent = mobicontrol.android.createIntent()
.withAction(ACTION_DIAL)
.withData(URI_DIAL_NUMBER);
mobicontrol.android.startActivity(intent);
The code above can be split into the following three steps:
- Create an empty intent with mobicontrol.android.createIntent
- Augment the intent with desired properties using various intent builder functions like mobicontrol.android.Intent.withAction and mobicontrol.android.Intent.withComponent.
- Launch activity with mobicontrol.android.startActivity.
Intent Extras
When extra properties need to be specified, use mobicontrol.android.Bundle. Like Intent
, Bundle
provides builder functions which allow construction of an empty Bundle
and populating it with specific properties.
The following script demonstrates how to open the Calendar app, adding a "John Doe Vacation" all-day event on October 20, 2025:
const startTime = new Date("2025-10-20T11:00:00").getTime();
var extras = mobicontrol.android.createBundle().
withString("title", "John Doe Vacation").
withBoolean("allDay", true);
var intent = mobicontrol.android.createIntent().
withData("content://com.android.calendar/events").
withAction("android.intent.action.INSERT").
withExtras(extras);
mobicontrol.android.startActivity(intent);
Sending Broadcasts and Starting Services with Intents
Intents can also be used to send broadcasts with mobicontrol.android.sendBroadcast and to start services with mobicontrol.android.startService.
For example, OEMs often use broadcasts to implement device management features. The following script reboots a Philips device:
var intent = mobicontrol.android.createIntent()
.withAction("php.intent.action.REBOOT");
mobicontrol.android.sendBroadcast(intent);
Starting an Activity for a Result
In some cases launched activities can send back a result, and our script might want to wait until such an activity is finished, so it can act
on the result. This functionality can be achieved with mobicontrol.android.startActivityForResult.
For example, the following script opens a file picker and lets the user select any image file. When the selection is done, the
callback function is triggered with result.intent.data
holding information about the picked file.
var intent = mobicontrol.android.createIntent()
.withAction("android.intent.action.GET_CONTENT")
.withType("image/*");
mobicontrol.android.startActivityForResult(intent, onFilePicked);
function onFilePicked(result) {
if (result.isSuccessful) {
mobicontrol.log.info("The file '" + result.intent.data + "' is picked!");
}
}
One limitation of mobicontrol.android.startActivityForResult is that we can only
wait for the result of one activity at a time. Any new call to startActivityForResult
will cancel a previous call, even if the latter was
done from a different script. This will cause the previous callback to fail with NEW_ACTIVITY_STARTED
error code.
To guarantee against concurrency failures in the same script, time the calls to startActivityForResult
so they don't overlap. The
example below demonstrates this technique: the contact picker intent is launched only after the file picker intent is finished. Note that
we set a timeout for the file picker app, limiting the wait time to 5 minutes.
var filePickerIntent = mobicontrol.android.createIntent()
.withAction("android.intent.action.GET_CONTENT")
.withType("image/*");
mobicontrol.android.startActivityForResult(filePickerIntent, onFilePicked, 5 * 60000);
function onFilePicked(result) {
if (result.isSuccessful) {
mobicontrol.log.info("The file is picked!");
}
selectContact()
}
function selectContact() {
var intent = mobicontrol.android.createIntent()
.withAction("android.intent.action.PICK")
.withType("vnd.android.cursor.dir/contact");
mobicontrol.android.startActivityForResult(intent, onContactPicked);
}
function onContactPicked(result) {
if (result.isSuccessful) {
mobicontrol.log.info("The contact is picked!");
}
}
Sharing Information using Intents
Intents provide a secure mechanism for information sharing (including file sharing) between the agent and other apps, as the administrator controls every aspect of the shared information:
- The app to run
- The information to send
- The information to receive
The following paragraphs provide detailed examples on how to write a custom app which can communicate with the agent.
Sending Data to a Custom App
The following script sends the MobiControl device ID to a custom com.example.app
app:
var extras = mobicontrol.android.createBundle()
.withString("id", mobicontrol.device.id);
var intent = mobicontrol.android.createIntent()
.withComponent("com.example.app/.MainActivity")
.withExtras(extras);
mobicontrol.android.startActivity(intent);
In order to handle this intent, the custom app's MainActivity
needs to implement the onCreate
method:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
val deviceId = getIntent().getStringExtra("id")
// Do something with deviceId
}
}
Multiple properties can be provided to a custom app either by multiple intent extras, or by a single extra containing a serialized JSON object:
var deviceInfo = {
id: mobicontrol.device.id,
agentVersion: mobicontrol.agent.version,
freeStorage: mobicontrol.storage.internal.availableSpace
};
var extras = mobicontrol.android.createBundle()
.withString("deviceInfo", JSON.stringify(deviceInfo));
var intent = mobicontrol.android.createIntent()
.withComponent("com.example.app/.MainActivity")
.withExtras(extras);
mobicontrol.android.startActivity(intent);
You can then use the Android JSON parser in the custom app's MainActivity
to extract each property:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
val jObject = JSONObject(getIntent().getStringExtra("deviceInfo"))
val deviceId = jObject.getString("id")
// Do something with deviceId
}
}
The following example demonstrates how to send custom attributes to a custom app. The custom attributes can be used later to configure the app:
const APP_PREFIX = "EXAMPLE_";
var exampleConfiguration = {};
var customAttributeKeys = [ "location", "manager", "phone_number" ];
customAttributeKeys.forEach(attribute =>
exampleConfiguration[attribute] = mobicontrol.agent.getCustomAttribute(APP_PREFIX + attribute));
var extras = mobicontrol.android.createBundle()
.withString("configuration", JSON.stringify(exampleConfiguration));
var intent = mobicontrol.android.createIntent()
.withComponent("com.example.app/.MainActivity")
.withExtras(extras);
mobicontrol.android.startActivity(intent);
Here is how the custom app's MainActivity
can read the value of the custom attribute corresponding to the "EXAMPLE_location"
key:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
val jObject = JSONObject(getIntent().getStringExtra("configuration"))
val location = jObject.getString("location")
// Configure the app with location
}
}
Receiving Data from a Custom App
In order to receive information from a custom app, the script needs to call
mobicontrol.android.startActivityForResult.
The callback function can then process the data from the app.
The following example shows how to populate
custom data
with itemsInStock
and lastStockUpdateDate
values from a custom app:
var intent = mobicontrol.android.createIntent()
.withComponent("com.example.app/.MainActivity")
.withAction("com.example.app.GET_STATUS");
mobicontrol.android.startActivityForResult(intent, receiveStatus);
function receiveStatus(result) {
if (result.isSuccessful) {
var jsonFile = new mobicontrol.io.File(mobicontrol.storage.internal.dataDirectory + '/status.json');
var status = JSON.parse(jsonFile.readText());
status.itemsInStock = result.intent.extras.getInteger("itemsInStock");
status.lastStockUpdateDate = result.intent.extras.getString("lastStockUpdateDate");
jsonFile.writeText(JSON.stringify(status));
}
}
The corresponding custom app implementation will look like the following:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
if (getIntent().action == "com.example.app.GET_STATUS") {
val result = Intent().apply {
putExtra("itemsInStock", getItemsInStock())
putExtra("lastStockUpdateDate", getLastStockUpdateDate())
}
setResult(RESULT_OK, result)
}
finish()
}
}
Note that the example above assumes the custom data
is configured with JSON build type and two custom definitions (itemsInStock
and lastStockUpdateDate
) of corresponding types.
File Sharing
Files can be securely shared between the agent and other apps using content:
URIs.
To get a content:
URI from a file, use mobicontrol.android.createFileContentUri.
The snippet below shows how to pass a file into the camera app. The app will store a captured image in that file:
var file = new mobicontrol.io.File(mobicontrol.storage.internal.dataDirectory + "/target.jpg");
var fileUri = mobicontrol.android.createFileContentUri(file)
var extras = mobicontrol.android.createBundle().withUri("output", fileUri);
var intent = mobicontrol.android.createIntent().
withAction("android.media.action.IMAGE_CAPTURE").
withExtras(extras);
mobicontrol.android.startActivity(intent);
Reading from a File Content URI
mobicontrol.io.File.writeContent can be used to read from a content:
URI into a file. The
following code copies an image file picked by the user into the agent's sandbox. Note that result.intent.data
returned by the file
picker is a content:
URI, which is guaranteed by CATEGORY_OPENABLE
. See
Common intents documentation.
var intent = mobicontrol.android.createIntent()
.withAction("android.intent.action.GET_CONTENT")
.withCategories([ "android.intent.category.OPENABLE" ])
.withType("image/*");
mobicontrol.android.startActivityForResult(intent, onFilePicked);
function onFilePicked(result) {
if (result.isSuccessful) {
var newFile = new mobicontrol.io.File(mobicontrol.storage.internal.dataDirectory + "/picked_image.jpg");
newFile.writeContent(result.intent.data);
}
}
Handling File Content URIs in a Custom App
Here is how a custom app can read from a content:
URI and write its content into the file FILE_PATH
:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
getIntent().data?.let { uri ->
contentResolver.openInputStream(uri).use { input ->
File(FILE_PATH).outputStream().use { output ->
input?.copyTo(output)
}
}
}
}
}
If your custom app needs to create a content:
URI from a file FILE_PATH
, and pass it to the agent, use the following technique:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
if (getIntent().action == "com.example.app.GET_FILE") {
val uri = FileProvider.getUriForFile(this, packageName, File(FILE_PATH))
val result = Intent().apply {
data = uri
}
setResult(RESULT_OK, result)
}
finish()
}
}
Note that in order for FILE_PATH
to be sharable, it needs to reside in a sharable folder configured in the app's AndroidManifest.xml
,
as clarified here.