Overview¶
There are two types of settings: predefined policies and custom settings. These settings are handled by the ClientPolicyService
and CustomSettingsService
services.
Predefined Client Policies¶
The following model classes are defined for predefined client policies in the policies
package:
PasscodePolicy
UsagePolicy
LogPolicy
NetworkPolicy
FeatureRestrictionPolicy
BlockWipingPolicy
ClientPolicies
to hold references to all above policies.
Loading Client Policies¶
As of Android SDK 3.2, the following APIs are defined to load all client policies:
suspend fun retrieveClientPolicies(
target: SettingsTarget = SettingsTarget.DEVICE
): ServiceResult<ClientPolicies> = withContext(IO) { ... }
fun retrieveClientPolicies(
target: SettingsTarget = SettingsTarget.DEVICE,
listener: ServiceListener<ClientPolicies>
) { ... }
The first API can be used in Kotlin client code. It runs on the IO
thread and returns the ClientPolicies
in ServiceResult<ClientPolicies>
. The second function can be used in the Java client. The actual API call also runs on the IO
thread but the result will be notified with ServiceListener<ClientPolicies>
on the UI thread.
findViewById(R.id.button).setOnClickListener(v -> new ClientPolicyService()
.retrieveClientPolicies(SettingsTarget.DEVICE, serviceResult -> {
if (serviceResult instanceof ServiceResult.SUCCESS) {
ClientPolicies polices =
(((ServiceResult.SUCCESS<ClientPolicies>) serviceResult).getData());
...
} else {
Log.e(
"TEST",
((ServiceResult.FAILURE<ClientPolicies>) serviceResult).getMessage()
);
}
}));
when (ClientPolicyService().retrieveClientPolicies(SettingsTarget.USER)) {
is ServiceResult.SUCCESS -> {
assertNotNull(result.data)
val passcodePolicy = result.data?.passcodePolicy
assertNotNull(passcodePolicy)
assertEquals(5, passcodePolicy!!.minLength)
}
is ServiceResult.FAILURE -> {
assertTrue(false)
}
}
Client policies may be updated on the server side. ClientPolicyService
can be configured to load the latest updates when the user is authenticated after the application restarts or is brought to the foreground after a passcode timeout period. To do this, the target
and autoRetrieveListener
parameters in the constructor should be provided, and then start the service with SDKInitializer
.
List<MobileService> services = new ArrayList<>();
ClientPolicyService clientPolicyService = new ClientPolicyService(
SettingsTarget.USER, serviceResult -> {
}
);
services.add(clientPolicyService);
SDKInitializer.INSTANCE.start(this, services.toArray(new MobileService[0]), null);
val services = mutableListOf<MobileService>()
val clientPolicyService =
ClientPolicyService(SettingsTarget.USER, object : ServiceListener<ClientPolicies> {
override fun onServiceDone(result: ServiceResult<ClientPolicies>) {
when (result) {
is ServiceResult.SUCCESS -> Log.d(
"TEST",
"Client policies: ${result.data?.toString()}"
)
is ServiceResult.FAILURE -> Log.e("TEST", "Error: ${result.message}")
}
}
})
services.add(clientPolicyService)
SDKInitializer.start(this, * services.toTypedArray())
Storing Client Policies¶
To store settings, call the updatePolicy
method. You can use the store method to create new values or update existing values. The APIs are defined as:
suspend fun <T : AbstractSettingsEntity> updatePolicy(
policy: T,
target: SettingsTarget = SettingsTarget.DEVICE
): ServiceResult<T> = withContext(IO) { ... }
fun <T : AbstractSettingsEntity> updatePolicy(
policy: T,
target: SettingsTarget = SettingsTarget.DEVICE,
listener: ServiceListener<T>
) { ... }
For Kotlin client code, the first API can be used. It runs on the IO
thread and returns the result with ServiceResult
. The Java client can call the second function and the result will be notified with ServiceListener
on the UI thread.
As of Android SDK 3.2, updatePolicy
does not require the keyPath
any more because each policy is internally aware of the value.
PasscodePolicy passcodePolicy = ...;
new ClientPolicyService().updatePolicy(passcodePolicy, SettingsTarget.DEVICE, serviceResult -> {
if( serviceResult instanceof ServiceResult.SUCCESS) {
...
} else {
...
}
});
val passcodePolicy = ...
val passcodePolicyToUpdate = passcodePolicy.copy(minLength = 8)
ClientPolicyService().updatePolicy(
passcodePolicyToUpdate,
SettingsTarget.USER,
object : ServiceListener<PasscodePolicy> {
override fun onServiceDone(result: ServiceResult<PasscodePolicy>) {
assertNotNull(result)
assertTrue(result is ServiceResult.SUCCESS)
assertTrue((result as ServiceResult.SUCCESS).data!!.minLength == 8)
}
})
Deleting Client Policies¶
As of Android SDK 3.2, ClientPolicyService
does not expose deletion APIs. CustomSettingsService
will provide APIs to delete custom settings.
Custom Settings¶
As of Android SDK 3.2, CustomSettingsService
is provided to handle custom settings. To handle custom settings, CustomSettingsEntity
should be extended:
abstract class CustomSettingsEntity(private val configurationName: String) {
abstract fun toJsonString(): String
}
configurationName
will be used as the keyPath
as before.
Loading Custom Settings¶
suspend fun <T : CustomSettingsEntity> loadCustomSettings(
target: SettingsTarget = SettingsTarget.DEVICE,
keyPath: String,
parse: (String) -> T
): ServiceResult<T> = withContext(IO) { ... }
fun <T : CustomSettingsEntity> loadCustomSettings(
target: SettingsTarget = SettingsTarget.DEVICE,
keyPath: String,
parser: (String) -> T,
listener: ServiceListener<T>
) { ... }
Like ClientPolicyService
, there are also two APIs to load custom settings. If client code holds an instance of CustomSettingsEntity
, getKeyPath()
will return the keyPath
, otherwise, configurationName
can be provided as the keyPath
for this API. parser
converts the JSON string returned from the mobile server to an instance of CustomSettingsEntity
.
Like predefined client policies, custom settings can also be loaded automatically when a user is authenticated or the mobile app is brought to the foreground. To do this, a little more information will be required when creating the instance of CustomSettingsService
.
class CustomSettingsService<out R : CustomSettingsEntity>(
private val target: SettingsTarget = SettingsTarget.USER,
private val keyPath: String? = null,
private val parse: ((String) -> R?)? = null,
private val autoCustomSettingsLoadListener: ServiceListener<R>? = null
) : MobileService() { ... }
From the constructor above, keyPath
, parse
, and autoCustomSettingsLoadListener
will all be required. After creating the instance, start it with SDKInitializer
.
List<MobileService> services = new ArrayList<>();
CustomSettingsService customSettingsService = new CustomSettingsService(
SettingsTarget.USER,
"key_path_to_custom_setting",
jsonString -> new CustomUserEntity("key_path_to_custom_setting"),
serviceResult -> {
}
);
services.add(customSettingsService);
SDKInitializer.INSTANCE.start(this, services.toArray(new MobileService[0]), null);
val customSetting = CustomSettingExample()
val customSettingService = CustomSettingsService(
target = SettingsTarget.USER,
keyPath = customSetting.getKeyPath(),
parse = { jsonString -> CustomSettingExample.createFromJsonString(jsonString) },
autoCustomSettingsLoadListener = object : ServiceListener<CustomSettingExample> {
override fun onServiceDone(result: ServiceResult<CustomSettingExample>) {
when (result) {
is ServiceResult.SUCCESS -> {
Log.d("TEST", result.data?.toString()?: "")
}
is ServiceResult.FAILURE -> {
Log.e("TEST", "Error: ${result.message}")
}
}
}
}
)
services.add(customSettingService)
SDKInitializer.start(this, * services.toTypedArray())
Note
Since SDKInitializer
can only initialize one instance of CustomSettingsService
, design the CustomSettingsEntity
carefully when using this feature.
Storing Custom Settings¶
suspend fun <T : CustomSettingsEntity> storeCustomPolicy(
entity: T,
target: SettingsTarget = SettingsTarget.DEVICE
): ServiceResult<T> { ... }
fun <T : CustomSettingsEntity> storeCustomPolicy(
entity: T,
target: SettingsTarget = SettingsTarget.DEVICE,
listener: ServiceListener<T>
) { ... }
For 'update', only the CustomSettingsEntity
instance is required. Both keyPath
and the JSON string can be obtained from the instance. The suspend
function will run on the IO
thread. The other function will notify the result with ServiceListener
on the UI thread.
Deleting Custom Settings¶
suspend fun deleteCustomPolicy(
keyPath: String,
target: SettingsTarget = SettingsTarget.DEVICE
): ServiceResult<Nothing> { ... }
fun deleteCustomPolicy(
keyPath: String,
target: SettingsTarget = SettingsTarget.DEVICE,
listener: ServiceListener<Nothing>
) { ... }
The suspend
function will run on the IO
thread. The other function will notify the result with ServiceListener
on the UI thread.
Permissions¶
Note that the access to settings (read/write/delete) is controlled by the user role permissions set by your mobile services administrator. See Storage Service API for more information.