Compare commits
No commits in common. "7b535461268c942a9c28d23d5da3234c16f0555f" and "5b0bb1e862c5736ed9f22d0dcf0404d8c24f4245" have entirely different histories.
7b53546126
...
5b0bb1e862
@ -23,10 +23,6 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
@ -39,6 +35,4 @@ dependencies {
|
|||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
||||||
implementation 'org.osmdroid:osmdroid-android:6.1.5'
|
|
||||||
implementation "androidx.preference:preference-ktx:1.1.0"
|
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
package="com.danilafe.fencelessgrazing">
|
package="com.danilafe.fencelessgrazing">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
@ -2,103 +2,28 @@ package com.danilafe.fencelessgrazing
|
|||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.preference.PreferenceManager
|
|
||||||
import androidx.recyclerview.widget.DividerItemDecoration
|
import androidx.recyclerview.widget.DividerItemDecoration
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.android.volley.RequestQueue
|
|
||||||
import com.android.volley.Response
|
|
||||||
import com.android.volley.toolbox.Volley
|
|
||||||
import com.danilafe.fencelessgrazing.model.CollarSummary
|
|
||||||
import com.danilafe.fencelessgrazing.requests.CollarRequest
|
|
||||||
import org.osmdroid.config.Configuration
|
|
||||||
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
|
|
||||||
import org.osmdroid.util.GeoPoint
|
|
||||||
import org.osmdroid.views.MapView
|
|
||||||
import org.osmdroid.views.overlay.Marker
|
|
||||||
|
|
||||||
class CollarListActivity : AppCompatActivity() {
|
class CollarListActivity : AppCompatActivity() {
|
||||||
|
|
||||||
// The list of collar summaries and its list adapter.
|
|
||||||
private val summaries : MutableList<CollarSummary> = mutableListOf()
|
|
||||||
private lateinit var summaryAdapter: CollarSummaryAdapter
|
|
||||||
|
|
||||||
// The API token and request queue.
|
|
||||||
private lateinit var token: String
|
private lateinit var token: String
|
||||||
private lateinit var queue: RequestQueue
|
private lateinit var summaryAdapter: CollarSummaryAdapter
|
||||||
|
|
||||||
// The OpenStreetMap map.
|
|
||||||
private lateinit var map: MapView
|
|
||||||
private val collarOverlays: MutableMap<Int, Marker> = mutableMapOf()
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
Configuration.getInstance().load(applicationContext,
|
|
||||||
PreferenceManager.getDefaultSharedPreferences(applicationContext))
|
|
||||||
|
|
||||||
setContentView(R.layout.activity_collar_list)
|
setContentView(R.layout.activity_collar_list)
|
||||||
|
|
||||||
val collarList: RecyclerView = findViewById(R.id.collarSummaryList)
|
val collarList: RecyclerView = findViewById(R.id.collarSummaryList)
|
||||||
val layoutManager = LinearLayoutManager(collarList.context)
|
val layoutManager = LinearLayoutManager(collarList.context)
|
||||||
token = intent.getStringExtra("token")!!
|
token = intent.getStringExtra("token")!!
|
||||||
summaryAdapter = CollarSummaryAdapter(summaries)
|
summaryAdapter = CollarSummaryAdapter(collarList.context, getString(R.string.apiUrl), token)
|
||||||
map = findViewById(R.id.map)
|
|
||||||
map.setTileSource(TileSourceFactory.MAPNIK)
|
|
||||||
queue = Volley.newRequestQueue(this)
|
|
||||||
|
|
||||||
collarList.adapter = summaryAdapter
|
collarList.adapter = summaryAdapter
|
||||||
collarList.layoutManager = layoutManager
|
collarList.layoutManager = layoutManager
|
||||||
collarList.addItemDecoration(DividerItemDecoration(collarList.context, layoutManager.orientation))
|
collarList.addItemDecoration(DividerItemDecoration(collarList.context, layoutManager.orientation))
|
||||||
triggerRefresh()
|
summaryAdapter.triggerRefresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
|
||||||
super.onResume()
|
|
||||||
map.onResume()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPause() {
|
|
||||||
super.onPause()
|
|
||||||
map.onPause()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun triggerRefresh() {
|
|
||||||
val request = CollarRequest(getString(R.string.apiUrl), token,
|
|
||||||
Response.Listener {
|
|
||||||
summaries.clear()
|
|
||||||
summaries.addAll(it)
|
|
||||||
summaryAdapter.notifyDataSetChanged()
|
|
||||||
updateMapOverlay()
|
|
||||||
},
|
|
||||||
Response.ErrorListener {
|
|
||||||
Toast.makeText(this, "Failed to retrieve collar list!", Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
queue.add(request)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateMapOverlay() {
|
|
||||||
val currentSet = mutableSetOf<Int>()
|
|
||||||
summaries.forEach {
|
|
||||||
// Create or update overlay
|
|
||||||
val overlay = collarOverlays[it.id] ?: Marker(map)
|
|
||||||
overlay.title = it.name
|
|
||||||
overlay.position = GeoPoint(it.pos.longitude.toDouble(), it.pos.latitude.toDouble())
|
|
||||||
|
|
||||||
// Store new / existing overlay.
|
|
||||||
if(!collarOverlays.containsKey(it.id)) map.overlays.add(overlay)
|
|
||||||
collarOverlays[it.id] = overlay
|
|
||||||
currentSet.add(it.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
val previousSet = collarOverlays.keys
|
|
||||||
previousSet.forEach {
|
|
||||||
if(!currentSet.contains(it)) {
|
|
||||||
map.overlays.remove(collarOverlays[it])
|
|
||||||
collarOverlays.remove(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,9 @@ import com.danilafe.fencelessgrazing.model.CollarSummary
|
|||||||
import com.danilafe.fencelessgrazing.requests.CollarRequest
|
import com.danilafe.fencelessgrazing.requests.CollarRequest
|
||||||
|
|
||||||
class CollarSummaryAdapter(
|
class CollarSummaryAdapter(
|
||||||
private val items : List<CollarSummary>
|
private val context: Context,
|
||||||
|
private val baseUrl: String,
|
||||||
|
private val token: String
|
||||||
) : ListAdapter<CollarSummary, CollarViewHolder>(DiffCallback()) {
|
) : ListAdapter<CollarSummary, CollarViewHolder>(DiffCallback()) {
|
||||||
|
|
||||||
class DiffCallback : DiffUtil.ItemCallback<CollarSummary>() {
|
class DiffCallback : DiffUtil.ItemCallback<CollarSummary>() {
|
||||||
@ -23,6 +25,9 @@ class CollarSummaryAdapter(
|
|||||||
= oldItem == newItem
|
= oldItem == newItem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val requestQueue = Volley.newRequestQueue(context)
|
||||||
|
private val items = mutableListOf<CollarSummary>()
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CollarViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CollarViewHolder {
|
||||||
val layout = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
|
val layout = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
|
||||||
return CollarViewHolder(layout)
|
return CollarViewHolder(layout)
|
||||||
@ -38,5 +43,18 @@ class CollarSummaryAdapter(
|
|||||||
|
|
||||||
override fun getItemCount(): Int = items.size
|
override fun getItemCount(): Int = items.size
|
||||||
|
|
||||||
|
fun triggerRefresh() {
|
||||||
|
val request = CollarRequest(baseUrl, token,
|
||||||
|
Response.Listener {
|
||||||
|
items.clear()
|
||||||
|
items.addAll(it)
|
||||||
|
notifyDataSetChanged()
|
||||||
|
},
|
||||||
|
Response.ErrorListener {
|
||||||
|
Toast.makeText(context, "Failed to retrieve collar list!", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
requestQueue.add(request)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -6,20 +6,11 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".CollarListActivity">
|
tools:context=".CollarListActivity">
|
||||||
|
|
||||||
<org.osmdroid.views.MapView
|
|
||||||
android:id="@+id/map"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
app:layout_constraintDimensionRatio="16:9"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/collarSummaryList"
|
android:id="@+id/collarSummaryList"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/map"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
tools:layout_editor_absoluteX="0dp" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
Loading…
Reference in New Issue
Block a user