From 55f0bec0b4b9c6468604e190402f709be2e90ade Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Tue, 12 May 2020 22:07:08 -0700 Subject: [PATCH] Create distance traveled graph. --- .../fencelessgrazing/CollarDetailActivity.kt | 4 +- .../fencelessgrazing/CollarListActivity.kt | 13 ++-- .../fencelessgrazing/DistanceTraveledGraph.kt | 72 +++++++++++++++++++ .../danilafe/fencelessgrazing/MainActivity.kt | 9 +-- .../fencelessgrazing/StatisticsActivity.kt | 10 ++- .../StatisticsGraphAdapter.kt | 10 +++ .../fencelessgrazing/model/CollarDistance.kt | 3 + .../requests/DistanceTraveledRequest.kt | 25 +++++++ .../main/res/layout/activity_collar_list.xml | 21 +++++- .../res/layout/distance_traveled_layoyt.xml | 19 +++++ app/src/main/res/values/strings.xml | 1 + 11 files changed, 174 insertions(+), 13 deletions(-) create mode 100644 app/src/main/java/com/danilafe/fencelessgrazing/DistanceTraveledGraph.kt create mode 100644 app/src/main/java/com/danilafe/fencelessgrazing/StatisticsGraphAdapter.kt create mode 100644 app/src/main/java/com/danilafe/fencelessgrazing/model/CollarDistance.kt create mode 100644 app/src/main/java/com/danilafe/fencelessgrazing/requests/DistanceTraveledRequest.kt create mode 100644 app/src/main/res/layout/distance_traveled_layoyt.xml diff --git a/app/src/main/java/com/danilafe/fencelessgrazing/CollarDetailActivity.kt b/app/src/main/java/com/danilafe/fencelessgrazing/CollarDetailActivity.kt index 7366874..bbf9934 100644 --- a/app/src/main/java/com/danilafe/fencelessgrazing/CollarDetailActivity.kt +++ b/app/src/main/java/com/danilafe/fencelessgrazing/CollarDetailActivity.kt @@ -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) diff --git a/app/src/main/java/com/danilafe/fencelessgrazing/CollarListActivity.kt b/app/src/main/java/com/danilafe/fencelessgrazing/CollarListActivity.kt index 0a5e8b9..1fa30ab 100644 --- a/app/src/main/java/com/danilafe/fencelessgrazing/CollarListActivity.kt +++ b/app/src/main/java/com/danilafe/fencelessgrazing/CollarListActivity.kt @@ -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 = 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() } + } diff --git a/app/src/main/java/com/danilafe/fencelessgrazing/DistanceTraveledGraph.kt b/app/src/main/java/com/danilafe/fencelessgrazing/DistanceTraveledGraph.kt new file mode 100644 index 0000000..5e7bb47 --- /dev/null +++ b/app/src/main/java/com/danilafe/fencelessgrazing/DistanceTraveledGraph.kt @@ -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() + 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) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/danilafe/fencelessgrazing/MainActivity.kt b/app/src/main/java/com/danilafe/fencelessgrazing/MainActivity.kt index 3cf4cf3..e9fc8b9 100644 --- a/app/src/main/java/com/danilafe/fencelessgrazing/MainActivity.kt +++ b/app/src/main/java/com/danilafe/fencelessgrazing/MainActivity.kt @@ -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 { diff --git a/app/src/main/java/com/danilafe/fencelessgrazing/StatisticsActivity.kt b/app/src/main/java/com/danilafe/fencelessgrazing/StatisticsActivity.kt index 1769e1e..60e5f4c 100644 --- a/app/src/main/java/com/danilafe/fencelessgrazing/StatisticsActivity.kt +++ b/app/src/main/java/com/danilafe/fencelessgrazing/StatisticsActivity.kt @@ -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() } } diff --git a/app/src/main/java/com/danilafe/fencelessgrazing/StatisticsGraphAdapter.kt b/app/src/main/java/com/danilafe/fencelessgrazing/StatisticsGraphAdapter.kt new file mode 100644 index 0000000..67b016f --- /dev/null +++ b/app/src/main/java/com/danilafe/fencelessgrazing/StatisticsGraphAdapter.kt @@ -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() +} \ No newline at end of file diff --git a/app/src/main/java/com/danilafe/fencelessgrazing/model/CollarDistance.kt b/app/src/main/java/com/danilafe/fencelessgrazing/model/CollarDistance.kt new file mode 100644 index 0000000..8b034a1 --- /dev/null +++ b/app/src/main/java/com/danilafe/fencelessgrazing/model/CollarDistance.kt @@ -0,0 +1,3 @@ +package com.danilafe.fencelessgrazing.model + +data class CollarDistance(val name: String, val id: Int, val distance: Float) \ No newline at end of file diff --git a/app/src/main/java/com/danilafe/fencelessgrazing/requests/DistanceTraveledRequest.kt b/app/src/main/java/com/danilafe/fencelessgrazing/requests/DistanceTraveledRequest.kt new file mode 100644 index 0000000..18905af --- /dev/null +++ b/app/src/main/java/com/danilafe/fencelessgrazing/requests/DistanceTraveledRequest.kt @@ -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>, + error: Response.ErrorListener +) : StringRequest( + Method.GET, "${baseUrl}/collars/stats/distance", + GsonListener( + object : TypeToken>() {}.type, + listener + ), error +) { + + override fun getHeaders(): MutableMap { + return mutableMapOf("Authorization" to "Bearer $token") + } + +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_collar_list.xml b/app/src/main/res/layout/activity_collar_list.xml index 1ff917e..cb599ce 100644 --- a/app/src/main/res/layout/activity_collar_list.xml +++ b/app/src/main/res/layout/activity_collar_list.xml @@ -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" /> + + + +