123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- /*
- * Nextcloud Talk application
- *
- * @author Marcel Hibbe
- * Copyright (C) 2021 Marcel Hibbe <dev@mhibbe.de>
- *
- * 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.controllers
- import android.app.SearchManager
- import android.content.Context
- import android.os.Build
- import android.os.Bundle
- import android.text.InputType
- import android.util.Log
- import android.view.Menu
- import android.view.MenuInflater
- import android.view.MenuItem
- import android.view.View
- import android.view.inputmethod.EditorInfo
- import android.widget.AdapterView
- import android.widget.Toast
- import androidx.appcompat.widget.SearchView
- import androidx.core.view.MenuItemCompat
- import androidx.preference.PreferenceManager
- import autodagger.AutoInjector
- import com.nextcloud.talk.R
- import com.nextcloud.talk.adapters.GeocodingAdapter
- import com.nextcloud.talk.api.NcApi
- import com.nextcloud.talk.application.NextcloudTalkApplication
- import com.nextcloud.talk.controllers.base.BaseController
- import com.nextcloud.talk.controllers.util.viewBinding
- import com.nextcloud.talk.databinding.ControllerGeocodingBinding
- import com.nextcloud.talk.utils.bundle.BundleKeys
- import fr.dudie.nominatim.client.TalkJsonNominatimClient
- 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 org.osmdroid.config.Configuration
- import javax.inject.Inject
- @AutoInjector(NextcloudTalkApplication::class)
- class GeocodingController(args: Bundle) :
- BaseController(
- R.layout.controller_geocoding,
- args
- ),
- SearchView.OnQueryTextListener {
- private val binding: ControllerGeocodingBinding by viewBinding(ControllerGeocodingBinding::bind)
- @Inject
- lateinit var ncApi: NcApi
- @Inject
- lateinit var okHttpClient: OkHttpClient
- var roomToken: String?
- var nominatimClient: TalkJsonNominatimClient? = null
- var searchItem: MenuItem? = null
- var searchView: SearchView? = null
- var query: String? = null
- lateinit var adapter: GeocodingAdapter
- private var geocodingResults: List<Address> = ArrayList()
- constructor(args: Bundle, listener: LocationPickerController) : this(args) {
- targetController = listener
- }
- init {
- setHasOptionsMenu(true)
- NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
- Configuration.getInstance().load(context, PreferenceManager.getDefaultSharedPreferences(context))
- query = args.getString(BundleKeys.KEY_GEOCODING_QUERY)
- roomToken = args.getString(BundleKeys.KEY_ROOM_TOKEN)
- }
- private fun initAdapter(addresses: List<Address>) {
- adapter = GeocodingAdapter(binding.geocodingResults.context!!, addresses)
- binding.geocodingResults.adapter = adapter
- }
- override fun onAttach(view: View) {
- super.onAttach(view)
- initAdapter(geocodingResults)
- initGeocoder()
- if (!query.isNullOrEmpty()) {
- searchLocation()
- } else {
- Log.e(TAG, "search string that was passed to GeocodingController was null or empty")
- }
- binding.geocodingResults.onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id ->
- val address: Address = adapter.getItem(position) as Address
- val listener: GeocodingResultListener? = targetController as GeocodingResultListener?
- listener?.receiveChosenGeocodingResult(address.latitude, address.longitude, address.displayName)
- router.popCurrentController()
- }
- }
- override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
- super.onCreateOptionsMenu(menu, inflater)
- inflater.inflate(R.menu.menu_geocoding, menu)
- searchItem = menu.findItem(R.id.geocoding_action_search)
- initSearchView()
- searchItem?.expandActionView()
- searchView?.setQuery(query, false)
- searchView?.clearFocus()
- }
- 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() {
- if (activity != null) {
- val searchManager = activity!!.getSystemService(Context.SEARCH_SERVICE) as SearchManager
- if (searchItem != null) {
- searchView = MenuItemCompat.getActionView(searchItem) as SearchView
- searchView?.maxWidth = Int.MAX_VALUE
- searchView?.inputType = InputType.TYPE_TEXT_VARIATION_FILTER
- var imeOptions = EditorInfo.IME_ACTION_DONE or EditorInfo.IME_FLAG_NO_FULLSCREEN
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && appPreferences!!.isKeyboardIncognito) {
- imeOptions = imeOptions or EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING
- }
- searchView?.imeOptions = imeOptions
- searchView?.queryHint = resources!!.getString(R.string.nc_search)
- searchView?.setSearchableInfo(searchManager.getSearchableInfo(activity!!.componentName))
- searchView?.setOnQueryTextListener(this)
- searchItem?.setOnActionExpandListener(object : MenuItem.OnActionExpandListener {
- override fun onMenuItemActionExpand(menuItem: MenuItem): Boolean {
- return true
- }
- override fun onMenuItemActionCollapse(menuItem: MenuItem): Boolean {
- router.popCurrentController()
- return true
- }
- })
- }
- }
- }
- private fun initGeocoder() {
- val baseUrl = context!!.getString(R.string.osm_geocoder_url)
- val email = context!!.getString(R.string.osm_geocoder_contact)
- 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)
- Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
- }
- updateResultsOnMainThread(results)
- }
- private suspend fun updateResultsOnMainThread(results: ArrayList<Address>) {
- withContext(Main) {
- initAdapter(results)
- }
- }
- interface GeocodingResultListener {
- fun receiveChosenGeocodingResult(lat: Double, lon: Double, name: String)
- }
- companion object {
- private const val TAG = "GeocodingController"
- }
- }
|