Collect stats for Q42 internal usage, shared across multiple Android projects.
An iOS version is also available: https://github.com/Q42/Q42Stats
Add the Jitpack repo and include the library:
allprojects {
repositories {
[..]
maven { url "https://jitpack.io" }
}
}
dependencies {
implementation 'com.github.q42:q42stats.android:X.X.X' // Replace X.X.X by the latest version,
// which is available in the Jitpack badge at the top of this page
}
-
Get the API key from The Api project. Use this key in the next step.
-
Call
Q42Stats().runAsync(Context)
from anywhere in your app.class SampleApplication : Application() { override fun onCreate() { super.onCreate() Q42Stats( Q42StatsConfig( apiKey = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", firestoreCollectionId = "yourExistingFirestoreCollectionId", // wait at least 7.1 days between data collections. the extra .1 is for a tiny bit of time-of-day randomization minimumSubmitIntervalSeconds = 7.1.days.inWholeSeconds ) ).runAsync(this.applicationContext) } }
This can be safely called from the main thread since all work (both collecting statistics and sending them to the server) are done on an IO thread.
It is safe to call this function multiple times, as it will exit immediately if it is already running or when a data collection interval has not passed yet.
By default, Q42Stats only logs errors. For debugging purposes, set the log level before using Q42Stats:
Q42Stats.logLevel = Q42StatsLogLevel.Debug
- Q42Stats is tiny, because it only depends on Kotlin. The library size is about 50kB.
- Data consumption is also very modest. For each
minimumSubmitInterval
that you configure, about 5kB of data is transferred. - Data collection is run on an IO thread, so it doesn't block your application.
Q42Stats does not collect any personally identifiably information and is fully GDPR compliant. This has been verified by legal counsel. It should not be necesaary to ask users for permission before invoking Q42Stats.
Below is a listing of all information gathered by Q42Stats. Not all fields are supported on all versions of Android. If unsupported, the corresponding key is omitted.
Key | Value | Notes |
---|---|---|
isClosedCaptioningEnabled |
bool | Live transcription of any spoken audio (minSdk >= 19) |
isTouchExplorationEnabled |
bool | Whether any assistive feature is enabled where the user navigates the interface by touch. Most probably TalkBack, or similar |
isTalkBackEnabled |
bool | iOS: VoiceOver |
isSamsungTalkBackEnabled |
bool | Specifically checks whether com.samsung.android.app.talkback.talkbackservice is enabled |
isSelectToSpeakEnabled |
bool | iOS: Speak Selection |
isSwitchAccessEnabled |
bool | Control the device by a switch such as a foot pedal |
isBrailleBackEnabled |
bool | Navigate the screen with an external Braille display |
isVoiceAccessEnabled |
bool | iOS: Voice Control |
fontScale |
float | Default value depends on device model. Some devices have a default font scaling of 1.1, for example |
fontWeightAdjustment |
float | Default value is: 0. When bold text is enabled this value is greater than 0 (minSdk >= 31). Known issue: Always returns 0 on Samsung |
displayScale |
float | Overall interface scaling ie. display density scaling. Default value may depend on device model (minSdk >= 24) |
isMagnificationEnabled |
bool | Whether magnification is enabled (more specifically, whether magnification shortcuts are enabled) (minSdk >= 17). |
isColorInversionEnabled |
bool | Available starting from Android 5.0 (>=21) |
isColorBlindModeEnabled |
bool | |
isHighTextContrastEnabled |
bool | When enabled, all text has a thin outline. Available starting from Android 5.0 (>=21) |
isAnimationsDisabled |
bool | Can be disabled pre-Android 9 (<28) through Developer Options, starting from Android 9 possible to any user (minSdk >= 19). |
enabledAccessibilityServices |
Array<String> | List of enabled accessibility package names, eg ['com.accessibility.service1', 'nl.accessibility.service2'] |
Key | Value | Notes |
---|---|---|
daytime |
day, twilight, night, unknown | Coarse estimation of time of day. unknown if user is not in Amsterdam TimeZone |
isNightModeEnabled |
bool | iOS: Dark Mode (minSdk: 29) |
Key | Value | Notes |
---|---|---|
screenOrientation |
portrait, landscape, unknown | |
screenWidth |
String | Screen width in density-independent pixels. Beware: this value is different when there is a different screen orientation. |
screenHeight |
String | Screen height in density-independent pixels. Beware: this value is different when there is a different screen orientation. |
Key | Value | Notes |
---|---|---|
applicationId |
String | identifier for the app for which data is collected, as set in the app's Manifest. iOS: bundleId |
defaultLanguage |
en-GB, nl-BE, nl, ... | If the country part (-BE) is not available, the value is just the language part ("nl") |
sdkVersion |
int | 29 for Android 10. See this list |
manufacturer |
String | eg. samsung |
modelName |
String | May be a marketing name, but more often an internal code name. eg. SM-G980F for a particular variant of a Samsung Galaxy S10 |
All classes and functions that are not used by implementing apps should have internal
visibility.
Since this a library, all errors should be caught so that implementing apps don't crash. During
development, you can use the debug flavors to allow the sample app to crash in cause of an
Exception (see handleException()
). When testing, use the release variants to make sure that
exceptions don't crash the implementing apps.
Catch Throwable; not Exception. Since Throwabl is the superclass of Exception, this will make the lib more resilient to crashes.
For accessibility properties we want to track but could not find a property for, see DOCUMENTATION.md
- Get the API key from The Api project. Use this key in the next step.
- Create a file called
secrets.properties
in the root of the project (not in the app folder). Contents:Note that this file will be ignored by git.apikey="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- Change the SampleApplication to construct a Q42Stats object for a real firestore collection.
This library is distributed using JitPack. This makes publishing a new version very easy:
- In
Q42Stats.kt
, incrementDATA_MODEL_VERSION
by 1 if any changes to collected data is made. - Push the code for the new version to the
main
branch - Unit tests will be run automatically. Check JitCI for status
- Create a tag in the semver format:
x.x.x
without the precedingv.
- On GitHub, create a release from that tag. Give it the same name;
x.x.x
- If everything went well the release will be visible on JitPack and the version number in the badge at the top of this page will update.
- In the Sample app build.gradle, Change the
line
jitpackImplementation 'com.github.q42:q42stats.android:x.x.x
to the latest version.
-
JitCi build failing while it can be successfully built locally
Perhaps something is broken in JitCi. JitPack can also be used for building and might be more stable. To disable JitCi, select "Stop Building" in JitCi's settings page You will lose thebuild status indicators in GitHub, however