Skip to content

Commit

Permalink
[Android]: Add kotlin example
Browse files Browse the repository at this point in the history
Signed-off-by: Antonio Gisondi <[email protected]>
  • Loading branch information
harlem88 committed Aug 30, 2022
1 parent 7fe4d2f commit a9875c0
Show file tree
Hide file tree
Showing 34 changed files with 911 additions and 1 deletion.
1 change: 1 addition & 0 deletions examples/KotlinAndroid/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
56 changes: 56 additions & 0 deletions examples/KotlinAndroid/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}

android {
compileSdkVersion 33

defaultConfig {
applicationId "org.astarteplatform.devicesdk.kotlinandroid.examples"
minSdkVersion 23
targetSdkVersion 33
versionCode 1
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
viewBinding true
}

lintOptions {
disable 'InvalidPackage'
}
}

dependencies {
implementation project(':DeviceSDKAndroid')
implementation fileTree(dir: "libs", include: ["*.jar"])

implementation 'androidx.core:core-ktx:1.8.0'
implementation 'androidx.appcompat:appcompat:1.5.0'
implementation 'com.google.android.material:material:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.navigation:navigation-fragment-ktx:2.5.1'
implementation 'androidx.navigation:navigation-ui-ktx:2.5.1'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}

tasks.withType(Javadoc).all { enabled = false }
21 changes: 21 additions & 0 deletions examples/KotlinAndroid/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.astarteplatform.devicesdk.kotlinandroid.examples

import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4

import org.junit.Test
import org.junit.runner.RunWith

import org.junit.Assert.*

/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("org.astarteplatform.devicesdk.kotlinandroid.examples", appContext.packageName)
}
}
25 changes: 25 additions & 0 deletions examples/KotlinAndroid/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.astarteplatform.devicesdk.android.kotlinexamples">

<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.Astartedevicesdkjava">
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.Astartedevicesdkjava.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"interface_name": "org.astarte-platform.genericcommands.ServerCommands",
"version_major": 0,
"version_minor": 1,
"type": "datastream",
"ownership": "server",
"description": "Generic server commands interface.",
"doc": "This interface allows sending strings representing a command to a device. This allows to build simple applications that interact with the device to, e.g., turn on or off a switch.",
"mappings": [
{
"endpoint": "/command",
"type": "string",
"explicit_timestamp": true,
"database_retention_policy": "use_ttl",
"database_retention_ttl": 86400,
"doc": "A string representing a command. The command is deleted from Astarte after 24 hours."
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"interface_name": "org.astarte-platform.genericevents.DeviceEvents",
"version_major": 0,
"version_minor": 1,
"type": "datastream",
"ownership": "device",
"description": "Generic device events interface.",
"doc": "This interface allows a device to send a string representing an event. This allows to build simple applications that notify Astarte when, e.g., a button is pressed on the device.",
"mappings": [
{
"endpoint": "/event",
"type": "string",
"explicit_timestamp": true,
"doc": "A string representing a device event."
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package org.astarteplatform.devicesdk.android.kotlinexamples

import org.astarteplatform.devicesdk.AstarteDevice
import org.astarteplatform.devicesdk.AstartePairingException
import org.astarteplatform.devicesdk.android.AstarteAndroidDevice
import org.astarteplatform.devicesdk.crypto.AstarteCryptoException
import org.astarteplatform.devicesdk.protocol.AstarteDeviceDatastreamInterface
import org.astarteplatform.devicesdk.transport.AstarteTransportException
import org.joda.time.DateTime
import java.util.concurrent.Executor

class AstarteDeviceService(
activity: MainActivity,
executor: Executor
) {
private val EVENTS_INTERFACE = "org.astarte-platform.genericevents.DeviceEvents"

/*
* We keep a reference to the activity so we can pass it to the handlers, that will call some
* callbacks on it.
*
* The call passes in an executor, all the Astarte Device code will be executed on it, since
* it uses Rooms for its persistence, which can't be called in the UI thread. This is just an
* example and you're free to use whatever Android threading abstraction you like, as long as
* the Astarte Device code is not executed on the UI thread.
*/
private var mExecutor: Executor? = executor
private var mActivity: MainActivity? = activity

private var mDevice: AstarteDevice? = null

fun run(realm: String, deviceId: String, credentialsSecret: String, pairingUrl: String) {
mExecutor?.execute(
Runnable {
mDevice = try {
/*
* Astarte device creation
*
* The interfaces supported by the device are populated by
* ExampleInterfaceProvider, see that class for more details
*/
AstarteAndroidDevice(
deviceId,
realm,
credentialsSecret,
ExampleInterfaceProvider(
mActivity
),
pairingUrl,
mActivity!!.applicationContext
)
} catch (e: Exception) {
mActivity?.onAstarteServiceError(e)
return@Runnable
}

/*
* Connect listeners
*
* See ExampleMessageListener to listen for device connection, disconnection
* and failure.
* See ExampleGlobalEventListener to listen for incoming data pushed from
* Astarte.
*/
mDevice?.addGlobalEventListener(
ExampleGlobalEventListener(
mActivity
)
)
mDevice?.astarteMessageListener = ExampleMessageListener(
mActivity
)

/*
* Set this if you want to let AstarteDevice take care of the reconnection.
* The default is false, which means that the application is responsible of
* reconnecting in case of failures
*/
mDevice?.setAlwaysReconnect(true)

/*
* Notify the activity that the device is ready to use
*/
mActivity?.onAstarteServiceInitialized()
})
}

@Throws(
AstartePairingException::class,
AstarteCryptoException::class,
AstarteTransportException::class
)
fun connect() {
mExecutor!!.execute {
try {
/*
* Start the connection. ExampleMessageListener will notify when the
* connection is completed.
*/
mDevice?.connect()
} catch (e: java.lang.Exception) {
mActivity?.onAstarteServiceError(e)
}
}
}

fun sendPing() {
mExecutor!!.execute {
try {
/*
* Publish on a datastream interface
*
* Retrieve the interface from the device and call streamData on it.
*/
(mDevice?.getInterface(EVENTS_INTERFACE) as AstarteDeviceDatastreamInterface)
.streamData("/event", "ping", DateTime.now())
} catch (e: java.lang.Exception) {
mActivity?.onAstarteServiceError(e)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package org.astarteplatform.devicesdk.android.kotlinexamples

import android.util.Log
import org.astarteplatform.devicesdk.protocol.AstarteAggregateDatastreamEvent
import org.astarteplatform.devicesdk.protocol.AstarteDatastreamEvent
import org.astarteplatform.devicesdk.protocol.AstarteGlobalEventListener
import org.astarteplatform.devicesdk.protocol.AstartePropertyEvent

class ExampleGlobalEventListener(activity: MainActivity?) : AstarteGlobalEventListener() {
private val TAG = "ExampleGlobalEventsListener"
private val COMMANDS_INTERFACE = "org.astarte-platform.genericcommands.ServerCommands"
private val mActivity: MainActivity? = activity

override fun valueReceived(e: AstarteDatastreamEvent) {
/*
* This function gets called when the device receives data on a server owned
* datastream interface with individual aggregation.
*
* We handle data coming from org.astarte-platform.genericcommands.ServerCommands/command
* as special case, displaying it in the commands TextView.
*/
if (e.interfaceName == COMMANDS_INTERFACE && e.path == "/command") {
mActivity?.setCommandsText(e.valueString)
} else {
/*
* Otherwise, we just print what we receive
*/
Log.i(
TAG,
"Received datastream value on interface "
+ e.interfaceName
+ ", path: "
+ e.path
+ ", value: "
+ e.value
)
}
}

override fun valueReceived(e: AstarteAggregateDatastreamEvent) {
/*
* This function gets called when the device receives data on a server owned
* datastream interface with object aggregation.
*/
Log.i(
TAG,
("Received aggregate datastream value on interface "
+ e.interfaceName
+ ", values: "
+ e.values)
)
}

override fun propertyReceived(e: AstartePropertyEvent) {
/*
* This function gets called when the device receives data on a server owned
* properties interface.
*/
Log.i(
TAG,
("Received property on interface "
+ e.interfaceName
+ ", path: "
+ e.path
+ ", value: "
+ e.value)
)
}

override fun propertyUnset(e: AstartePropertyEvent) {
/*
* This function gets called when the device receives an unset on a server owned
* properties interface.
*/
Log.i(TAG, "Received unset on interface " + e.interfaceName + ", path: " + e.path)
}
}
Loading

0 comments on commit a9875c0

Please sign in to comment.