diff --git a/android/.idea/jarRepositories.xml b/android/.idea/jarRepositories.xml
new file mode 100644
index 0000000..a5f05cd
--- /dev/null
+++ b/android/.idea/jarRepositories.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/.idea/misc.xml b/android/.idea/misc.xml
index b6ea2b1..7bfef59 100644
--- a/android/.idea/misc.xml
+++ b/android/.idea/misc.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 6dbdb6b..41e998a 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -37,9 +37,10 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
- implementation 'androidx.core:core-ktx:1.2.0'
+ implementation 'androidx.core:core-ktx:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
+ implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'com.google.android.material:material:1.1.0'
implementation "com.google.android.gms:play-services-location:17.0.0"
implementation 'androidx.preference:preference:1.1.1'
diff --git a/android/app/src/main/java/de/hft/geotracker/RecordsAdapter.kt b/android/app/src/main/java/de/hft/geotracker/RecordsAdapter.kt
new file mode 100644
index 0000000..1df65dc
--- /dev/null
+++ b/android/app/src/main/java/de/hft/geotracker/RecordsAdapter.kt
@@ -0,0 +1,44 @@
+package de.hft.geotracker
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.cardview.widget.CardView
+import androidx.recyclerview.widget.RecyclerView
+import de.hft.geotracker.activities.RecordEntry
+
+class RecordsAdapter : RecyclerView.Adapter() {
+ var data = listOf()
+ set(value) {
+ field = value
+ notifyDataSetChanged()
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TextItemViewHolder {
+ val layoutInflater = LayoutInflater.from(parent.context)
+ val view = layoutInflater
+ .inflate(R.layout.text_item_view, parent, false) as CardView
+ return TextItemViewHolder(view)
+ }
+
+ override fun getItemCount(): Int {
+ return data.size
+ }
+
+ override fun onBindViewHolder(holder: TextItemViewHolder, position: Int) {
+ val item = data[position]
+ holder.textFrom.setText("Start: " + item.from)
+ holder.textTo.setText("End: " + item.to)
+ if (item.duration != -1) {
+ holder.textTotal.setText("Duration: " + item.duration)
+ }
+
+ }
+
+}
+
+class TextItemViewHolder(textView: CardView): RecyclerView.ViewHolder(textView) {
+ val textFrom = itemView.findViewById(R.id.recyclerText_from)
+ val textTo = itemView.findViewById(R.id.recyclerText_to)
+ val textTotal = itemView.findViewById(R.id.recyclerText_total)
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/de/hft/geotracker/activities/Login.kt b/android/app/src/main/java/de/hft/geotracker/activities/Login.kt
index e1e02a0..895ce8f 100644
--- a/android/app/src/main/java/de/hft/geotracker/activities/Login.kt
+++ b/android/app/src/main/java/de/hft/geotracker/activities/Login.kt
@@ -54,7 +54,7 @@ class Login : AppCompatActivity() {
.build()
service = retrofit.create(GeofenceService::class.java)
- login = findViewById(R.id.button_create_account)
+ login = findViewById(R.id.button_login)
login.setOnClickListener {
intent = Intent(this, MainActivity::class.java)
login()
diff --git a/android/app/src/main/java/de/hft/geotracker/activities/MainActivity.kt b/android/app/src/main/java/de/hft/geotracker/activities/MainActivity.kt
index ccaf3bf..ddb55c9 100644
--- a/android/app/src/main/java/de/hft/geotracker/activities/MainActivity.kt
+++ b/android/app/src/main/java/de/hft/geotracker/activities/MainActivity.kt
@@ -1,8 +1,12 @@
package de.hft.geotracker.activities
+import android.Manifest
+import android.Manifest.permission.ACCESS_COARSE_LOCATION
import android.Manifest.permission.ACCESS_FINE_LOCATION
+import android.app.AlertDialog
import android.app.PendingIntent
import android.content.Context
+import android.content.DialogInterface
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
@@ -12,12 +16,14 @@ import android.widget.*
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.app.ActivityCompat.requestPermissions
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
import com.google.android.gms.location.*
import de.hft.geotracker.GeofenceBroadcastReceiver
import de.hft.geotracker.R
+import de.hft.geotracker.RecordsAdapter
import de.hft.geotracker.retrofit.*
import kotlinx.android.synthetic.main.activity_home.*
-import kotlinx.android.synthetic.main.dropdown_menu.*
import okhttp3.OkHttpClient
import retrofit2.Call
import retrofit2.Callback
@@ -34,6 +40,7 @@ class MainActivity : AppCompatActivity() {
lateinit var actionButton: TextView
var running = false
var accName: String? = null
+ var workingSince: String? = null
lateinit var accounts: ValuesTimetrackAccounts
lateinit var service: GeofenceService
lateinit var locationRequest: LocationRequest
@@ -65,19 +72,20 @@ class MainActivity : AppCompatActivity() {
?.apply()
getSharedPreferences("LOCATION", Context.MODE_PRIVATE)
.registerOnSharedPreferenceChangeListener { sharedPreferences, key ->
- val btnState = sharedPreferences.getBoolean("ENABLED", false)
+ val isInside = sharedPreferences.getBoolean("ENABLED", false)
- println("Buttonstate: $btnState")
- if (btnState) {
+ println("Is inside? -> $isInside")
+ if (isInside) {
+ button_start_stop?.text = getString(R.string.start)
button_start_stop?.setBackgroundColor(resources.getColor(R.color.logo_blue))
- } else {
+ } else {
button_start_stop?.setBackgroundColor(resources.getColor(R.color.colorPrimaryDark))
if (running) {
- button_start_stop.toggle()
callStartStop()
}
+ button_start_stop?.text = getString(R.string.outside_place)
}
- button_start_stop.isEnabled = btnState
+ button_start_stop.isEnabled = isInside
}
//JWToken lesen
@@ -102,37 +110,35 @@ class MainActivity : AppCompatActivity() {
val retrofit = builder.build()
service = retrofit.create(GeofenceService::class.java)
showUsername()
-
- //Get Timetrack Accounts
- val accountNames = mutableListOf()
-// accountNames.add("None")
- val call = service.getAccounts()
- call.enqueue(object: Callback {
- override fun onResponse(
- call: Call,
- response: Response
- ) {
- if (response.isSuccessful) {
- accounts = response.body()!!.accounts
- accounts.entries.forEach {
- accountNames.add(it.name + "")
- }
- initializeDropdown(accountNames)
- }
- }
- override fun onFailure(call: Call, t: Throwable) {
- accountNames.add("None")
- initializeDropdown(accountNames)
- Toast.makeText(this@MainActivity, "You dont have any Timetrack Accounts ", Toast.LENGTH_LONG)
- .show()
- }
- })
+ updateRecyclerView()
actionButton = findViewById(R.id.button_start_stop)
+ actionButton.setBackgroundColor(resources.getColor(R.color.colorPrimaryDark))
actionButton.setOnClickListener {
- callStartStop()
+ if (running) {
+ val builder: AlertDialog.Builder = AlertDialog.Builder(this)
+ builder.setTitle(R.string.app_name)
+ builder.setMessage("Do you want to stop?")
+ builder.setIcon(R.drawable.ic_logo)
+ builder.setPositiveButton("Yes", object : DialogInterface.OnClickListener {
+ override fun onClick(dialog: DialogInterface, id: Int) {
+ callStartStop()
+ dialog.dismiss()
+ }
+ })
+ builder.setNegativeButton("No", object : DialogInterface.OnClickListener {
+ override fun onClick(dialog: DialogInterface, id: Int) {
+ dialog.dismiss()
+ }
+ })
+ val alert: AlertDialog = builder.create()
+ alert.show()
+ } else {
+ callStartStop()
+ }
}
+ //Toolbar listener
my_toolbar.setOnMenuItemClickListener { menuItem ->
when (menuItem.itemId) {
R.id.settings -> {
@@ -141,6 +147,9 @@ class MainActivity : AppCompatActivity() {
true
}
R.id.logout -> {
+ if (running) {
+ callStartStop()
+ }
deleteFile("JWToken")
startActivity(Intent(this, Login::class.java))
println("Logout pressed")
@@ -149,30 +158,76 @@ class MainActivity : AppCompatActivity() {
else -> false
}
}
+
+
+
+ }
+
+ private fun updateRecyclerView() {
+ //Recycler View
+ val recView: RecyclerView = records_list
+ recView.setHasFixedSize(true)
+ val layoutManager: RecyclerView.LayoutManager = LinearLayoutManager(this)
+ val adapter = RecordsAdapter()
+ val recordList = ArrayList()
+
+ val call = service.getTodaysRecords()
+ call.enqueue(object: Callback {
+ override fun onResponse(call: Call, response: Response) {
+ if (response.isSuccessful) {
+ val entries = response.body()!!.records.entries
+ if (!entries.isEmpty()) {
+ entries.forEach {
+ recordList.add(RecordEntry(it.startdate.substring(11, 16)
+ , it.enddate.substring(11, 16)
+ , it.duration))
+ }
+ if (running) {
+ recordList.add(RecordEntry(workingSince!!, "PENDING", -1))
+ }
+ adapter.data = recordList
+ recView.layoutManager = layoutManager
+ recView.adapter = adapter
+ } else {
+ println("No Records!")
+ }
+
+ } else {
+ println("Response for todays records was not successful")
+ }
+ }
+ override fun onFailure(call: Call, t: Throwable) {
+ println("Getting todays records failed")
+ }
+
+ })
}
private fun callStartStop() {
running = !running
if (running) {
account_spinner.visibility = View.GONE
+ button_start_stop?.text = getString(R.string.stop)
} else {
account_spinner.visibility = View.VISIBLE
+ button_start_stop?.text = getString(R.string.start)
}
if (!accName.isNullOrEmpty()) {
val call = service.triggerTracking(accName!!)
call.enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
- latitude.text = response.body()?.startdate
- longitude.text = response.body()?.enddate
+ workingSince = response.body()?.startdate?.substring(11, 16)
+ updateRecyclerView()
println("Tracking event successful!")
}
override fun onFailure(call: Call, t: Throwable) {
println("Problem at start tracking: " + t.message)
}
})
+ } else {
+ println("Accounts list is emty")
}
println("StartStop pressed: $running")
- //ToDO call /track Endpoint
}
private fun showUsername() {
@@ -182,13 +237,16 @@ class MainActivity : AppCompatActivity() {
if (response.isSuccessful) {
val firstname = response.body()?.firstname
val location = response.body()?.location
+ val username = response.body()?.username
+ getTimetrackAccounts(username!!)
lbl_username.text = "Hello " + firstname
- println("Body: " + firstname)
if (location?.latitude == null) {
+ button_start_stop?.text = "No geofence set for you"
+ button_start_stop?.setBackgroundColor(resources.getColor(R.color.colorPrimaryDark))
Toast.makeText(this@MainActivity, "No geofence set for you", Toast.LENGTH_LONG)
.show()
} else {
- initializeGeofence(location?.latitude, location?.longitude, location?.radius)
+ initializeGeofence(location.latitude, location.longitude, location.radius)
}
} else {
println("Response not successful: ${response.code()}")
@@ -199,6 +257,41 @@ class MainActivity : AppCompatActivity() {
println("Response 'whoami' failed. " + t.message)
}
})
+
+
+
+ }
+ private fun getTimetrackAccounts(user: String) {
+ val accountNames = mutableListOf()
+// accountNames.add("None")
+ val call = service.getAccounts(user)
+ call.enqueue(object: Callback {
+ override fun onResponse(
+ call: Call,
+ response: Response
+ ) {
+ if (response.isSuccessful) {
+ accounts = response.body()!!.accounts
+ if (!accounts.entries.isEmpty()) {
+ accounts.entries.forEach {
+ accountNames.add(it.name + "")
+ }
+ } else {
+ accountNames.add("None")
+ initializeDropdown(accountNames)
+ display_description.setText("You dont have any Timetrack Accounts")
+ Toast.makeText(this@MainActivity, "You dont have any Timetrack Accounts", Toast.LENGTH_LONG)
+ .show()
+ }
+
+ initializeDropdown(accountNames)
+ println("Dropdown initialized")
+ }
+ }
+ override fun onFailure(call: Call, t: Throwable) {
+ println("Failed to get accounts")
+ }
+ })
}
private fun initializeDropdown(accountNames: MutableList) {
val spinner: Spinner = findViewById(R.id.account_spinner)
@@ -218,8 +311,6 @@ class MainActivity : AppCompatActivity() {
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
}
@@ -237,6 +328,13 @@ class MainActivity : AppCompatActivity() {
.setExpirationDuration(Geofence.NEVER_EXPIRE)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER or Geofence.GEOFENCE_TRANSITION_EXIT)
.build()
+ if (ActivityCompat.checkSelfPermission(
+ this,
+ ACCESS_FINE_LOCATION
+ ) != PackageManager.PERMISSION_GRANTED
+ ) {
+ requestPermissions(this, arrayOf(ACCESS_FINE_LOCATION), 1000)
+ }
geofencingClient.addGeofences(getGeofencingRequest(), geofencePendingIntent)?.run {
addOnSuccessListener {
println("Geofence added with: latitude: $lat longitude: $long radius: $rad")
@@ -266,6 +364,16 @@ class MainActivity : AppCompatActivity() {
startLocationUpdates()
}
private fun startLocationUpdates() {
+ if (ActivityCompat.checkSelfPermission(
+ this,
+ ACCESS_FINE_LOCATION
+ ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
+ this,
+ Manifest.permission.ACCESS_COARSE_LOCATION
+ ) != PackageManager.PERMISSION_GRANTED
+ ) {
+ requestPermissions(this, arrayOf(ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION), 1000)
+ }
fusedLocationClient.requestLocationUpdates(
locationRequest,
locationCallback,
@@ -285,3 +393,11 @@ class MainActivity : AppCompatActivity() {
}
}
+
+class RecordEntry(from: String, to: String, duration: Int) {
+ val from = from
+ val to = to
+ val duration = duration
+}
+
+
diff --git a/android/app/src/main/java/de/hft/geotracker/activities/Register.kt b/android/app/src/main/java/de/hft/geotracker/activities/Register.kt
index faa0ffd..6fbfd19 100644
--- a/android/app/src/main/java/de/hft/geotracker/activities/Register.kt
+++ b/android/app/src/main/java/de/hft/geotracker/activities/Register.kt
@@ -3,6 +3,7 @@ package de.hft.geotracker.activities
import android.content.Intent
import android.os.Bundle
import android.widget.TextView
+import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import de.hft.geotracker.R
@@ -11,14 +12,15 @@ class Register : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_register)
- reg = findViewById(R.id.button_create_account)
+ reg = findViewById(R.id.button_login)
reg.setOnClickListener {
createAccount()
}
}
private fun createAccount() {
- startActivity(Intent(this, MainActivity::class.java))
+ Toast.makeText(this@Register, "Not yet implemented!", Toast.LENGTH_LONG)
+ .show()
}
diff --git a/android/app/src/main/java/de/hft/geotracker/activities/Settings.kt b/android/app/src/main/java/de/hft/geotracker/activities/Settings.kt
index 16e2f70..438e020 100644
--- a/android/app/src/main/java/de/hft/geotracker/activities/Settings.kt
+++ b/android/app/src/main/java/de/hft/geotracker/activities/Settings.kt
@@ -1,6 +1,8 @@
package de.hft.geotracker.activities
import android.os.Bundle
+import android.widget.TextView
+import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import de.hft.geotracker.R
import kotlinx.android.synthetic.main.activity_home.*
@@ -9,10 +11,14 @@ class Settings : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setContentView(R.layout.settings_activity)
+ setContentView(R.layout.activity_settings)
my_toolbar.setNavigationOnClickListener {
onBackPressed()
}
+ findViewById(R.id.button_submit).setOnClickListener {
+ Toast.makeText(this@Settings, "Not yet implemented!", Toast.LENGTH_LONG)
+ .show()
+ }
}
}
\ No newline at end of file
diff --git a/android/app/src/main/java/de/hft/geotracker/retrofit/EmbeddedRecords.kt b/android/app/src/main/java/de/hft/geotracker/retrofit/EmbeddedRecords.kt
new file mode 100644
index 0000000..503d782
--- /dev/null
+++ b/android/app/src/main/java/de/hft/geotracker/retrofit/EmbeddedRecords.kt
@@ -0,0 +1,8 @@
+package de.hft.geotracker.retrofit
+
+import com.google.gson.annotations.SerializedName
+
+class EmbeddedRecords(records: ValuesRecordsArray) {
+ @SerializedName("_embedded")
+ var records = records
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/de/hft/geotracker/retrofit/GeofenceService.kt b/android/app/src/main/java/de/hft/geotracker/retrofit/GeofenceService.kt
index d437f99..9839b51 100644
--- a/android/app/src/main/java/de/hft/geotracker/retrofit/GeofenceService.kt
+++ b/android/app/src/main/java/de/hft/geotracker/retrofit/GeofenceService.kt
@@ -1,10 +1,7 @@
package de.hft.geotracker.retrofit
import retrofit2.Call
-import retrofit2.http.Body
-import retrofit2.http.GET
-import retrofit2.http.POST
-import retrofit2.http.Query
+import retrofit2.http.*
interface GeofenceService {
@POST("/login")
@@ -13,9 +10,12 @@ interface GeofenceService {
@GET("whoami")
fun getUser(): Call
- @GET("accounts")
- fun getAccounts(): Call
+ @GET("accounts/search/findByUsername")
+ fun getAccounts(@Query("username") username : String): Call
@GET("track")
fun triggerTracking(@Query("account") account: String): Call
+
+ @GET("records/search/today")
+ fun getTodaysRecords(): Call
}
\ No newline at end of file
diff --git a/android/app/src/main/java/de/hft/geotracker/retrofit/ValuesRecordEntry.kt b/android/app/src/main/java/de/hft/geotracker/retrofit/ValuesRecordEntry.kt
new file mode 100644
index 0000000..8e9848b
--- /dev/null
+++ b/android/app/src/main/java/de/hft/geotracker/retrofit/ValuesRecordEntry.kt
@@ -0,0 +1,22 @@
+package de.hft.geotracker.retrofit
+
+import com.google.gson.annotations.SerializedName
+
+class ValuesRecordEntry(
+ start: String,
+ end: String,
+ type: String,
+ duration: Int
+) {
+ @SerializedName("startdate")
+ var startdate = start
+
+ @SerializedName("enddate")
+ var enddate = end
+
+ @SerializedName("type")
+ var type = type
+
+ @SerializedName("duration")
+ var duration = duration
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/de/hft/geotracker/retrofit/ValuesRecordsArray.kt b/android/app/src/main/java/de/hft/geotracker/retrofit/ValuesRecordsArray.kt
new file mode 100644
index 0000000..2339bc4
--- /dev/null
+++ b/android/app/src/main/java/de/hft/geotracker/retrofit/ValuesRecordsArray.kt
@@ -0,0 +1,8 @@
+package de.hft.geotracker.retrofit
+
+import com.google.gson.annotations.SerializedName
+
+class ValuesRecordsArray(entries: Array) {
+ @SerializedName("records")
+ var entries = entries
+}
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/activity_home.xml b/android/app/src/main/res/layout/activity_home.xml
index f267e14..933c2d7 100644
--- a/android/app/src/main/res/layout/activity_home.xml
+++ b/android/app/src/main/res/layout/activity_home.xml
@@ -68,23 +68,20 @@
app:layout_constraintTop_toBottomOf="@+id/divider2"
app:layout_constraintVertical_bias="0.0" />
-
@@ -99,8 +96,7 @@
android:foregroundGravity="right|center_horizontal"
android:textAlignment="textEnd"
app:layout_constraintBottom_toBottomOf="@+id/selected_acc"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toEndOf="@+id/selected_acc" />
+ app:layout_constraintEnd_toEndOf="parent" />
-
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/text_today" />
-
-
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/activity_login.xml b/android/app/src/main/res/layout/activity_login.xml
index d496f1a..ca2e95f 100644
--- a/android/app/src/main/res/layout/activity_login.xml
+++ b/android/app/src/main/res/layout/activity_login.xml
@@ -63,7 +63,7 @@
android:textColorHint="@color/logo_white"
app:boxBackgroundColor="@color/common_google_signin_btn_text_dark_disabled"
app:boxBackgroundMode="outline"
- app:layout_constraintBottom_toTopOf="@+id/button_create_account"
+ app:layout_constraintBottom_toTopOf="@+id/button_login"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
@@ -81,7 +81,7 @@
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/activity_register.xml b/android/app/src/main/res/layout/activity_register.xml
index b5a5689..88ce1d6 100644
--- a/android/app/src/main/res/layout/activity_register.xml
+++ b/android/app/src/main/res/layout/activity_register.xml
@@ -33,7 +33,7 @@
android:hint="@string/confirm_password"
android:inputType="textPassword"
android:textAlignment="center"
- app:layout_constraintBottom_toTopOf="@+id/button_create_account"
+ app:layout_constraintBottom_toTopOf="@+id/button_login"
app:layout_constraintEnd_toEndOf="@+id/input_email_register"
app:layout_constraintTop_toBottomOf="@+id/input_password_layout" />
@@ -87,7 +87,7 @@
app:layout_constraintTop_toBottomOf="@+id/input_email_register" />