Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,14 @@ buildscript {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.2.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}

plugins {
id 'com.diffplug.spotless' version '5.5.1'
id("io.github.gradle-nexus.publish-plugin") version "1.1.0"
id 'com.android.application' version '4.2.2' apply false
id 'com.android.library' version '4.2.2' apply false
id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
}

allprojects {
Expand Down
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