Create distance traveled graph.
This commit is contained in:
		
							parent
							
								
									f6098e9ed4
								
							
						
					
					
						commit
						55f0bec0b4
					
				@ -59,9 +59,9 @@ class CollarDetailActivity : AppCompatActivity() {
 | 
			
		||||
        collarPos = findViewById(R.id.collarPos)
 | 
			
		||||
        collarStimulus = findViewById(R.id.collarStimulus)
 | 
			
		||||
 | 
			
		||||
        token = intent.getStringExtra("token")!!
 | 
			
		||||
        token = getSharedPreferences("FencelessGrazing", 0).getString("token", null)!!
 | 
			
		||||
        collarId = intent.getIntExtra("identifier", -1)
 | 
			
		||||
        queue = Volley.newRequestQueue(this)
 | 
			
		||||
        queue = Volley.newRequestQueue(applicationContext)
 | 
			
		||||
 | 
			
		||||
        map = findViewById(R.id.detailMap)
 | 
			
		||||
        map.controller.setZoom(9.5)
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,8 @@ package com.danilafe.fencelessgrazing
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import androidx.appcompat.app.AppCompatActivity
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.view.View
 | 
			
		||||
import android.widget.Button
 | 
			
		||||
import android.widget.Toast
 | 
			
		||||
import androidx.preference.PreferenceManager
 | 
			
		||||
import androidx.recyclerview.widget.DividerItemDecoration
 | 
			
		||||
@ -22,7 +24,6 @@ import java.util.*
 | 
			
		||||
import kotlin.concurrent.timerTask
 | 
			
		||||
 | 
			
		||||
class CollarListActivity : AppCompatActivity() {
 | 
			
		||||
 | 
			
		||||
    // The list of collar summaries and its list adapter.
 | 
			
		||||
    private val summaries : MutableList<CollarSummary> = mutableListOf()
 | 
			
		||||
    private lateinit var summaryAdapter: CollarSummaryAdapter
 | 
			
		||||
@ -49,10 +50,10 @@ class CollarListActivity : AppCompatActivity() {
 | 
			
		||||
        setContentView(R.layout.activity_collar_list)
 | 
			
		||||
        findViews()
 | 
			
		||||
 | 
			
		||||
        token = intent.getStringExtra("token")!!
 | 
			
		||||
        token = getSharedPreferences("FencelessGrazing", 0).getString("token", null)!!
 | 
			
		||||
        map.setTileSource(TileSourceFactory.MAPNIK)
 | 
			
		||||
        map.controller.setZoom(9.5)
 | 
			
		||||
        queue = Volley.newRequestQueue(this)
 | 
			
		||||
        queue = Volley.newRequestQueue(applicationContext)
 | 
			
		||||
 | 
			
		||||
        setupCollarList()
 | 
			
		||||
    }
 | 
			
		||||
@ -71,6 +72,10 @@ class CollarListActivity : AppCompatActivity() {
 | 
			
		||||
        refreshTimer.purge()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun openStatistics(view: View) {
 | 
			
		||||
        startActivity(Intent(this, StatisticsActivity::class.java))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun triggerRefresh() {
 | 
			
		||||
        val request = CollarRequest(getString(R.string.apiUrl), token,
 | 
			
		||||
            Response.Listener {
 | 
			
		||||
@ -93,7 +98,6 @@ class CollarListActivity : AppCompatActivity() {
 | 
			
		||||
 | 
			
		||||
    private fun startCollarDetailActivity(collar: CollarSummary) {
 | 
			
		||||
        val newIntent = Intent(this, CollarDetailActivity::class.java).apply {
 | 
			
		||||
            putExtra("token", token)
 | 
			
		||||
            putExtra("identifier", collar.id)
 | 
			
		||||
        }
 | 
			
		||||
        startActivity(newIntent)
 | 
			
		||||
@ -150,4 +154,5 @@ class CollarListActivity : AppCompatActivity() {
 | 
			
		||||
 | 
			
		||||
        map.invalidate()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -15,7 +15,6 @@ class MainActivity : AppCompatActivity() {
 | 
			
		||||
    override fun onCreate(savedInstanceState: Bundle?) {
 | 
			
		||||
        super.onCreate(savedInstanceState)
 | 
			
		||||
        setContentView(R.layout.activity_main)
 | 
			
		||||
        startActivity(Intent(this, StatisticsActivity::class.java))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun attemptLogin(view: View) {
 | 
			
		||||
@ -27,9 +26,11 @@ class MainActivity : AppCompatActivity() {
 | 
			
		||||
            LoginRequest(getString(R.string.apiUrl),
 | 
			
		||||
                usernameField.text.toString(), passwordField.text.toString(),
 | 
			
		||||
                Response.Listener {
 | 
			
		||||
                    val newIntent = Intent(this, CollarListActivity::class.java).apply {
 | 
			
		||||
                        putExtra("token", it?.token)
 | 
			
		||||
                    }
 | 
			
		||||
                    val editor = getSharedPreferences("FencelessGrazing", 0).edit()
 | 
			
		||||
                    editor.putString("token", it.token)
 | 
			
		||||
                    editor.apply()
 | 
			
		||||
 | 
			
		||||
                    val newIntent = Intent(this, CollarListActivity::class.java)
 | 
			
		||||
                    startActivity(newIntent)
 | 
			
		||||
                },
 | 
			
		||||
                Response.ErrorListener {
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@ package com.danilafe.fencelessgrazing
 | 
			
		||||
 | 
			
		||||
import androidx.appcompat.app.AppCompatActivity
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import androidx.fragment.app.FragmentActivity
 | 
			
		||||
import androidx.viewpager.widget.ViewPager
 | 
			
		||||
import androidx.viewpager2.widget.ViewPager2
 | 
			
		||||
import com.google.android.material.tabs.TabLayout
 | 
			
		||||
@ -12,6 +13,10 @@ class StatisticsActivity : AppCompatActivity() {
 | 
			
		||||
    private lateinit var tabLayout: TabLayout
 | 
			
		||||
    private lateinit var viewPager: ViewPager2
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        val tabNames = arrayOf("Distance Traveled")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onCreate(savedInstanceState: Bundle?) {
 | 
			
		||||
        super.onCreate(savedInstanceState)
 | 
			
		||||
        setContentView(R.layout.activity_statistics)
 | 
			
		||||
@ -19,8 +24,9 @@ class StatisticsActivity : AppCompatActivity() {
 | 
			
		||||
        tabLayout = findViewById(R.id.statisticsTabs)
 | 
			
		||||
        viewPager = findViewById(R.id.statisticsPager)
 | 
			
		||||
 | 
			
		||||
        viewPager.adapter = StatisticsGraphAdapter(this)
 | 
			
		||||
        TabLayoutMediator(tabLayout, viewPager) { tab, position ->
 | 
			
		||||
            tab.text = "Tab ${position + 1}"
 | 
			
		||||
        }
 | 
			
		||||
            tab.text = tabNames[position]
 | 
			
		||||
        }.attach()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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()
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,3 @@
 | 
			
		||||
package com.danilafe.fencelessgrazing.model
 | 
			
		||||
 | 
			
		||||
data class CollarDistance(val name: String, val id: Int, val distance: Float)
 | 
			
		||||
@ -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")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -20,6 +20,25 @@
 | 
			
		||||
        android:layout_width="match_parent"
 | 
			
		||||
        android:layout_height="0dp"
 | 
			
		||||
        app:layout_constraintBottom_toBottomOf="parent"
 | 
			
		||||
        app:layout_constraintTop_toBottomOf="@id/map"
 | 
			
		||||
        app:layout_constraintTop_toBottomOf="@+id/linearLayout"
 | 
			
		||||
        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>
 | 
			
		||||
							
								
								
									
										19
									
								
								app/src/main/res/layout/distance_traveled_layoyt.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								app/src/main/res/layout/distance_traveled_layoyt.xml
									
									
									
									
									
										Normal 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>
 | 
			
		||||
@ -7,4 +7,5 @@
 | 
			
		||||
    <string name="collarSummaryLocation">Currently at %1f, %2f</string>
 | 
			
		||||
    <string name="collarSummaryStimulus">Required %1d stimuli in the last 24 hours</string>
 | 
			
		||||
    <string name="distanceTraveled">Distance Traveled</string>
 | 
			
		||||
    <string name="viewStatistics">View Statistics</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user