Create distance traveled graph.

This commit is contained in:
Danila Fedorin 2020-05-12 22:07:08 -07:00
parent f6098e9ed4
commit 55f0bec0b4
11 changed files with 174 additions and 13 deletions

View File

@ -59,9 +59,9 @@ class CollarDetailActivity : AppCompatActivity() {
collarPos = findViewById(R.id.collarPos) collarPos = findViewById(R.id.collarPos)
collarStimulus = findViewById(R.id.collarStimulus) collarStimulus = findViewById(R.id.collarStimulus)
token = intent.getStringExtra("token")!! token = getSharedPreferences("FencelessGrazing", 0).getString("token", null)!!
collarId = intent.getIntExtra("identifier", -1) collarId = intent.getIntExtra("identifier", -1)
queue = Volley.newRequestQueue(this) queue = Volley.newRequestQueue(applicationContext)
map = findViewById(R.id.detailMap) map = findViewById(R.id.detailMap)
map.controller.setZoom(9.5) map.controller.setZoom(9.5)

View File

@ -3,6 +3,8 @@ package com.danilafe.fencelessgrazing
import android.content.Intent import android.content.Intent
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.Toast import android.widget.Toast
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
@ -22,7 +24,6 @@ import java.util.*
import kotlin.concurrent.timerTask import kotlin.concurrent.timerTask
class CollarListActivity : AppCompatActivity() { class CollarListActivity : AppCompatActivity() {
// The list of collar summaries and its list adapter. // The list of collar summaries and its list adapter.
private val summaries : MutableList<CollarSummary> = mutableListOf() private val summaries : MutableList<CollarSummary> = mutableListOf()
private lateinit var summaryAdapter: CollarSummaryAdapter private lateinit var summaryAdapter: CollarSummaryAdapter
@ -49,10 +50,10 @@ class CollarListActivity : AppCompatActivity() {
setContentView(R.layout.activity_collar_list) setContentView(R.layout.activity_collar_list)
findViews() findViews()
token = intent.getStringExtra("token")!! token = getSharedPreferences("FencelessGrazing", 0).getString("token", null)!!
map.setTileSource(TileSourceFactory.MAPNIK) map.setTileSource(TileSourceFactory.MAPNIK)
map.controller.setZoom(9.5) map.controller.setZoom(9.5)
queue = Volley.newRequestQueue(this) queue = Volley.newRequestQueue(applicationContext)
setupCollarList() setupCollarList()
} }
@ -71,6 +72,10 @@ class CollarListActivity : AppCompatActivity() {
refreshTimer.purge() refreshTimer.purge()
} }
fun openStatistics(view: View) {
startActivity(Intent(this, StatisticsActivity::class.java))
}
private fun triggerRefresh() { private fun triggerRefresh() {
val request = CollarRequest(getString(R.string.apiUrl), token, val request = CollarRequest(getString(R.string.apiUrl), token,
Response.Listener { Response.Listener {
@ -93,7 +98,6 @@ class CollarListActivity : AppCompatActivity() {
private fun startCollarDetailActivity(collar: CollarSummary) { private fun startCollarDetailActivity(collar: CollarSummary) {
val newIntent = Intent(this, CollarDetailActivity::class.java).apply { val newIntent = Intent(this, CollarDetailActivity::class.java).apply {
putExtra("token", token)
putExtra("identifier", collar.id) putExtra("identifier", collar.id)
} }
startActivity(newIntent) startActivity(newIntent)
@ -150,4 +154,5 @@ class CollarListActivity : AppCompatActivity() {
map.invalidate() map.invalidate()
} }
} }

View File

@ -0,0 +1,72 @@
package com.danilafe.fencelessgrazing
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import com.android.volley.RequestQueue
import com.android.volley.Response
import com.android.volley.toolbox.Volley
import com.danilafe.fencelessgrazing.requests.DistanceTraveledRequest
import com.github.mikephil.charting.charts.BarChart
import com.github.mikephil.charting.data.BarData
import com.github.mikephil.charting.data.BarDataSet
import com.github.mikephil.charting.data.BarEntry
import com.github.mikephil.charting.formatter.IndexAxisValueFormatter
import com.github.mikephil.charting.utils.ColorTemplate
class DistanceTraveledGraph() : Fragment() {
private lateinit var distanceTraveledChart: BarChart
private lateinit var queue : RequestQueue
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.distance_traveled_layoyt, container, false)
distanceTraveledChart = view.findViewById(R.id.distanceTraveledChart)
return view
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
queue = Volley.newRequestQueue(requireActivity().applicationContext)
triggerRefresh()
}
private fun triggerRefresh() {
val activity = requireActivity()
val sharedPrefs = activity.getSharedPreferences("FencelessGrazing", 0)
val token = sharedPrefs.getString("token", null)!!
val request = DistanceTraveledRequest(activity.getString(R.string.apiUrl), token,
Response.Listener {
val entries = mutableListOf<BarEntry>()
val labels = it.map { c -> c.name }
it.forEachIndexed { i, v ->
entries.add(BarEntry(i.toFloat(), v.distance))
}
val dataSet = BarDataSet(entries, "Distance Traveled")
dataSet.colors = ColorTemplate.VORDIPLOM_COLORS.toList()
val data = BarData(dataSet)
data.barWidth = 0.9f
distanceTraveledChart.legend.textSize = 20.0f
distanceTraveledChart.data = data
distanceTraveledChart.setFitBars(true)
distanceTraveledChart.xAxis.valueFormatter = IndexAxisValueFormatter(labels)
distanceTraveledChart.xAxis.granularity = 1.0f
distanceTraveledChart.xAxis.isGranularityEnabled = true
distanceTraveledChart.invalidate()
},
Response.ErrorListener {
Toast.makeText(activity, "Failed to retrieve distance traveled!", Toast.LENGTH_SHORT).show()
}
)
queue.add(request)
}
}

View File

@ -15,7 +15,6 @@ class MainActivity : AppCompatActivity() {
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)
startActivity(Intent(this, StatisticsActivity::class.java))
} }
fun attemptLogin(view: View) { fun attemptLogin(view: View) {
@ -27,9 +26,11 @@ class MainActivity : AppCompatActivity() {
LoginRequest(getString(R.string.apiUrl), LoginRequest(getString(R.string.apiUrl),
usernameField.text.toString(), passwordField.text.toString(), usernameField.text.toString(), passwordField.text.toString(),
Response.Listener { Response.Listener {
val newIntent = Intent(this, CollarListActivity::class.java).apply { val editor = getSharedPreferences("FencelessGrazing", 0).edit()
putExtra("token", it?.token) editor.putString("token", it.token)
} editor.apply()
val newIntent = Intent(this, CollarListActivity::class.java)
startActivity(newIntent) startActivity(newIntent)
}, },
Response.ErrorListener { Response.ErrorListener {

View File

@ -2,6 +2,7 @@ package com.danilafe.fencelessgrazing
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.FragmentActivity
import androidx.viewpager.widget.ViewPager import androidx.viewpager.widget.ViewPager
import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout
@ -12,6 +13,10 @@ class StatisticsActivity : AppCompatActivity() {
private lateinit var tabLayout: TabLayout private lateinit var tabLayout: TabLayout
private lateinit var viewPager: ViewPager2 private lateinit var viewPager: ViewPager2
companion object {
val tabNames = arrayOf("Distance Traveled")
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_statistics) setContentView(R.layout.activity_statistics)
@ -19,8 +24,9 @@ class StatisticsActivity : AppCompatActivity() {
tabLayout = findViewById(R.id.statisticsTabs) tabLayout = findViewById(R.id.statisticsTabs)
viewPager = findViewById(R.id.statisticsPager) viewPager = findViewById(R.id.statisticsPager)
viewPager.adapter = StatisticsGraphAdapter(this)
TabLayoutMediator(tabLayout, viewPager) { tab, position -> TabLayoutMediator(tabLayout, viewPager) { tab, position ->
tab.text = "Tab ${position + 1}" tab.text = tabNames[position]
} }.attach()
} }
} }

View File

@ -0,0 +1,10 @@
package com.danilafe.fencelessgrazing
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
class StatisticsGraphAdapter(activity : FragmentActivity) : FragmentStateAdapter(activity) {
override fun getItemCount(): Int = 1
override fun createFragment(position: Int): Fragment = DistanceTraveledGraph()
}

View File

@ -0,0 +1,3 @@
package com.danilafe.fencelessgrazing.model
data class CollarDistance(val name: String, val id: Int, val distance: Float)

View File

@ -0,0 +1,25 @@
package com.danilafe.fencelessgrazing.requests
import com.danilafe.fencelessgrazing.model.CollarDistance
import com.android.volley.Response
import com.android.volley.toolbox.StringRequest
import com.google.gson.reflect.TypeToken
class DistanceTraveledRequest(
baseUrl: String,
private val token : String,
listener: Response.Listener<List<CollarDistance>>,
error: Response.ErrorListener
) : StringRequest(
Method.GET, "${baseUrl}/collars/stats/distance",
GsonListener(
object : TypeToken<List<CollarDistance>>() {}.type,
listener
), error
) {
override fun getHeaders(): MutableMap<String, String> {
return mutableMapOf("Authorization" to "Bearer $token")
}
}

View File

@ -20,6 +20,25 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/map" app:layout_constraintTop_toBottomOf="@+id/linearLayout"
tools:layout_editor_absoluteX="0dp" /> tools:layout_editor_absoluteX="0dp" />
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_max="wrap"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/map">
<Button
android:id="@+id/statisticsButton"
style="@android:style/Widget.Material.Button.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="openStatistics"
android:text="@string/viewStatistics" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.github.mikephil.charting.charts.BarChart
android:id="@+id/distanceTraveledChart"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -7,4 +7,5 @@
<string name="collarSummaryLocation">Currently at %1f, %2f</string> <string name="collarSummaryLocation">Currently at %1f, %2f</string>
<string name="collarSummaryStimulus">Required %1d stimuli in the last 24 hours</string> <string name="collarSummaryStimulus">Required %1d stimuli in the last 24 hours</string>
<string name="distanceTraveled">Distance Traveled</string> <string name="distanceTraveled">Distance Traveled</string>
<string name="viewStatistics">View Statistics</string>
</resources> </resources>