Merge branch '100-change-timetrack-account' into 'master'

Resolve "Change timetrack account"

Closes #100

See merge request marcel.schwarz/2020ss-qbc-geofence-timetracking!76
This commit is contained in:
Tobias Wieck 2020-05-28 19:16:22 +00:00
commit d1910ea10d
10 changed files with 285 additions and 48 deletions

View File

@ -7,19 +7,17 @@ import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Bundle import android.os.Bundle
import android.os.Looper import android.os.Looper
import android.widget.ArrayAdapter import android.view.View
import android.widget.Spinner import android.widget.*
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.app.ActivityCompat.requestPermissions import androidx.core.app.ActivityCompat.requestPermissions
import com.google.android.gms.location.* import com.google.android.gms.location.*
import de.hft.geotracker.GeofenceBroadcastReceiver import de.hft.geotracker.GeofenceBroadcastReceiver
import de.hft.geotracker.R import de.hft.geotracker.R
import de.hft.geotracker.retrofit.AuthenticationInterceptor import de.hft.geotracker.retrofit.*
import de.hft.geotracker.retrofit.GeofenceService
import de.hft.geotracker.retrofit.ValuesUser
import kotlinx.android.synthetic.main.activity_home.* import kotlinx.android.synthetic.main.activity_home.*
import kotlinx.android.synthetic.main.dropdown_menu.*
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
@ -35,6 +33,8 @@ class MainActivity : AppCompatActivity() {
lateinit var geofence: Geofence lateinit var geofence: Geofence
lateinit var actionButton: TextView lateinit var actionButton: TextView
var running = false var running = false
var accName: String? = null
lateinit var accounts: ValuesTimetrackAccounts
lateinit var service: GeofenceService lateinit var service: GeofenceService
lateinit var locationRequest: LocationRequest lateinit var locationRequest: LocationRequest
lateinit var fusedLocationClient: FusedLocationProviderClient lateinit var fusedLocationClient: FusedLocationProviderClient
@ -80,22 +80,6 @@ class MainActivity : AppCompatActivity() {
button_start_stop.isEnabled = btnState button_start_stop.isEnabled = btnState
} }
//Setup geofence
geofencingClient = LocationServices.getGeofencingClient(this)
geofence = Geofence.Builder().setRequestId("Test")
.setCircularRegion(48.3575, 8.9745, 50F)
.setExpirationDuration(Geofence.NEVER_EXPIRE)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER or Geofence.GEOFENCE_TRANSITION_EXIT)
.build()
geofencingClient.addGeofences(getGeofencingRequest(), geofencePendingIntent)?.run {
addOnSuccessListener {
println("Geofence added")
}
addOnFailureListener {
println("Error: " + it.stackTrace.forEach { println(it.toString()) })
}
}
//JWToken lesen //JWToken lesen
val fis = openFileInput("JWToken") val fis = openFileInput("JWToken")
val isr = InputStreamReader(fis) val isr = InputStreamReader(fis)
@ -119,17 +103,31 @@ class MainActivity : AppCompatActivity() {
service = retrofit.create(GeofenceService::class.java) service = retrofit.create(GeofenceService::class.java)
showUsername() showUsername()
val spinner: Spinner = findViewById(R.id.account_spinner) //Get Timetrack Accounts
// Create an ArrayAdapter using the string array and a default spinner layout val accountNames = mutableListOf<String>()
ArrayAdapter.createFromResource( // accountNames.add("None")
this, val call = service.getAccounts()
R.array.accounts, android.R.layout.simple_spinner_item call.enqueue(object: Callback<EmbeddedAccounts> {
).also { adapter -> override fun onResponse(
// Specify the layout to use when the list of choices appears call: Call<EmbeddedAccounts>,
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) response: Response<EmbeddedAccounts>
// Apply the adapter to the spinner ) {
spinner.adapter = adapter if (response.isSuccessful) {
} accounts = response.body()!!.accounts
accounts.entries.forEach {
accountNames.add(it.name + "")
}
initializeDropdown(accountNames)
}
}
override fun onFailure(call: Call<EmbeddedAccounts>, t: Throwable) {
accountNames.add("None")
initializeDropdown(accountNames)
Toast.makeText(this@MainActivity, "You dont have any Timetrack Accounts ", Toast.LENGTH_LONG)
.show()
}
})
actionButton = findViewById(R.id.button_start_stop) actionButton = findViewById(R.id.button_start_stop)
actionButton.setOnClickListener { actionButton.setOnClickListener {
callStartStop() callStartStop()
@ -155,6 +153,24 @@ class MainActivity : AppCompatActivity() {
private fun callStartStop() { private fun callStartStop() {
running = !running running = !running
if (running) {
account_spinner.visibility = View.GONE
} else {
account_spinner.visibility = View.VISIBLE
}
if (!accName.isNullOrEmpty()) {
val call = service.triggerTracking(accName!!)
call.enqueue(object : Callback<ValuesTracking> {
override fun onResponse(call: Call<ValuesTracking>, response: Response<ValuesTracking>) {
latitude.text = response.body()?.startdate
longitude.text = response.body()?.enddate
println("Tracking event successful!")
}
override fun onFailure(call: Call<ValuesTracking>, t: Throwable) {
println("Problem at start tracking: " + t.message)
}
})
}
println("StartStop pressed: $running") println("StartStop pressed: $running")
//ToDO call /track Endpoint //ToDO call /track Endpoint
} }
@ -165,18 +181,71 @@ class MainActivity : AppCompatActivity() {
override fun onResponse(call: Call<ValuesUser>, response: Response<ValuesUser>) { override fun onResponse(call: Call<ValuesUser>, response: Response<ValuesUser>) {
if (response.isSuccessful) { if (response.isSuccessful) {
val firstname = response.body()?.firstname val firstname = response.body()?.firstname
val location = response.body()?.location
lbl_username.text = "Hello " + firstname lbl_username.text = "Hello " + firstname
println("Body: " + firstname) println("Body: " + firstname)
if (location?.latitude == null) {
Toast.makeText(this@MainActivity, "No geofence set for you", Toast.LENGTH_LONG)
.show()
} else {
initializeGeofence(location?.latitude, location?.longitude, location?.radius)
}
} else { } else {
println("Response not successful: ${response.code()}") println("Response not successful: ${response.code()}")
} }
} }
override fun onFailure(call: Call<ValuesUser>, t: Throwable) { override fun onFailure(call: Call<ValuesUser>, t: Throwable) {
println("Response 'whoami' failed") println("Response 'whoami' failed. " + t.message)
} }
}) })
} }
private fun initializeDropdown(accountNames: MutableList<String>) {
val spinner: Spinner = findViewById(R.id.account_spinner)
// Create an ArrayAdapter using the string array and a default spinner layout
val arrayAdapter = ArrayAdapter<String>(this, R.layout.spinner_layout, accountNames)
arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spinner.adapter = arrayAdapter
spinner.onItemSelectedListener = object: AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
if (!accountNames.get(0).equals("None")) {
accName = accounts.entries.get(position).name
display_description.setText(accounts.entries.get(position).description)
display_revenue.setText(accounts.entries.get(position).revenue.toString())
} else {
display_description.visibility = View.GONE
display_description_layout.visibility = View.GONE
display_revenue.visibility = View.GONE
display_revenue_layout.visibility = View.GONE
}
println("Selected: " + accountNames.get(position))
}
override fun onNothingSelected(parent: AdapterView<*>?) {
println("Nothing selected")
}
}
}
private fun initializeGeofence(lat: Double, long: Double, rad: Float) {
geofencingClient = LocationServices.getGeofencingClient(this)
geofence = Geofence.Builder().setRequestId("Test")
.setCircularRegion(lat, long, rad)
.setExpirationDuration(Geofence.NEVER_EXPIRE)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER or Geofence.GEOFENCE_TRANSITION_EXIT)
.build()
geofencingClient.addGeofences(getGeofencingRequest(), geofencePendingIntent)?.run {
addOnSuccessListener {
println("Geofence added with: latitude: $lat longitude: $long radius: $rad")
}
addOnFailureListener {
println("Error: " + it.stackTrace.forEach { println(it.toString()) })
}
}
}
private fun getGeofencingRequest(): GeofencingRequest { private fun getGeofencingRequest(): GeofencingRequest {
return GeofencingRequest.Builder().apply { return GeofencingRequest.Builder().apply {

View File

@ -0,0 +1,8 @@
package de.hft.geotracker.retrofit
import com.google.gson.annotations.SerializedName
class EmbeddedAccounts(accounts: ValuesTimetrackAccounts) {
@SerializedName("_embedded")
var accounts = accounts
}

View File

@ -4,6 +4,7 @@ import retrofit2.Call
import retrofit2.http.Body import retrofit2.http.Body
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.POST import retrofit2.http.POST
import retrofit2.http.Query
interface GeofenceService { interface GeofenceService {
@POST("/login") @POST("/login")
@ -11,4 +12,10 @@ interface GeofenceService {
@GET("whoami") @GET("whoami")
fun getUser(): Call<ValuesUser> fun getUser(): Call<ValuesUser>
@GET("accounts")
fun getAccounts(): Call<EmbeddedAccounts>
@GET("track")
fun triggerTracking(@Query("account") account: String): Call<ValuesTracking>
} }

View File

@ -0,0 +1,19 @@
package de.hft.geotracker.retrofit
import com.google.gson.annotations.SerializedName
class ValuesLocation(
latitude: Double,
longitude: Double,
radius: Int
) {
@SerializedName("latitude")
var latitude = latitude
@SerializedName("longitude")
var longitude = longitude
@SerializedName("radius")
var radius = radius.toFloat()
}

View File

@ -0,0 +1,8 @@
package de.hft.geotracker.retrofit
import com.google.gson.annotations.SerializedName
class ValuesTimetrackAccounts(entries: Array<ValuesTimetrackAccountsEntries>) {
@SerializedName("accounts")
var entries = entries
}

View File

@ -0,0 +1,19 @@
package de.hft.geotracker.retrofit
import com.google.gson.annotations.SerializedName
class ValuesTimetrackAccountsEntries(
revenue: Double,
name: String,
description: String
) {
@SerializedName("revenue")
var revenue = revenue
@SerializedName("name")
var name = name
@SerializedName("description")
var description = description
}

View File

@ -0,0 +1,30 @@
package de.hft.geotracker.retrofit
import com.google.gson.annotations.SerializedName
class ValuesTracking(
duration: Int,
start: String,
end: String,
account: String,
user: String,
type: String
) {
@SerializedName("duration")
var duration = duration
@SerializedName("startdate")
var startdate = start
@SerializedName("enddate")
var enddate = end
@SerializedName("account")
var account = account
@SerializedName("username")
var username = user
@SerializedName("type")
var type = type
}

View File

@ -7,7 +7,7 @@ class ValuesUser(
firstname: String, firstname: String,
lastname: String, lastname: String,
username: String, username: String,
location: String, location: ValuesLocation,
id: Int id: Int
) { ) {

View File

@ -55,16 +55,18 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginTop="@dimen/margin16"
android:layout_marginEnd="16dp"
android:fontFamily="@font/montserrat" android:fontFamily="@font/montserrat"
android:text="@string/timetrack_account" android:text="@string/timetrack_account"
android:textAppearance="@style/text_style" android:textAppearance="@style/text_style"
android:textColor="@color/logo_white" android:textColor="@color/logo_white"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="@id/account_spinner"
app:layout_constraintHorizontal_bias="0.0" app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appBarLayout" app:layout_constraintTop_toBottomOf="@+id/divider2"
app:layout_constraintVertical_bias="0.19" /> app:layout_constraintVertical_bias="0.0" />
<ToggleButton <ToggleButton
android:id="@+id/button_start_stop" android:id="@+id/button_start_stop"
@ -91,36 +93,109 @@
android:id="@+id/account_spinner" android:id="@+id/account_spinner"
android:layout_width="180dp" android:layout_width="180dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/margin16" android:layout_marginEnd="16dp"
android:background="@color/logo_white" android:background="@color/colorPrimary"
android:foreground="@android:drawable/arrow_down_float"
android:foregroundGravity="right|center_horizontal"
android:textAlignment="textEnd" android:textAlignment="textEnd"
app:layout_constraintBottom_toBottomOf="@+id/selected_acc" app:layout_constraintBottom_toBottomOf="@+id/selected_acc"
app:layout_constraintEnd_toEndOf="parent" /> app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/selected_acc" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/display_description_layout"
style="@style/LoginTextInputLayoutStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin16"
android:layout_marginTop="8dp"
android:layout_marginEnd="@dimen/margin16"
android:colorControlNormal="@color/logo_blue"
android:hint="Description"
android:textColorHint="@color/logo_white"
app:boxBackgroundColor="@color/common_google_signin_btn_text_dark_disabled"
app:boxBackgroundMode="outline"
app:boxCornerRadiusBottomEnd="0dp"
app:boxCornerRadiusBottomStart="0dp"
app:layout_constraintBottom_toTopOf="@+id/display_revenue_layout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/selected_acc"
app:layout_constraintVertical_bias="0.0"
app:layout_constraintVertical_chainStyle="packed">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/display_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:enabled="false"
android:inputType="textPersonName"
android:lineSpacingExtra="8sp"
android:textAppearance="@style/text_style"
android:textColor="@color/logo_white"
android:textColorHint="@color/logo_white"
android:textSize="14sp" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/display_revenue_layout"
style="@style/LoginTextInputLayoutStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin16"
android:layout_marginEnd="@dimen/margin16"
android:layout_marginBottom="16dp"
android:colorControlNormal="@color/logo_blue"
android:hint="Revenue"
android:textColorHint="@color/logo_white"
app:boxBackgroundColor="@color/common_google_signin_btn_text_dark_disabled"
app:boxBackgroundMode="outline"
app:boxCornerRadiusTopEnd="0dp"
app:boxCornerRadiusTopStart="0dp"
app:layout_constraintBottom_toTopOf="@+id/latitude"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/display_description_layout"
app:layout_constraintVertical_bias="0.0"
app:layout_constraintVertical_chainStyle="packed">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/display_revenue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:enabled="false"
android:inputType="textPersonName"
android:lineSpacingExtra="8sp"
android:textAppearance="@style/text_style"
android:textColor="@color/logo_white"
android:textColorHint="@color/logo_white"
android:textSize="14sp" />
</com.google.android.material.textfield.TextInputLayout>
<TextView <TextView
android:id="@+id/altitude" android:id="@+id/altitude"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="dummy" android:text="dummy"
android:textAppearance="@style/text_style" android:textAppearance="@style/text_style"
app:layout_constraintBottom_toTopOf="@+id/button_start_stop"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent" />
app:layout_constraintTop_toBottomOf="@+id/longitude" />
<TextView <TextView
android:id="@+id/latitude" android:id="@+id/latitude"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginTop="32dp" android:layout_marginTop="16dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:text="dummy" android:text="dummy"
android:textAppearance="@style/text_style" android:textAppearance="@style/text_style"
android:textColor="@color/logo_white" android:textColor="@color/logo_white"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/selected_acc" /> app:layout_constraintTop_toBottomOf="@+id/display_revenue_layout" />
<TextView <TextView
android:id="@+id/longitude" android:id="@+id/longitude"

View File

@ -1,12 +1,14 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.textfield.TextInputLayout style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox.ExposedDropdownMenu" <com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox.ExposedDropdownMenu"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="42dp" android:layout_height="42dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:layout_marginBottom="100dp" android:layout_marginBottom="100dp"
android:hint="@string/no_account" android:hint="@string/no_account"
xmlns:android="http://schemas.android.com/apk/res/android"> xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<AutoCompleteTextView <AutoCompleteTextView
android:id="@+id/filled_exposed_dropdown" android:id="@+id/filled_exposed_dropdown"