Native Android Guide
Native SDK/Library to collect data from android device.
Based on native application context to pull data using queries on local device.
To gain access application will need user permission on these permissions:
SDK collection method will only collect data if the permission is granted, for example if user only accepted location and call logs, collection method will return a JSOn object with these two only to avoid exceptions
- SMS:
READ_SMS
- Minimum SDK Required 21
- Target SDK Required 31+
General usage guide
1. Install the SDK
To install the SDK you have to obtain the AAR file that contains Credify Library:
-
add Credify SDK dependency to your application level gradle file:
dependencies {
implementation 'com.github.Credify-App.mono-repo:credify-collection-sdk-full:1.0.2'
} -
If you are planning to use the SMS-Only version of the SDK use the following flavour/package:
dependencies {
implementation 'com.github.Credify-App.mono-repo:credify-collection-sdk-sms:1.0.2'
} -
If your project doesn't use Jitpack gradle repository, please add in root-level gradle file:
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
} -
Rebuild your application
2. Importing library
Upon including the SDK in your application build.gradle and running a clean build, it will add the above permissions to the application manifest. You can remove unused permissions that your application collection profile doesn't require.
Sample Imports
// add the following imports to classes that will use the SDK methods
import com.credify.collectionsdk.Collector
import com.credify.collectionsdk.collectiontask.CollectionTaskCallback
//...
// To start using SDK methods create an instance from Credify collector
val collector = Collector()
//...
3. Initializing the SDK
To start using SDK functionalities SDK must be initialized with some identifiers that will be used to tag information collected with the user that is using the application. Usually initialization should take place right after user login.
Sample Initialization
// creating callbacks to get the initialization result
val initializeCallback = object : CollectionTaskCallback {
override fun onSuccess(result: Boolean, data: JSONObject) {
// onSuccess callback is called when a successful initialization is done
// Needed to run on UI main thread
MainThreadExecutor().execute {
binding.progressBar.isVisible = false // you can now hide loading indicators
// provide user identifiers to SetUserInfo
collector.SetUserInfo("MyUserName", "Ahmed", "+01008888333")
// You can also provide username only if name and mobile number are not available
collector.SetUserIdentifier("Testinguser")
}
}
override fun onError(err: String?, errorCode: String?) {
// onError callback is called when an error occurs while initializing, check error code and error message for more information.
runOnUiThread {
binding.previewCollectedData.text = "$errorCode:$err"
binding.progressBar.isVisible = false
}
}
}
// a good place to show a loading indicator
binding.progressBar.isVisible = true
collector.Initialize(
this, // activity is required
applicationContext, // application context is required
initializeCallback, // callback that was defined above
"<Your application token goes here>" // application token that was generated for your application
)
4. Requesting Permissions
User consent / approval on permissions is needed to run the collection method, to do so you can choose one of the following options:
- Ask user for permission from inside the client application code (can be useful if client application wants to show custom UI before asking for permissions)
- Or you can simply call the SDK method RequestPermissions which requests permissions for collection items that your application can collect.
Sample Permissions Request
collector.RequestPermissions()
6. Requesting permissions
SDK provies a method to get status of each enabled collection type and permissions that are accepted and/or rejected, this method can be used to check which collection types will be collected and sent.
Permission status will be checked for all collection types that are enabled.
Sample Permissions Status Response
{
"accepted_permissions": ["android.permission.READ_SMS"],
"rejected_permissions": [],
"enabled_collection": {
"SMS_LOG": true,
"DEVICE_INFO": true
}
}
Sample Permissions Status Request
collector.GetPermissionsStatus()
6. Collecting and Sending Data
After gaining proper approval, you can can call CollectData to collect and send collected data securely.
val collectionCallback = object : CollectionTaskCallback {
override fun onSuccess(result: Boolean, data: JSONObject) {
// onSuccess is triggered when data is collected and sent successfully
MainThreadExecutor().execute {
// data returned contains a JSON Object that contains each type of data collected and status of collecting and sending the data
// result 200 means success
// other results might indicate error, please check the error codes below for more information.
binding.previewCollectedData.text =
if (result) "Collection Successful\n\n${data.toString(2)}" else "Error in collection"
binding.progressBar.isVisible = false
}
}
override fun onError(err: String?, errorCode: String?) {
// onError is triggered when a critical error occurs that prevents Credify SDK from collecting data.
// can be due to a null context or destroyed activity, check errorCode and error message for more debugging information.
binding.previewCollectedData.text = "$errorCode:$err"
binding.progressBar.isVisible = false
}
}
Error Codes and Debugging
Initialization error codes
Error Code | Description | Recommendation |
---|---|---|
100 | Activity or Context null | Make sure the activity is not null and avoid placing initialization in splash activity |
104 | Credify API configuration responded but configuration is null or mismatching | Update your Credify SDK |
Invalid token, response code ... | Credify API configuration responded with non-success status code | Review your token and make sure it is valid |
103 | Unhandled Exception | Check logcat logs for more info |
Collection error codes
These are returned on each data type collected
Error Code | Description | Recommendation |
---|---|---|
200 | Data was collected and sent properly | |
100 | Activity or Context null | Make sure the activity is not null and avoid placing initialization in splash activity |
104 | Credify API configuration responded but configuration is null or mismatching | Update your Credify SDK |
103 | Unhandled Exception | Check logcat logs for more info |
other values | Means that data was collected properly, but a network/service error occurred while sending | Get in touch with Credify Team to investigate further |
Debugging
Simply run your application in debug mode and filter logcat logs with the tag name "CredifySDK", logs messages under this tag contains log of all steps taken by the SDK and contains informational logs, logical errors, handled native exceptions and unhandled native exceptions.
Full code example
Fully working example
package com.credify.sampleAPP
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.navigateUp
import androidx.navigation.ui.setupActionBarWithNavController
import com.credify.collectionsdk.Collector
import com.credify.collectionsdk.collectiontask.CollectionTaskCallback
import com.credify.sampleAPP.databinding.ActivityMainBinding
import org.json.JSONObject
import java.util.concurrent.Executor
class MainActivity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(binding.toolbar)
val navController = findNavController(R.id.nav_host_fragment_content_main)
appBarConfiguration = AppBarConfiguration(navController.graph)
setupActionBarWithNavController(navController, appBarConfiguration)
val collector = Collector()
val collectionCallback = object : CollectionTaskCallback {
override fun onSuccess(result: Boolean, data: JSONObject) {
MainThreadExecutor().execute {
// Code will run on the main thread
binding.previewCollectedData.text =
if (result) "Collection Successful\n\n${data.toString(2)}" else "Error in collection"
binding.progressBar.isVisible = false
}
}
override fun onError(err: String?, errorCode: String?) {
binding.previewCollectedData.text = "$errorCode:$err"
binding.progressBar.isVisible = false
}
}
val initializeCallback = object : CollectionTaskCallback {
override fun onSuccess(result: Boolean, data: JSONObject) {
MainThreadExecutor().execute {
// Code will run on the main thread
binding.previewCollectedData.text =
if (result) "Initialize successful\n\n${data.toString(2)}" else "Error in collection"
binding.progressBar.isVisible = false
// after login
//collector.SetUserIdentifier("SampleUserName")
collector.SetUserInfo("SampleUserName", "Amr", "01008888333")
}
}
override fun onError(err: String?, errorCode: String?) {
runOnUiThread {
binding.previewCollectedData.text = "$errorCode:$err"
binding.progressBar.isVisible = false
}
}
}
binding.progressBar.isVisible = true
collector.Initialize(
this,
applicationContext,
initializeCallback,
"<your application token goes here>"
)
binding.permissionsButton.setOnClickListener { view ->
collector.RequestPermissions()
}
binding.collectButton.setOnClickListener { view ->
binding.progressBar.isVisible = true
collector.CollectData(collectionCallback)
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.menu_main, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
return when (item.itemId) {
R.id.action_settings -> true
else -> super.onOptionsItemSelected(item)
}
}
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.nav_host_fragment_content_main)
return navController.navigateUp(appBarConfiguration)
|| super.onSupportNavigateUp()
}
}
class MainThreadExecutor : Executor {
private val handler = Handler(Looper.getMainLooper())
override fun execute(r: Runnable) {
handler.post(r)
}
}