Skip to main content

Android (XML)

This example shows how to build a classic Android app with AppCompat, XML layouts, and Kotlin coroutines, without Jetpack Compose or ViewModels.

build.gradle.kts

plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
}

android {
namespace = "com.advantahealth.core.sample"
compileSdk = 34

defaultConfig {
applicationId = "com.advantahealth.core.sample"
minSdk = 26
targetSdk = 34
versionCode = 1
versionName = "1.0"
}

compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
}

dependencies {
implementation("com.advantahealth:core-android:1.1")

implementation("androidx.appcompat:appcompat:1.7.0")
implementation("com.google.android.material:material:1.12.0")
implementation("androidx.constraintlayout:constraintlayout:2.2.0")

implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2")
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.advantahealth.core.sample">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat.DayNight.NoActionBar">

<activity android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

</application>

</manifest>

res/layout/activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">

<EditText
android:id="@+id/usernameField"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="Username"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>

<EditText
android:id="@+id/passwordField"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="Password"
android:inputType="textPassword"
app:layout_constraintTop_toBottomOf="@id/usernameField"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="8dp"/>

<Button
android:id="@+id/loginButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Login"
app:layout_constraintTop_toBottomOf="@id/passwordField"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="12dp"/>

<ScrollView
android:id="@+id/scrollView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/loginButton"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="16dp">

<TextView
android:id="@+id/resultView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:background="#EEEEEE"
android:textSize="14sp"/>
</ScrollView>

</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.kt

class MainActivity : AppCompatActivity() {

private lateinit var usernameField: EditText
private lateinit var passwordField: EditText
private lateinit var resultView: TextView

private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

usernameField = findViewById(R.id.usernameField)
passwordField = findViewById(R.id.passwordField)
resultView = findViewById(R.id.resultView)
val loginButton: Button = findViewById(R.id.loginButton)

// Initialize AFCore
val config = AFCoreConfig.builder()
.authKey("Your-Auth-Key")
.baseUrl("https://your.api.base.url")
.swAuthKey("Your-SmartWalking-Key")
.swBaseUrl("https://your.smartwalking.url")
.enableLogging(true)
.logLevel(AFLogLevel.VERBOSE)
.build()
AFCore.initialize(config)

// Register for permission lifecycle hooks
AFPermissions.registerLifecycle(this)

loginButton.setOnClickListener {
login()
}
}

private fun login() {
val username = usernameField.text.toString()
val password = passwordField.text.toString()
scope.launch {
try {
val success = withContext(Dispatchers.IO) {
AFCore.authentication().login(username, password)
}
if (success) {
log("Login success")
runSampleCalls()
} else {
log("Login failed")
}
} catch (e: Exception) {
log("Login error: ${e.localizedMessage}")
}
}
}

private fun runSampleCalls() {
scope.launch {
try {
val profile = withContext(Dispatchers.IO) { AFCore.profile().get() }
log("Profile: ${profile.firstName}")

val acts = withContext(Dispatchers.IO) { AFCore.activities().get(7, 2025) }
log("Activities: ${acts.activities.size}")

val pays = withContext(Dispatchers.IO) { AFCore.payments().get() }
log("Payments: ${pays.size}")

val deviceId = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
withContext(Dispatchers.IO) {
AFCore.smartWalking().connectGoogleHealthConnect(this@MainActivity, deviceId)
}
log("Connected to Health Connect")

withContext(Dispatchers.IO) { AFCore.facilities().startMonitoring() }
log("Started Geofencing")

withContext(Dispatchers.IO) { AFCore.facilities().stopMonitoring() }
log("Stopped Geofencing")

withContext(Dispatchers.IO) { AFPermissions.requestPermission(Permission.FINE_LOCATION) }
log("Requested Fine Location")
} catch (e: Exception) {
log("Error: ${e.localizedMessage}")
}
}
}

private fun log(message: String) {
resultView.append("\n$message")
}

override fun onDestroy() {
super.onDestroy()
scope.cancel()
}
}