Compare commits

...

2 Commits

Author SHA1 Message Date
Danila Fedorin 7b53546126 Add collar overlay to map 2020-02-16 17:40:30 -08:00
Danila Fedorin cfae237d17 Add a basic map view to app 2020-02-16 16:36:25 -08:00
5 changed files with 99 additions and 25 deletions

View File

@ -23,6 +23,10 @@ android {
}
}
repositories {
mavenCentral()
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
@ -35,4 +39,6 @@ dependencies {
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'org.osmdroid:osmdroid-android:6.1.5'
implementation "androidx.preference:preference-ktx:1.1.0"
}

View File

@ -3,6 +3,8 @@
package="com.danilafe.fencelessgrazing">
<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
android:allowBackup="true"

View File

@ -2,28 +2,103 @@ package com.danilafe.fencelessgrazing
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
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() {
private lateinit var token: String
// 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 queue: RequestQueue
// The OpenStreetMap map.
private lateinit var map: MapView
private val collarOverlays: MutableMap<Int, Marker> = mutableMapOf()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Configuration.getInstance().load(applicationContext,
PreferenceManager.getDefaultSharedPreferences(applicationContext))
setContentView(R.layout.activity_collar_list)
val collarList: RecyclerView = findViewById(R.id.collarSummaryList)
val layoutManager = LinearLayoutManager(collarList.context)
token = intent.getStringExtra("token")!!
summaryAdapter = CollarSummaryAdapter(collarList.context, getString(R.string.apiUrl), token)
summaryAdapter = CollarSummaryAdapter(summaries)
map = findViewById(R.id.map)
map.setTileSource(TileSourceFactory.MAPNIK)
queue = Volley.newRequestQueue(this)
collarList.adapter = summaryAdapter
collarList.layoutManager = layoutManager
collarList.addItemDecoration(DividerItemDecoration(collarList.context, layoutManager.orientation))
summaryAdapter.triggerRefresh()
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)
}
}
}
}

View File

@ -12,9 +12,7 @@ import com.danilafe.fencelessgrazing.model.CollarSummary
import com.danilafe.fencelessgrazing.requests.CollarRequest
class CollarSummaryAdapter(
private val context: Context,
private val baseUrl: String,
private val token: String
private val items : List<CollarSummary>
) : ListAdapter<CollarSummary, CollarViewHolder>(DiffCallback()) {
class DiffCallback : DiffUtil.ItemCallback<CollarSummary>() {
@ -25,9 +23,6 @@ class CollarSummaryAdapter(
= oldItem == newItem
}
private val requestQueue = Volley.newRequestQueue(context)
private val items = mutableListOf<CollarSummary>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CollarViewHolder {
val layout = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
return CollarViewHolder(layout)
@ -43,18 +38,5 @@ class CollarSummaryAdapter(
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)
}
}

View File

@ -6,11 +6,20 @@
android:layout_height="match_parent"
tools:context=".CollarListActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/collarSummaryList"
<org.osmdroid.views.MapView
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="wrap_content"
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
android:id="@+id/collarSummaryList"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/map"
tools:layout_editor_absoluteX="0dp" />
</androidx.constraintlayout.widget.ConstraintLayout>