Browse Source

Database Inserted

master
sipp11 5 years ago
parent
commit
573d3bc021
  1. 14
      app/build.gradle
  2. 81
      app/src/androidTest/java/co/zzyzx/sensorlogger/DatabaseTest.kt
  3. 20
      app/src/main/java/co/zzyzx/sensorlogger/Database.kt
  4. 103
      app/src/main/java/co/zzyzx/sensorlogger/EndlessService.kt
  5. 57
      app/src/main/java/co/zzyzx/sensorlogger/MainActivity.kt
  6. 43
      app/src/main/java/co/zzyzx/sensorlogger/db/Database.kt
  7. 29
      app/src/main/java/co/zzyzx/sensorlogger/db/dao.kt
  8. 15
      app/src/main/java/co/zzyzx/sensorlogger/db/entity.kt
  9. 10
      app/src/main/res/layout/activity_main.xml

14
app/build.gradle

@ -1,14 +1,14 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android { android {
compileSdkVersion 28 compileSdkVersion 28
defaultConfig { defaultConfig {
applicationId "co.zzyzx.sensorlogger" applicationId "co.zzyzx.sensorlogger"
minSdkVersion 23 minSdkVersion 26
targetSdkVersion 28 targetSdkVersion 28
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"
@ -30,9 +30,17 @@ dependencies {
implementation 'com.github.kittinunf.fuel:fuel:2.1.0' implementation 'com.github.kittinunf.fuel:fuel:2.1.0'
implementation 'com.github.kittinunf.fuel:fuel-android:2.1.0' implementation 'com.github.kittinunf.fuel:fuel-android:2.1.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0-M2' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0-M2'
implementation 'org.jetbrains.exposed:exposed:0.16.1' // Room dependencies
// implementation 'androidx.room:room-runtime:2.0.0'
// kapt 'androidx.room:room-compiler:2.0.0'
implementation 'android.arch.persistence.room:runtime:1.1.1'
annotationProcessor 'android.arch.persistence.room:compiler:1.1.1'
kapt 'android.arch.persistence.room:compiler:1.1.1'
// implementation 'android.arch.lifecycle:extensions:1.1.1'
// kapt 'android.arch.lifecycle:compiler:1.1.1'
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
} }

81
app/src/androidTest/java/co/zzyzx/sensorlogger/DatabaseTest.kt

@ -0,0 +1,81 @@
package co.zzyzx.sensorlogger
import android.arch.persistence.room.Room
import android.support.test.InstrumentationRegistry
import android.support.test.runner.AndroidJUnit4
import co.zzyzx.sensorlogger.db.AppDatabase
import co.zzyzx.sensorlogger.db.Record
import co.zzyzx.sensorlogger.db.RecordDao
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import java.io.IOException
import java.time.Instant
@RunWith(AndroidJUnit4::class)
class DatabaseTest {
private lateinit var recordDao: RecordDao
private lateinit var db: AppDatabase
@Before
fun createDb() {
val appContext = InstrumentationRegistry.getTargetContext()
db = Room.inMemoryDatabaseBuilder(appContext, AppDatabase::class.java)
.allowMainThreadQueries()
.build()
recordDao = db.recordDao()
val records = arrayListOf(
Record(
timestamp = Instant.now().epochSecond,
sensor = "gps",
data = "123,23232"
),
Record(
timestamp = Instant.now().epochSecond,
sensor = "accelerometer",
data = "123,23232"
)
)
for (rec in records) {
recordDao.insertAll(rec)
}
}
@After
@Throws(IOException::class)
fun closeDb() {
db.close()
}
@Test
@Throws(Exception::class)
fun writeUserAndReadInList() {
val rec = Record(
timestamp = Instant.now().epochSecond,
sensor = "gps",
data = "333,32322"
)
recordDao.insertAll(rec)
val byName = recordDao.findBySensor("gps")
assertEquals(byName.data, rec.data)
assertEquals(byName.timestamp, rec.timestamp)
}
@Test
@Throws(Exception::class)
fun deleteRecord() {
val records = recordDao.getAll()
val total = records.size
// Delete one
recordDao.delete(records.get(0))
val totalAfterMinusOne = recordDao.getAll().size
assertEquals(total - 1, totalAfterMinusOne)
}
}

20
app/src/main/java/co/zzyzx/sensorlogger/Database.kt

@ -1,20 +0,0 @@
package co.zzyzx.sensorlogger
import org.jetbrains.exposed.dao.EntityID
import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass
import org.jetbrains.exposed.dao.IntIdTable
object Records : IntIdTable() {
val timestamp = datetime("timestamp")
val sensor = varchar("sensor", 20)
val data = text("data:")
}
class Record(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<Record>(Records)
var timestamp by Records.timestamp
var sensor by Records.sensor
var data by Records.data
}

103
app/src/main/java/co/zzyzx/sensorlogger/EndlessService.kt

@ -1,6 +1,5 @@
package co.zzyzx.sensorlogger package co.zzyzx.sensorlogger
//import com.github.kittinunf.fuel.core.extensions.jsonBody
import android.app.* import android.app.*
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
@ -19,6 +18,7 @@ import android.os.IBinder
import android.os.PowerManager import android.os.PowerManager
import android.provider.Settings import android.provider.Settings
import android.widget.Toast import android.widget.Toast
import co.zzyzx.sensorlogger.db.RecordRepository
import com.github.kittinunf.fuel.Fuel import com.github.kittinunf.fuel.Fuel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
@ -29,32 +29,11 @@ import java.util.*
fun Double.format(digits: Int) = java.lang.String.format("%.${digits}f", this) fun Double.format(digits: Int) = java.lang.String.format("%.${digits}f", this)
fun Float.format(digits: Int) = java.lang.String.format("%.${digits}f", this)
const val SENSOR_DELAY = SensorManager.SENSOR_DELAY_GAME
class EndlessService : Service(), SensorEventListener, LocationListener { class EndlessService : Service(), SensorEventListener, LocationListener {
override fun onLocationChanged(result: Location) {
val txt =
"coords: ${result.longitude.format(4)}, ${result.latitude.format(4)} - ${result.time.toDouble().format(
0
)}"
log(txt)
val notification = createNotification(txt)
nm.notify(notificationId, notification)
}
override fun onStatusChanged(p0: String?, p1: Int, p2: Bundle?) {
}
override fun onProviderEnabled(p0: String?) {
}
override fun onProviderDisabled(p0: String?) {
}
private var wakeLock: PowerManager.WakeLock? = null private var wakeLock: PowerManager.WakeLock? = null
private var isServiceStarted = false private var isServiceStarted = false
private lateinit var nm: NotificationManager private lateinit var nm: NotificationManager
@ -63,7 +42,10 @@ class EndlessService : Service(), SensorEventListener, LocationListener {
private lateinit var mSensorManager: SensorManager private lateinit var mSensorManager: SensorManager
private lateinit var mAccelerometer: Sensor private lateinit var mAccelerometer: Sensor
private lateinit var mLocationManager: LocationManager private lateinit var mGyroscope: Sensor
private var mLocationManager: LocationManager? = null // need to do this because of permission
private lateinit var recRepo: RecordRepository
override fun onBind(intent: Intent): IBinder? { override fun onBind(intent: Intent): IBinder? {
log("Some component want to bind with the service") log("Some component want to bind with the service")
@ -87,7 +69,8 @@ class EndlessService : Service(), SensorEventListener, LocationListener {
) )
} }
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL) mSensorManager.registerListener(this, mAccelerometer, SENSOR_DELAY)
mSensorManager.registerListener(this, mGyroscope, SENSOR_DELAY)
// by returning this we make sure the service is restarted if the system kills the service // by returning this we make sure the service is restarted if the system kills the service
return START_STICKY return START_STICKY
@ -96,35 +79,40 @@ class EndlessService : Service(), SensorEventListener, LocationListener {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
log("The service has been created".toUpperCase()) log("The service has been created".toUpperCase())
// database init
recRepo = RecordRepository(applicationContext)
// notification init
val notification = createNotification() val notification = createNotification()
notification.flags = Notification.FLAG_ONGOING_EVENT notification.flags = Notification.FLAG_ONGOING_EVENT
startForeground(notificationId, notification) startForeground(notificationId, notification)
mSensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager mSensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
if (checkSelfPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { if (checkSelfPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
mLocationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager mLocationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
mLocationManager.requestLocationUpdates( mLocationManager?.requestLocationUpdates(
LocationManager.PASSIVE_PROVIDER, LocationManager.PASSIVE_PROVIDER,
2000, // 2-sec 2000, // 2-sec
2.toFloat(), // 2-meter 2.toFloat(), // 2-meter
this this
) )
mLocationManager.requestLocationUpdates( mLocationManager?.requestLocationUpdates(
LocationManager.GPS_PROVIDER, LocationManager.GPS_PROVIDER,
2000, // 2-sec 2000, // 2-sec
2.toFloat(), // 2-meter 2.toFloat(), // 2-meter
this this
) )
log("ok permission to get location updates") // log("ok permission to get location updates")
} }
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
mSensorManager.unregisterListener(this) mSensorManager.unregisterListener(this)
mLocationManager.removeUpdates(this) mLocationManager?.removeUpdates(this)
log("The service has been destroyed".toUpperCase()) log("The service has been destroyed".toUpperCase())
Toast.makeText(this, "Service destroyed", Toast.LENGTH_SHORT).show() Toast.makeText(this, "Service destroyed", Toast.LENGTH_SHORT).show()
} }
@ -175,6 +163,39 @@ class EndlessService : Service(), SensorEventListener, LocationListener {
nm.cancelAll() nm.cancelAll()
} }
override fun onLocationChanged(result: Location) {
val notiText = "coords: ${result.longitude.format(4)}, ${result.latitude.format(4)}"
val txt = arrayOf(
result.time,
result.longitude.format(7),
result.latitude.format(7),
result.speed,
result.altitude,
result.bearing,
result.accuracy,
result.provider,
result.isFromMockProvider
).joinToString(",")
recRepo.addNewRecord("location", txt)
val notification = createNotification(notiText)
nm.notify(notificationId, notification)
}
override fun onStatusChanged(p0: String?, p1: Int, p2: Bundle?) {
}
override fun onProviderEnabled(p0: String?) {
}
override fun onProviderDisabled(p0: String?) {
}
override fun onAccuracyChanged(p0: Sensor?, p1: Int) { override fun onAccuracyChanged(p0: Sensor?, p1: Int) {
} }
@ -185,7 +206,6 @@ class EndlessService : Service(), SensorEventListener, LocationListener {
when (evt.sensor?.type) { when (evt.sensor?.type) {
Sensor.TYPE_ACCELEROMETER -> { Sensor.TYPE_ACCELEROMETER -> {
val accelLin = arrayOf(0.toDouble(), 0.toDouble(), 0.toDouble()) val accelLin = arrayOf(0.toDouble(), 0.toDouble(), 0.toDouble())
val alpha = 0.8f val alpha = 0.8f
accelGravity[0] = alpha * accelGravity[0] + (1 - alpha) * evt.values[0] accelGravity[0] = alpha * accelGravity[0] + (1 - alpha) * evt.values[0]
@ -194,13 +214,20 @@ class EndlessService : Service(), SensorEventListener, LocationListener {
accelLin[0] = evt.values[0] - accelGravity[0] accelLin[0] = evt.values[0] - accelGravity[0]
accelLin[1] = evt.values[1] - accelGravity[1] accelLin[1] = evt.values[1] - accelGravity[1]
accelLin[2] = evt.values[2] - accelGravity[2] accelLin[2] = evt.values[2] - accelGravity[2]
val txt = arrayOf(
val txt = accelLin[0].format(3),
"[accel] (${accelLin[0].format(3)}, ${accelLin[1].format(3)}, ${accelLin[2].format( accelLin[1].format(3),
3 accelLin[2].format(3)
)})" ).joinToString(",")
// val notification = createNotification(txt) recRepo.addNewRecord("accelerometer", txt)
// nm.notify(notificationId, notification) }
Sensor.TYPE_GYROSCOPE -> {
val txt = arrayOf(
evt.values[0].format(3),
evt.values[1].format(3),
evt.values[2].format(3)
).joinToString(",")
recRepo.addNewRecord("gyroscope", txt)
} }
} }
} }

57
app/src/main/java/co/zzyzx/sensorlogger/MainActivity.kt

@ -1,19 +1,20 @@
package co.zzyzx.sensorlogger package co.zzyzx.sensorlogger
//import co.zzyzx.sensorlogger.db.RecordDatabase
import android.Manifest import android.Manifest
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.graphics.Color
import android.location.LocationManager import android.location.LocationManager
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.support.v4.app.ActivityCompat import android.support.v4.app.ActivityCompat
import android.support.v4.content.ContextCompat import android.support.v4.content.ContextCompat
import android.support.v7.app.AppCompatActivity import android.support.v7.app.AppCompatActivity
import android.view.View import android.view.View
import android.widget.Button
import android.widget.Toast import android.widget.Toast
import co.zzyzx.sensorlogger.db.RecordRepository
import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.activity_main.*
@ -22,9 +23,15 @@ const val PERMISSION_LOCATION = 0
class MainActivity : AppCompatActivity(), class MainActivity : AppCompatActivity(),
ActivityCompat.OnRequestPermissionsResultCallback { ActivityCompat.OnRequestPermissionsResultCallback {
private lateinit var recRepo: RecordRepository
private var handler = Handler()
private val millisDelay = 200L
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
// db init
recRepo = RecordRepository(applicationContext)
requestForNecessaryPermissions() requestForNecessaryPermissions()
title = getString(R.string.app_name) title = getString(R.string.app_name)
@ -43,23 +50,56 @@ class MainActivity : AppCompatActivity(),
} }
} }
ask_location_permission_button.let{ ask_location_permission_button.let {
it.setOnClickListener { requestForNecessaryPermissions() } it.setOnClickListener {
val perms = arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)
ActivityCompat.requestPermissions(
this, perms,
PERMISSION_LOCATION
)
}
} }
updateCounter()
} }
private fun UIStatusUpdates(hasLocPerm : Boolean = true) { private fun updateCounter() {
val runnable = Runnable { updateCounter() }
val sensors = arrayOf("location", "accelerometer", "gyroscope")
var text = mutableListOf<String>("Status", "-------------------------------")
for (sensor in sensors) {
val count = recRepo.getRecordCount(sensor)
text.add("$sensor --- $count records")
}
status_textview.text = text.joinToString(System.getProperty("line.separator"))
handler.postDelayed(runnable, millisDelay)
}
private fun UIStatusUpdates(hasLocPerm: Boolean = true) {
val mLocationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager val mLocationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
log("isLocationEnabled? ${mLocationManager.isLocationEnabled} - ${mLocationManager.allProviders.toString()}") log("isLocationEnabled? ${mLocationManager.isLocationEnabled} - ${mLocationManager.allProviders}")
if (hasLocPerm && mLocationManager.isLocationEnabled) { if (hasLocPerm && mLocationManager.isLocationEnabled) {
location_status_textview.text = "OK" location_status_textview.text = "OK"
location_status_textview.setBackgroundColor(ContextCompat.getColor(applicationContext, R.color.green)) location_status_textview.setBackgroundColor(
ContextCompat.getColor(
applicationContext,
R.color.green
)
)
ask_location_permission_button.visibility = View.INVISIBLE
} else { } else {
location_status_textview.text = "OFF" location_status_textview.text = "OFF"
location_status_textview.setBackgroundColor(ContextCompat.getColor(applicationContext, R.color.red)) location_status_textview.setBackgroundColor(
ContextCompat.getColor(
applicationContext,
R.color.red
)
)
ask_location_permission_button.visibility = View.VISIBLE
} }
} }
@ -71,7 +111,6 @@ class MainActivity : AppCompatActivity(),
) )
!= PackageManager.PERMISSION_GRANTED != PackageManager.PERMISSION_GRANTED
) { ) {
ask_location_permission_button.visibility = View.INVISIBLE
// Permission is not granted // Permission is not granted
// Should we show an explanation? // Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale( if (ActivityCompat.shouldShowRequestPermissionRationale(

43
app/src/main/java/co/zzyzx/sensorlogger/db/Database.kt

@ -0,0 +1,43 @@
package co.zzyzx.sensorlogger.db
import android.arch.persistence.room.Database
import android.arch.persistence.room.Room
import android.arch.persistence.room.RoomDatabase
import android.content.Context
import co.zzyzx.sensorlogger.log
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import java.time.Instant
@Database(entities = arrayOf(Record::class), version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun recordDao(): RecordDao
}
class RecordRepository {
private var DB_NAME = "db_sensor_recs"
var db: AppDatabase
private var dao: RecordDao
constructor(context: Context) {
db = Room.databaseBuilder(context, AppDatabase::class.java, DB_NAME)
.allowMainThreadQueries()
.build()
dao = db.recordDao()
}
fun addNewRecord(sensor: String, data: String) {
val now = Instant.now().toEpochMilli()
GlobalScope.launch {
log("add [${now}] ${sensor} - ${data}")
dao.insertAll(Record(timestamp = now, sensor = sensor, data = data))
}
}
fun getRecordCount(sensor: String): Int {
return dao.getCount(sensor)
}
}

29
app/src/main/java/co/zzyzx/sensorlogger/db/dao.kt

@ -0,0 +1,29 @@
package co.zzyzx.sensorlogger.db
import android.arch.lifecycle.LiveData
import android.arch.persistence.room.*
@Dao
interface RecordDao {
@Query("SELECT COUNT(*) FROM record WHERE sensor = :sensor")
fun getCount(sensor: String): Int
@Query("SELECT * FROM record")
fun getAll(): List<Record>
@Query("SELECT * FROM record WHERE sensor LIKE :sensor ORDER BY timestamp DESC LIMIT 1")
fun getLatestLiveData(sensor : String): LiveData<List<Record>>
@Query("SELECT * FROM record WHERE sensor LIKE :name ORDER BY timestamp DESC LIMIT 1")
fun findBySensor(name: String): Record
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(vararg records: Record)
@Insert
fun insert(vararg records: Record)
@Delete
fun delete(record: Record)
}

15
app/src/main/java/co/zzyzx/sensorlogger/db/entity.kt

@ -0,0 +1,15 @@
package co.zzyzx.sensorlogger.db
import android.arch.persistence.room.ColumnInfo
import android.arch.persistence.room.Entity
import android.arch.persistence.room.Index
import android.arch.persistence.room.PrimaryKey
@Entity(indices = [(Index(value = ["timestamp", "sensor"], unique = true))])
data class Record(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "timestamp") val timestamp: Long,
@ColumnInfo(name = "sensor") val sensor: String?,
@ColumnInfo(name = "data") val data: String?
)

10
app/src/main/res/layout/activity_main.xml

@ -7,11 +7,6 @@
tools:context=".MainActivity"> tools:context=".MainActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Status" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -63,4 +58,9 @@
android:text="Stop Foreground Service" /> android:text="Stop Foreground Service" />
</LinearLayout> </LinearLayout>
<TextView
android:id="@+id/status_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout> </LinearLayout>
Loading…
Cancel
Save