Do you want to display a questionnaire to get the opinion of your users? A survey for a medical trial? A series of instructions in a manual-like style?
SurveyKit is an Android library that allows you to create exactly that.
Thematically it is built to provide a feeling of a professional research survey. The library aims to be visually clean, lean and easily configurable. We aim to keep the functionality close to iOS ResearchKit Surveys.
This is an early version and work in progress. Do not hesitate to give feedback, ideas or improvements via an issue.
- What SurveyKit does for you
- What SurveyKit does not (yet) do for you
- Library Setup
- Usage
- Location steps
- Custom steps
- Comparison to ResearchKit on iOS
- Author
- Contributing
- License
- Simplifies the creation of surveys
- Provides rich animations and transitions out of the box (custom animations planned)
- Build with a consistent, lean, simple style, to fit research purposes
- Survey navigation can be linear or based on a decision tree (directed graph)
- Gathers results and provides them in a convinient manner to the developer for further use
- Gives you complete freedom on creating your own questions
- Allows you to customize the style
- Provides an API and structure that is very similar to iOS ResearchKit Surveys
- Is used in production by Quickbird Studios
As stated before, this is an early version and a work in progress. We aim to extend this library until it matches the functionality of the iOS ResearchKit Surveys.
build.gradle
allprojects {
repositories {
jcenter()
}
}
build.gradle.kts
dependencies {
implementation(project("com.quickbirdstudios:surveykit:1.1.0"))
}
Find the latest version or all releases
A working example project can be found HERE
Add the SurveyView to your xml
(it looks best if it fills the screen).
<com.quickbirdstudios.survey_kit.survey.SurveyView
android:id="@+id/survey_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Find the view in the xml
and save it for further use.
var surveyView: SurveyView = view.findViewById(R.id.survey_view)
To create a step, create an instance of one of these 3 classes:
InstructionStep(
title = R.string.intro_title,
text = R.string.intro_text,
buttonText = R.string.intro_start
)
The title
is the general title of the Survey you want to conduct.
The text
is, in this case, the introduction text which should give an introduction, about what the survey is about.
The buttonText
specifies the text of the button, which will start the survey.
All of these properties have to be resource Ids.
CompletionStep(
title = R.string.finish_question_title,
text = R.string.finish_question_text,
buttonText = R.string.finish_question_submit
)
The title
is the general title of the Survey you want to conduct, same as for the InstructionStep
.
The text
is here should be something motivational: that the survey has been completed successfully.
The buttonText
specifies the text of the button, which will end the survey.
All of these properties have to be resource Ids.
QuestionStep(
title = R.string.about_you_question_title,
text = R.string.about_you_question_text,
answerFormat = AnswerFormat.TextAnswerFormat(
multipleLines = true,
maximumLength = 100
)
)
The title
same as for the InstructionStep
and CompletionStep
.
The text
the actual question you want to ask. Depending on the answer type of this, you should set the next property.
The answerFormat
specifies the type of question (the type of answer to the question) you want to ask. Currently there these types supported:
TextAnswerFormat
IntegerAnswerFormat
ScaleAnswerFormat
SingleChoiceAnswerFormat
MultipleChoiceAnswerFormat
LocationAnswerFormat
All that's left is to collect your steps in a list.
val steps = listOf(step1, step2, step3, ...)
Next you need a task. Each survey has exactly one task. A Task
is used to define how the user should navigate through your steps
.
val task = OrderedTask(steps = steps)
The OrderedTask
just presents the questions in order, as they are given.
val task = NavigableOrderedTask(steps = steps)
The NavigableOrderedTask
allows you to specify navigation rules.
There are two types of navigation rules:
With the DirectStepNavigationRule
you say that after this step, another specified step should follow.
task.setNavigationRule(
steps[4].id,
NavigationRule.DirectStepNavigationRule(
destinationStepStepIdentifier = steps[6].id
)
)
With the MultipleDirectionStepNavigationRule
you can specify the next step, depending on the answer of the step.
task.setNavigationRule(
steps[6].id,
NavigationRule.MultipleDirectionStepNavigationRule(
resultToStepIdentifierMapper = { input ->
when (input) {
"Yes" -> steps[7].id
"No" -> steps[0].id
else -> null
}
}
)
)
When the survey is finished, you get a callback. No matter of the FinishReason
, you always get all results gathered until now.
The TaskResult
contains a list of StepResult
s. The StepResult
contains a list of QuestionResult
s.
surveyView.onSurveyFinish = { taskResult: TaskResult, reason: FinishReason ->
if (reason == FinishReason.Completed) {
taskResult.results.forEach { stepResult ->
Log.e("logTag", "answer ${stepResult.results.firstOrNull()}")
}
}
}
These is how you add custom styling to your survey. We'll add even more options in the future.
val configuration = SurveyTheme(
themeColorDark = ContextCompat.getColor(requireContext(), R.color.cyan_dark),
themeColor = ContextCompat.getColor(requireContext(), R.color.cyan_normal),
textColor = ContextCompat.getColor(requireContext(), R.color.cyan_text)
)
All that's left is to start the survey and enjoy.🎉🎊
surveyView.start(task, configuration)
When you cancel the survey, there is an option to change dialog default Strings. Must be imported from resources.
val configuration = SurveyTheme(
themeColorDark = ContextCompat.getColor(this, R.color.cyan_dark),
themeColor = ContextCompat.getColor(this, R.color.cyan_normal),
textColor = ContextCompat.getColor(this, R.color.cyan_text),
abortDialogConfiguration = AbortDialogConfiguration(
title = R.string.title,
message = R.string.message,
neutralMessage = R.string.no,
negativeMessage = R.string.yes
)
)
You need add below to your own application AndroidManifest.xml
file to use Google Map.
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="GOOGLE API KEY" />
Also need to append location permissions on AndroidManifest.xml
. This is not required. But If you gave this permissions, map can select current location automatically.
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
You might wanna run location question steps test or example app on this project. Need to add api keys on local.properties
file.
google_sdk_key="[API_KEY]"
//if you want to use yandex address suggession on example app
yandex_sdk_key="[API_KEY]"
And finally you can instante location question step like below.
QuestionStep(
title = "title",
text = this.resources.getString(R.string.location_question_text),
lifecycle = lifecycle,
answerFormat = AnswerFormat.LocationAnswerFormat(
lifecycle = lifecycle,
//addressProvider = YandexAddressSuggestionProvider(api_key)
)
)
Default address provider is GeocoderAddressSuggestionProvider
based on android.location.Geocoder
.
If you want to use custom address provider. You can use AddressSuggestionProvider
interface and make your own implements like YandexAddressSuggestionProvider
.
At some point, you might wanna define your own custom question steps. That could, for example, be a question which prompts the user to pick color values or even sound samples. These are not implemented yet but you can easily create them yourself:
You'll need a CustomResult
and a CustomStep
. The Result class tells SurveyKit which data you want to save.
@Parcelize
data class CustomResult(
val customData: String,
override val stringIdentifier: String,
override val id: Identifier,
override val startDate: Date,
override var endDate: Date
) : QuestionResult, Parcelable
Next you'll need a CustomStep class:
class CustomStep : Step {
override val isOptional: Boolean = true
override val id: StepIdentifier = StepIdentifier()
val tmp = id
override fun createView(context: Context, stepResult: StepResult?): StepView {
return object : StepView(context, id, isOptional) {
override fun setupViews() = Unit
val root = View.inflate(context, R.layout.example, this)
override fun createResults(): QuestionResult =
CustomResult(
root.findViewById<EditText>(R.id.input).text.toString(),
"stringIdentifier",
id,
Date(),
Date()
)
override fun isValidInput(): Boolean = this@CustomStep.isOptional
override var isOptional: Boolean = this@CustomStep.isOptional
override val id: StepIdentifier = tmp
override fun style(surveyTheme: SurveyTheme) {
// do styling here
}
init {
root.findViewById<Button>(R.id.continue)
.setOnClickListener { onNextListener(createResults()) }
root.findViewById<Button>(R.id.back)
.setOnClickListener { onBackListener(createResults()) }
root.findViewById<Button>(R.id.close)
.setOnClickListener { onCloseListener(createResults(), FinishReason.Completed) }
root.findViewById<Button>(R.id.skip)
.setOnClickListener { onSkipListener() }
root.findViewById<EditText>(R.id.input).setText(
(stepResult?.results?.firstOrNull() as? CustomResult)?.customData ?: ""
)
}
}
}
}
This is an overview of which features iOS ResearchKit Surveys provides and which ones are already supported by SurveyKit on Android. The goal is to make both libraries match in terms of their functionality.
Steps | iOS ResearchKit | Android SurveyKit |
---|---|---|
Instruction | ✅ | ✅ |
Single selection | ✅ | ✅ |
Multi selection | ✅ | ✅ |
Boolean answer | ✅ | ✅ |
Value picker | ✅ | ✅ |
Image choice | ✅ | ✅ |
Numeric answer | ✅ | ✅ |
Time of day | ✅ | ✅ |
Date selection | ✅ | ✅ |
Text answer (unlimited) | ✅ | ✅ |
Text answer (limited) | ✅ | ✅ |
Text answer (validated) | ✅ | ✅ |
Scale answer | ✅ | ✅ |
Email answer | ✅ | ✅ |
Location answer | ✅ | ✅ |
This Android library is created with ❤️ by QuickBird Studios.
Open an issue if you need help, if you found a bug, or if you want to discuss a feature request.
Open a PR if you want to make changes to SurveyKit.
For the moment, a mandatory requirement for a PR to be accepted is also applying ktlint when submitting this PR.
SurveyKit is released under an MIT license. See License for more information.