Usabilla for Apps allows you to collect feedback from your users with great ease and flexibility.
- Android API: Minimum 19 - Target 30
- Use of AndroidX support libraries
- Use of TLS1.2 protocol for network connections (automatically enabled for Android API >= 21)
- Project targeting Java8 in the
build.gradle
compileOptions
If your app supports Android API 19, in order to enable TLS1.2 on those devices you need to update your security provider as follows
Include the following dependency in your build.gradle
file
implementation `com.google.android.gms:play-services-safetynet:17.0.0`
Call the following function in your app before any action with the Usabilla SDK
fun upgradeSecurityProvider(context: Context) {
ProviderInstaller.installIfNeededAsync(context, object : ProviderInstallListener {
override fun onProviderInstalled() {
// You are good to go
}
override fun onProviderInstallFailed(errorCode: Int, recoveryIntent: Intent) {
// Something went wrong. For more details please refer to https://developer.android.com/training/articles/security-gms-provider
}
})
}
Our SDK targets Java8 and uses components available from Android API 26, therefore if your app targets previous Android versions please do enable desugaring support as explained in the official Google guidelines
The following functionalities will only be available on phones running Android API >= 21
- The cursor on text fields is tinted with the accent color
- The progress bar at the top of the form is tinted with the accent color
- The star component allow custom drawables to be applied to it
Grab the latest version using
implementation 'com.usabilla.sdk:ubform:8.0.2'
If you have obfuscation enabled (ProGuard/R8) and you use a version of our SDK <= 6.4.0 you need to add this line to your obfuscation configuration
-keep public class com.usabilla.sdk.ubform.eventengine.TargetingOptionsModel
Custom variables are represented by a non mutable Map of objects and are attached to each feedback sent
Usabilla.customVariables = mapOf(
Pair("tier", "premium"),
Pair("loggedIn", true)
)
There are a few limitations to the kind of objects you can add to the custom variables
- Custom objects need to override the
toString()
function. - Arrays are not allowed.
- The name (key) of each custom variable must not be
blank
and it must not contain.
or$
.
String
.
Local logging (disabled by default) can be enabled using
Usabilla.debugEnabled = true
It is possible to hide the default navigation buttons our forms use and provide your own (e.g. in the Toolbar).
To do so a couple of steps are required:
-
Set the standard navigation buttons invisible
Usabilla.setDefaultNavigationButtonsVisibility(false)
-
From your custom trigger call the function
navigationButtonPushed
on the form fragment to continue.myButton.setOnClickListener { formFragment.navigationButtonPushed() }
Telemetry data submission (enabled by default) can be disabled using
Usabilla.submitTelemetryData = false
A custom theme can be applied to both passive forms and campaign forms using
Usabilla.theme = UsabillaTheme(UbFonts(), UbImages())
The first function called on the Usabilla SDK should be initialize
.
Failure to call initialize
before any other public function in the SDK can cause erratic behaviour.
Usabilla.initialize(context: Context, appId: String?, httpClient: UsabillaHttpClient?, callback: UsabillaReadyCallback?)
Passive feedback form are loaded using
Usabilla.loadFeedbackForm(formId: String, screenshot: Bitmap?, theme: UsabillaTheme? , callback: UsabillaFormCallback?)
Preloading the form will fetch and store it locally. It can be then shown by calling loadFeedbackForm with the preloaded formId.
Usabilla.preloadFeedbackForms(formIds: List<String>)
Preloaded forms can be removed from cache using
Usabilla.removeCachedForms()
We offer two utility functions to capture a screenshot that can then be added to the passive form load request
val myScreenshot = Usabilla.takeScreenshot(view: View)
val myScreenshot = Usabilla.takeScreenshot(activity: Activity)
Campaigns are triggered by events sent using
Usabilla.sendEvent(context: Context, event: String)
To show campaigns it's necessary to provide a reference to the FragmentManager using
Usabilla.updateFragmentManager(fragmentManager: FragmentManager)
requireActivity().supportFragmentManager
.
Campaign data stored locally can be removed (and fetched again, effectively losing any trace whether they already triggered or not) using
Usabilla.resetCampaignData(context: Context, callback: UsabillaReadyCallback?)
Forms showing on screen can be programmatically dismissed using
Usabilla.dismiss(context: Context)
Campaigns are dismissed directly by the SDK, whereas passive forms assume the proper broadcast receiver is implemented.
PII (Personally Identifiable Information) present in all input text fields can be masked (on submission) using
Usabilla.setDataMasking(masks: List<String>, maskCharacter: Char)
Setting whether the footer logo is clickable or not can be done using
Usabilla.setFooterLogoClickable(clickable: Boolean)
Our forms (campaign banner included) intercepts the phone's back button clicks and remove themselves from the screen
You can optionally register receivers with any of the following filters
private val uiScope = CoroutineScope(Dispatchers.Main) // you can use your scope of component
private fun collectClosingFormSharedFlow() {
uiScope.launch {
Usabilla.sharedFlowClosingForm.collectLatest {
if (it.formType == com.usabilla.sdk.ubform.sdk.form.FormType.PASSIVE_FEEDBACK) {
// The passive feedback form needs to be closed and the feedback result is returned
val res: FeedbackResult = it.feedbackResult
} else if (it.formType == com.usabilla.sdk.ubform.sdk.form.FormType.CAMPAIGN) {
// The campaign feedback form has been closed and the feedback result is returned
val res: FeedbackResult = it.feedbackResult
}
}
}
}
override fun onStart() {
super.onStart()
collectClosingFormSharedFlow()
}
override fun onStop() {
super.onStop()
uiScope.coroutineContext.cancelChildren()
}
private final Observer<ClosingFormData> closingObserver = closingFormData -> {
if (closingFormData.getFormType().equals(FormType.PASSIVE_FEEDBACK)) {
// The passive feedback form needs to be closed and the feedback result is returned
FeedbackResult feedbackResult = closingFormData.getFeedbackResult();
} else if (closingFormData.getFormType().equals(FormType.CAMPAIGN)) {
// The campaign feedback form has been closed and the feedback result is returned
FeedbackResult feedbackResult = closingFormData.getFeedbackResult();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Usabilla.INSTANCE.getClosingData().observe(this, closingObserver);
}
Additionally, in order to receive user entries when a form or campaign has been closed, you need to implement another collector :
private fun collectEntriesSharedFlow() {
scope.launch {
Usabilla.sharedFlowEntries.collectLatest {
// The campaign or feedback form has been closed
// `entries` is the `string` implementation of `fieldId` -> `fieldValue` map
val entries : String = it
}
}
}
override fun onStart() {
super.onStart()
collectEntriesSharedFlow()
}
override fun onStop() {
super.onStop()
uiScope.coroutineContext.cancelChildren()
}
private final Observer<String> entriesObserver = _entries -> {
String entries = _entries
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Usabilla.INSTANCE.getEntriesData().observe(this, entriesObserver);
}
To apply any logic before presenting any campaign, you must construct a collector that listens for the campaign's before show event. You will be notified of this event prior to displaying the campaign.
Inside the collectLatest
method it's possible to obtain a FormType
object.
uiScope.launch {
Usabilla.sharedFlowBeforeShowCampaign.collectLatest {
if (it == FormType.CAMPAIGN_BEFORE_SHOW) {
// Campaign will be displayed.
}
}
}
private final Observer<FormType> beforeShowCampaignObserver = formType -> {
if (formType.equals(FormType.CAMPAIGN_BEFORE_SHOW)) {
// Campaign will be displayed.
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Usabilla.INSTANCE.getBeforeShowCampaign().observe(this, beforeShowCampaignObserver);
}
When a form is given a 4 or 5 rating in the mood/star component, once closed the official In-App review API prompt to rate the app on the PlayStore will be presented.
This will show only on devices that have the Play Store installed.
For information regarding testing/checking this feature, please refer to the official guidelines.
We enable the injection of a custom http client in the initialize
function to handle all the network connections in our SDK.
A sample http client implementation can be seen in the classes CustomHttpClient.java
or CustomHttpClient.kt
The SDK collects diagnostic data to improve the performance and optimise usage on the variety of devices it can be run on.
Adding the SDK to your project will not send us any information; information is sent only in the following cases:
- Call to
initialise()
completes - Call to
loadFeedbackForm()
completes - Call to
sendEvent()
completes - Exiting a feedback form
The data collected content is as follows:
{
"appVersion": "1.0.0",
"appName": "AppName",
"device": "Android SDK built for x86",
"freeMemory": "831172",
"freeSpace": "582800",
"orientation": "Portrait",
"osVersion": "8.1.0",
"reachability": "WiFi",
"rooted": false,
"screenSize": "1440x2392",
"sdkVersion": "8.0.2",
"system": "android",
"totalMemory": "1530604",
"totalSpace": "793488",
"id": "ms since Unix Epoch",
"timestamp": "human readable date",
"originClass": "com.usabilla.Usabilla",
"action": {
"duration": "102",
"errorCode": "0",
"errorMessage": "",
"name": "function or property invoked on our public interface"
...
function parameters are also collected here
...
}
}
To provide your own translation of some of the strings our SDK uses just overwrite them in your strings.xml
file
<string name="ub_field_error">Please check this field</string>
<string name="ub_element_screenshot_title">Screenshot (optional)</string>
<string name="ub_element_screenshot_message">Add an image</string>
<string name="ub_button_close_default">Close</string>
<string name="ub_button_continue_default">Continue</string>
<string name="ub_button_playStore_default">Rate on the play store</string>
<string name="ub_button_submit_default">Submit</string>
<string name="ub_dialog_playStore_title">Rate</string>
<string name="ub_dialog_playStore_message">Thank you for your feedback! Would you like to leave a review?</string>
<string name="ub_dialog_playStore_negative">No, thanks</string>
<string name="ub_dialog_playStore_positive">Rate now</string>
<string name="ub_sdk_permission_disabled_label">Permission disabled!\nEnable it from Settings -> app info</string>
// Accessibility labels
<string name="ub_element_mood_select_rating">Select a rating out of %1$d</string>
<string name="ub_element_mood_adjust_instructions">Swipe up or swipe down to adjust</string>
<string name="ub_element_slider_select_rating">Select a rating from %1$d (%2$s) to %3$d (%4$s)</string>
<string name="ub_element_required">This field is required</string>
<string name="ub_element_screenshot_delete">Delete Screenshot</string>
<string name="ub_element_screenshot_edit">Change Screenshot</string>
<string name="ub_element_mood_hate">I really don\'t like it!</string>
<string name="ub_element_mood_dislike">I don\'t like it</string>
<string name="ub_element_mood_neutral">I feel neutral</string>
<string name="ub_element_mood_like">I like it!</string>
<string name="ub_element_mood_love">I love it!</string>
<string name="ub_element_mood_one_star">One star</string>
<string name="ub_element_mood_two_star">Two stars</string>
<string name="ub_element_mood_three_star">Three stars</string>
<string name="ub_element_mood_four_star">Four stars</string>
<string name="ub_element_mood_five_star">Five stars</string>
<string name="ub_usabilla_logo">Powered by Usabilla</string>
<string name="ub_take_picture">Take Picture</string>
<string name="ub_screenshot_preview">Screenshot Preview</string>
<string name="ub_menu_add">Add</string>
<string name="ub_menu_undo">Undo</string>
<string name="ub_menu_done">Done</string>
<string name="ub_camera_access_denied">No access to camera</string>
<string name="ub_camera_access_denied_details">Allowing access lets you take photos to add to your feedback.</string>
<string name="ub_camera_access_allow">Allow access to camera</string>
<string name="ub_edit_title">Edit</string>
For Android TalkBack to work properly with our passive feedback form you have to make sure that the Activity
(or Fragment
) holding it is not considered for TalkBack.
This can be achieved using
view.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
where view
is the ViewGroup
you want to exclude from the TalkBack.
When the form is dismissed then you can set it back using
view.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
There might be some display issues in devices with a notch, especially if no action bar theme is used.
This can be handled for devices running Android 9 (API Level 28) and above following google guidelines.
If you need help, want to report an issue, or have a question please reach out to the support team via our Help Center or email [email protected]