mirror of
https://github.com/nextcloud/talk-android
synced 2025-06-19 19:49:33 +01:00
Merge pull request #3349 from nextcloud/LIveData
use MVVM for Geocoding
This commit is contained in:
commit
f85aea157c
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
package com.nextcloud.talk.adapters
|
package com.nextcloud.talk.adapters
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
@ -29,13 +30,19 @@ import androidx.recyclerview.widget.RecyclerView
|
|||||||
import com.nextcloud.talk.R
|
import com.nextcloud.talk.R
|
||||||
import fr.dudie.nominatim.model.Address
|
import fr.dudie.nominatim.model.Address
|
||||||
|
|
||||||
class GeocodingAdapter(private val context: Context, private val dataSource: List<Address>) :
|
class GeocodingAdapter(private val context: Context, private var dataSource: List<Address>) :
|
||||||
RecyclerView.Adapter<GeocodingAdapter.ViewHolder>() {
|
RecyclerView.Adapter<GeocodingAdapter.ViewHolder>() {
|
||||||
|
|
||||||
interface OnItemClickListener {
|
interface OnItemClickListener {
|
||||||
fun onItemClick(position: Int)
|
fun onItemClick(position: Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
fun updateData(data: List<Address>) {
|
||||||
|
this.dataSource = data
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
private var listener: OnItemClickListener? = null
|
private var listener: OnItemClickListener? = null
|
||||||
fun setOnItemClickListener(listener: OnItemClickListener) {
|
fun setOnItemClickListener(listener: OnItemClickListener) {
|
||||||
this.listener = listener
|
this.listener = listener
|
||||||
|
@ -32,12 +32,11 @@ import android.view.Menu
|
|||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.core.view.MenuItemCompat
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import autodagger.AutoInjector
|
import autodagger.AutoInjector
|
||||||
import com.google.android.material.snackbar.Snackbar
|
|
||||||
import com.nextcloud.talk.R
|
import com.nextcloud.talk.R
|
||||||
import com.nextcloud.talk.activities.BaseActivity
|
import com.nextcloud.talk.activities.BaseActivity
|
||||||
import com.nextcloud.talk.adapters.GeocodingAdapter
|
import com.nextcloud.talk.adapters.GeocodingAdapter
|
||||||
@ -45,21 +44,16 @@ import com.nextcloud.talk.api.NcApi
|
|||||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||||
import com.nextcloud.talk.databinding.ActivityGeocodingBinding
|
import com.nextcloud.talk.databinding.ActivityGeocodingBinding
|
||||||
import com.nextcloud.talk.utils.bundle.BundleKeys
|
import com.nextcloud.talk.utils.bundle.BundleKeys
|
||||||
|
import com.nextcloud.talk.viewmodels.GeoCodingViewModel
|
||||||
import fr.dudie.nominatim.client.TalkJsonNominatimClient
|
import fr.dudie.nominatim.client.TalkJsonNominatimClient
|
||||||
import fr.dudie.nominatim.model.Address
|
import fr.dudie.nominatim.model.Address
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers.IO
|
|
||||||
import kotlinx.coroutines.Dispatchers.Main
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import org.osmdroid.config.Configuration
|
import org.osmdroid.config.Configuration
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AutoInjector(NextcloudTalkApplication::class)
|
@AutoInjector(NextcloudTalkApplication::class)
|
||||||
class GeocodingActivity :
|
class GeocodingActivity :
|
||||||
BaseActivity(),
|
BaseActivity() {
|
||||||
SearchView.OnQueryTextListener {
|
|
||||||
|
|
||||||
private lateinit var binding: ActivityGeocodingBinding
|
private lateinit var binding: ActivityGeocodingBinding
|
||||||
|
|
||||||
@ -70,15 +64,15 @@ class GeocodingActivity :
|
|||||||
lateinit var okHttpClient: OkHttpClient
|
lateinit var okHttpClient: OkHttpClient
|
||||||
|
|
||||||
lateinit var roomToken: String
|
lateinit var roomToken: String
|
||||||
var nominatimClient: TalkJsonNominatimClient? = null
|
private var nominatimClient: TalkJsonNominatimClient? = null
|
||||||
|
|
||||||
var searchItem: MenuItem? = null
|
private var searchItem: MenuItem? = null
|
||||||
var searchView: SearchView? = null
|
var searchView: SearchView? = null
|
||||||
var query: String? = null
|
|
||||||
|
|
||||||
lateinit var adapter: GeocodingAdapter
|
lateinit var adapter: GeocodingAdapter
|
||||||
private var geocodingResults: List<Address> = ArrayList()
|
private var geocodingResults: List<Address> = ArrayList()
|
||||||
private lateinit var recyclerView: RecyclerView
|
private lateinit var recyclerView: RecyclerView
|
||||||
|
private lateinit var viewModel: GeoCodingViewModel
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@ -92,11 +86,27 @@ class GeocodingActivity :
|
|||||||
Configuration.getInstance().load(context, PreferenceManager.getDefaultSharedPreferences(context))
|
Configuration.getInstance().load(context, PreferenceManager.getDefaultSharedPreferences(context))
|
||||||
|
|
||||||
roomToken = intent.getStringExtra(BundleKeys.KEY_ROOM_TOKEN)!!
|
roomToken = intent.getStringExtra(BundleKeys.KEY_ROOM_TOKEN)!!
|
||||||
query = intent.getStringExtra(BundleKeys.KEY_GEOCODING_QUERY)
|
|
||||||
recyclerView = findViewById(R.id.geocoding_results)
|
recyclerView = findViewById(R.id.geocoding_results)
|
||||||
recyclerView.layoutManager = LinearLayoutManager(this)
|
recyclerView.layoutManager = LinearLayoutManager(this)
|
||||||
adapter = GeocodingAdapter(this, geocodingResults)
|
adapter = GeocodingAdapter(this, geocodingResults)
|
||||||
recyclerView.adapter = adapter
|
recyclerView.adapter = adapter
|
||||||
|
viewModel = ViewModelProvider(this)[GeoCodingViewModel::class.java]
|
||||||
|
|
||||||
|
var query = viewModel.getQuery()
|
||||||
|
if (query.isEmpty() && intent.hasExtra(BundleKeys.KEY_GEOCODING_QUERY)) {
|
||||||
|
query = intent.getStringExtra(BundleKeys.KEY_GEOCODING_QUERY).orEmpty()
|
||||||
|
viewModel.setQuery(query)
|
||||||
|
}
|
||||||
|
val savedResults = viewModel.getGeocodingResults()
|
||||||
|
initAdapter(savedResults)
|
||||||
|
viewModel.getGeocodingResultsLiveData().observe(this) { results ->
|
||||||
|
geocodingResults = results
|
||||||
|
adapter.updateData(results)
|
||||||
|
}
|
||||||
|
val baseUrl = getString(R.string.osm_geocoder_url)
|
||||||
|
val email = context.getString(R.string.osm_geocoder_contact)
|
||||||
|
nominatimClient = TalkJsonNominatimClient(baseUrl, okHttpClient, email)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
@ -108,12 +118,11 @@ class GeocodingActivity :
|
|||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
|
||||||
if (!query.isNullOrEmpty()) {
|
if (viewModel.getQuery().isNotEmpty() && adapter.itemCount == 0) {
|
||||||
searchLocation()
|
viewModel.searchLocation()
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG, "search string that was passed to GeocodingController was null or empty")
|
Log.e(TAG, "search string that was passed to GeocodingController was null or empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
adapter.setOnItemClickListener(object : GeocodingAdapter.OnItemClickListener {
|
adapter.setOnItemClickListener(object : GeocodingAdapter.OnItemClickListener {
|
||||||
override fun onItemClick(position: Int) {
|
override fun onItemClick(position: Int) {
|
||||||
val address: Address = adapter.getItem(position) as Address
|
val address: Address = adapter.getItem(position) as Address
|
||||||
@ -125,6 +134,7 @@ class GeocodingActivity :
|
|||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
searchView?.setQuery(viewModel.getQuery(), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupActionBar() {
|
private fun setupActionBar() {
|
||||||
@ -160,38 +170,43 @@ class GeocodingActivity :
|
|||||||
menuInflater.inflate(R.menu.menu_geocoding, menu)
|
menuInflater.inflate(R.menu.menu_geocoding, menu)
|
||||||
searchItem = menu.findItem(R.id.geocoding_action_search)
|
searchItem = menu.findItem(R.id.geocoding_action_search)
|
||||||
initSearchView()
|
initSearchView()
|
||||||
|
|
||||||
searchItem?.expandActionView()
|
searchItem?.expandActionView()
|
||||||
searchView?.setQuery(query, false)
|
searchView?.setQuery(viewModel.getQuery(), false)
|
||||||
searchView?.clearFocus()
|
searchView?.clearFocus()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onQueryTextSubmit(query: String?): Boolean {
|
|
||||||
this.query = query
|
|
||||||
searchLocation()
|
|
||||||
searchView?.clearFocus()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onQueryTextChange(newText: String?): Boolean {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initSearchView() {
|
private fun initSearchView() {
|
||||||
val searchManager = getSystemService(Context.SEARCH_SERVICE) as SearchManager
|
val searchManager = getSystemService(Context.SEARCH_SERVICE) as SearchManager
|
||||||
if (searchItem != null) {
|
if (searchItem != null) {
|
||||||
searchView = MenuItemCompat.getActionView(searchItem) as SearchView
|
searchView = searchItem!!.actionView as SearchView?
|
||||||
|
|
||||||
searchView?.maxWidth = Int.MAX_VALUE
|
searchView?.maxWidth = Int.MAX_VALUE
|
||||||
searchView?.inputType = InputType.TYPE_TEXT_VARIATION_FILTER
|
searchView?.inputType = InputType.TYPE_TEXT_VARIATION_FILTER
|
||||||
var imeOptions = EditorInfo.IME_ACTION_DONE or EditorInfo.IME_FLAG_NO_FULLSCREEN
|
var imeOptions = EditorInfo.IME_ACTION_DONE or EditorInfo.IME_FLAG_NO_FULLSCREEN
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && appPreferences!!.isKeyboardIncognito) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && appPreferences.isKeyboardIncognito) {
|
||||||
imeOptions = imeOptions or EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING
|
imeOptions = imeOptions or EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING
|
||||||
}
|
}
|
||||||
searchView?.imeOptions = imeOptions
|
searchView?.imeOptions = imeOptions
|
||||||
searchView?.queryHint = resources!!.getString(R.string.nc_search)
|
searchView?.queryHint = resources!!.getString(R.string.nc_search)
|
||||||
searchView?.setSearchableInfo(searchManager.getSearchableInfo(componentName))
|
searchView?.setSearchableInfo(searchManager.getSearchableInfo(componentName))
|
||||||
searchView?.setOnQueryTextListener(this)
|
searchView?.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
||||||
|
override fun onQueryTextSubmit(query: String): Boolean {
|
||||||
|
viewModel.setQuery(query)
|
||||||
|
viewModel.searchLocation()
|
||||||
|
searchView?.clearFocus()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onQueryTextChange(query: String): Boolean {
|
||||||
|
// This is a workaround to not set viewModel data when onQueryTextChange is triggered on startup
|
||||||
|
// Otherwise it would be set to an empty string.
|
||||||
|
if (searchView?.width!! > 0) {
|
||||||
|
viewModel.setQuery(query)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
searchItem?.setOnActionExpandListener(object : MenuItem.OnActionExpandListener {
|
searchItem?.setOnActionExpandListener(object : MenuItem.OnActionExpandListener {
|
||||||
override fun onMenuItemActionExpand(menuItem: MenuItem): Boolean {
|
override fun onMenuItemActionExpand(menuItem: MenuItem): Boolean {
|
||||||
@ -215,37 +230,7 @@ class GeocodingActivity :
|
|||||||
nominatimClient = TalkJsonNominatimClient(baseUrl, okHttpClient, email)
|
nominatimClient = TalkJsonNominatimClient(baseUrl, okHttpClient, email)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun searchLocation(): Boolean {
|
|
||||||
CoroutineScope(IO).launch {
|
|
||||||
executeGeocodingRequest()
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("Detekt.TooGenericExceptionCaught")
|
|
||||||
private suspend fun executeGeocodingRequest() {
|
|
||||||
var results: ArrayList<Address> = ArrayList()
|
|
||||||
try {
|
|
||||||
results = nominatimClient!!.search(query) as ArrayList<Address>
|
|
||||||
for (address in results) {
|
|
||||||
Log.d(TAG, address.displayName)
|
|
||||||
Log.d(TAG, address.latitude.toString())
|
|
||||||
Log.d(TAG, address.longitude.toString())
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(TAG, "Failed to get geocoded addresses", e)
|
|
||||||
Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
updateResultsOnMainThread(results)
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun updateResultsOnMainThread(results: ArrayList<Address>) {
|
|
||||||
withContext(Main) {
|
|
||||||
initAdapter(results)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val TAG = GeocodingActivity::class.java.simpleName
|
val TAG = GeocodingActivity::class.java.simpleName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Nextcloud Talk application
|
||||||
|
*
|
||||||
|
* @author Samanwith KSN
|
||||||
|
* Copyright (C) 2023 Samanwith KSN <samanwith21@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.talk.viewmodels
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.nextcloud.talk.activities.CallActivity.Companion.TAG
|
||||||
|
import com.nextcloud.talk.location.GeocodingActivity
|
||||||
|
import fr.dudie.nominatim.client.TalkJsonNominatimClient
|
||||||
|
import fr.dudie.nominatim.model.Address
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
class GeoCodingViewModel : ViewModel() {
|
||||||
|
private val geocodingResultsLiveData = MutableLiveData<List<Address>>()
|
||||||
|
private val nominatimClient: TalkJsonNominatimClient
|
||||||
|
private val okHttpClient: OkHttpClient = OkHttpClient.Builder().build()
|
||||||
|
private var geocodingResults: List<Address> = ArrayList()
|
||||||
|
private var query: String = ""
|
||||||
|
fun getGeocodingResultsLiveData(): LiveData<List<Address>> {
|
||||||
|
return geocodingResultsLiveData
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getQuery(): String {
|
||||||
|
return query
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setQuery(query: String) {
|
||||||
|
this.query = query
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getGeocodingResults(): List<Address> {
|
||||||
|
return geocodingResults
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
nominatimClient = TalkJsonNominatimClient(
|
||||||
|
"https://nominatim.openstreetmap.org/",
|
||||||
|
okHttpClient,
|
||||||
|
" android@nextcloud.com"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun searchLocation() {
|
||||||
|
if (query.isNotEmpty()) {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
try {
|
||||||
|
val results = nominatimClient.search(query) as ArrayList<Address>
|
||||||
|
for (address in results) {
|
||||||
|
Log.d(GeocodingActivity.TAG, address.displayName)
|
||||||
|
Log.d(GeocodingActivity.TAG, address.latitude.toString())
|
||||||
|
Log.d(GeocodingActivity.TAG, address.longitude.toString())
|
||||||
|
}
|
||||||
|
geocodingResults = results
|
||||||
|
geocodingResultsLiveData.postValue(results)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
Log.e(TAG, "Failed to get geocoded addresses", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user