Bug fixes
This commit is contained in:
parent
60f7532abf
commit
484d01fb83
|
@ -212,11 +212,7 @@ class UnifiedLocationClient private constructor(context: Context) {
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun requestLocationUpdates(listener: LocationListener, interval: Long, count: Int) {
|
fun requestLocationUpdates(listener: LocationListener, interval: Long, count: Int) {
|
||||||
for (request in requests) {
|
requests.removeAll(requests.filter { it.listener === listener })
|
||||||
if (request.listener === listener) {
|
|
||||||
requests.remove(request)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
requests.add(LocationRequest(listener, interval, count))
|
requests.add(LocationRequest(listener, interval, count))
|
||||||
updateServiceInterval()
|
updateServiceInterval()
|
||||||
updateBinding()
|
updateBinding()
|
||||||
|
@ -224,13 +220,7 @@ class UnifiedLocationClient private constructor(context: Context) {
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun removeLocationUpdates(listener: LocationListener) {
|
fun removeLocationUpdates(listener: LocationListener) {
|
||||||
for (request in requests) {
|
removeRequests(requests.filter { it.listener === listener })
|
||||||
if (request.listener === listener) {
|
|
||||||
requests.remove(request)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateServiceInterval()
|
|
||||||
updateBinding()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun refAndGetService(): UnifiedLocationService = suspendCoroutine { continuation -> refAndGetServiceContinued(continuation) }
|
private suspend fun refAndGetService(): UnifiedLocationService = suspendCoroutine { continuation -> refAndGetServiceContinued(continuation) }
|
||||||
|
@ -293,7 +283,7 @@ class UnifiedLocationClient private constructor(context: Context) {
|
||||||
service.getFromLocationWithOptions(latitude, longitude, maxResults, locale, options, AddressContinuation(continuation))
|
service.getFromLocationWithOptions(latitude, longitude, maxResults, locale, options, AddressContinuation(continuation))
|
||||||
configureContinuationTimeout(continuation, timeout)
|
configureContinuationTimeout(continuation, timeout)
|
||||||
}
|
}
|
||||||
} catch (e: RemoteException) {
|
} catch (e: Exception) {
|
||||||
Log.w(TAG, "Failed to request geocode", e)
|
Log.w(TAG, "Failed to request geocode", e)
|
||||||
return emptyList()
|
return emptyList()
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -368,7 +358,7 @@ class UnifiedLocationClient private constructor(context: Context) {
|
||||||
|
|
||||||
suspend fun setGeocoderBackends(backends: Array<String>) {
|
suspend fun setGeocoderBackends(backends: Array<String>) {
|
||||||
try {
|
try {
|
||||||
refAndGetService().locationBackends = backends
|
refAndGetService().geocoderBackends = backends
|
||||||
} catch (e: RemoteException) {
|
} catch (e: RemoteException) {
|
||||||
Log.w(TAG, "Failed to handle request", e)
|
Log.w(TAG, "Failed to handle request", e)
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -399,10 +389,16 @@ class UnifiedLocationClient private constructor(context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
private fun removeRequest(request: LocationRequest) {
|
private fun removeRequestPendingRemoval() {
|
||||||
requests.remove(request)
|
removeRequests(requests.filter { it.needsRemoval })
|
||||||
updateServiceInterval()
|
}
|
||||||
updateBinding()
|
|
||||||
|
private fun removeRequests(removalNeeded: List<LocationRequest>) {
|
||||||
|
if (removalNeeded.isNotEmpty()) {
|
||||||
|
requests.removeAll(removalNeeded)
|
||||||
|
updateServiceInterval()
|
||||||
|
updateBinding()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
|
@ -459,6 +455,7 @@ class UnifiedLocationClient private constructor(context: Context) {
|
||||||
for (request in requests) {
|
for (request in requests) {
|
||||||
request.handleLocation(location)
|
request.handleLocation(location)
|
||||||
}
|
}
|
||||||
|
removeRequestPendingRemoval()
|
||||||
}
|
}
|
||||||
}, options)
|
}, options)
|
||||||
updateServiceInterval()
|
updateServiceInterval()
|
||||||
|
@ -501,6 +498,10 @@ class UnifiedLocationClient private constructor(context: Context) {
|
||||||
private inner class LocationRequest(val listener: LocationListener, var interval: Long, var pendingCount: Int) {
|
private inner class LocationRequest(val listener: LocationListener, var interval: Long, var pendingCount: Int) {
|
||||||
private var lastUpdate: Long = 0
|
private var lastUpdate: Long = 0
|
||||||
|
|
||||||
|
private var failed: Boolean = false
|
||||||
|
val needsRemoval: Boolean
|
||||||
|
get() = pendingCount <= 0 || failed
|
||||||
|
|
||||||
fun reset(interval: Long, count: Int) {
|
fun reset(interval: Long, count: Int) {
|
||||||
this.interval = interval
|
this.interval = interval
|
||||||
this.pendingCount = count
|
this.pendingCount = count
|
||||||
|
@ -508,6 +509,7 @@ class UnifiedLocationClient private constructor(context: Context) {
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun handleLocation(location: Location) {
|
fun handleLocation(location: Location) {
|
||||||
|
if (needsRemoval) return
|
||||||
if (lastUpdate > System.currentTimeMillis()) {
|
if (lastUpdate > System.currentTimeMillis()) {
|
||||||
lastUpdate = System.currentTimeMillis()
|
lastUpdate = System.currentTimeMillis()
|
||||||
}
|
}
|
||||||
|
@ -520,13 +522,10 @@ class UnifiedLocationClient private constructor(context: Context) {
|
||||||
listener.onLocation(location)
|
listener.onLocation(location)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w(TAG, "Listener threw uncaught exception, stopping location request", e)
|
Log.w(TAG, "Listener threw uncaught exception, stopping location request", e)
|
||||||
removeRequest(this)
|
failed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if (pendingCount == 0) {
|
|
||||||
removeRequest(this)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -152,7 +152,7 @@ class UnifiedLocationServiceRoot(private val service: UnifiedLocationServiceEntr
|
||||||
|
|
||||||
override fun setGeocoderBackends(backends: Array<String>) {
|
override fun setGeocoderBackends(backends: Array<String>) {
|
||||||
if (Binder.getCallingUid() != myUid()) throw SecurityException("Only allowed from same UID")
|
if (Binder.getCallingUid() != myUid()) throw SecurityException("Only allowed from same UID")
|
||||||
Preferences(service).locationBackends = backends
|
Preferences(service).geocoderBackends = backends
|
||||||
reloadPreferences()
|
reloadPreferences()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,10 @@ import android.content.Intent.ACTION_VIEW
|
||||||
import android.content.pm.PackageManager.GET_META_DATA
|
import android.content.pm.PackageManager.GET_META_DATA
|
||||||
import android.content.res.ColorStateList
|
import android.content.res.ColorStateList
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.provider.Settings
|
||||||
|
import android.util.Log
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
@ -21,9 +24,10 @@ import androidx.annotation.AttrRes
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.databinding.Observable
|
import androidx.databinding.Observable
|
||||||
|
import androidx.databinding.Observable.OnPropertyChangedCallback
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.delay
|
||||||
import org.microg.nlp.client.UnifiedLocationClient
|
import org.microg.nlp.client.UnifiedLocationClient
|
||||||
import org.microg.nlp.ui.BackendType.GEOCODER
|
import org.microg.nlp.ui.BackendType.GEOCODER
|
||||||
import org.microg.nlp.ui.BackendType.LOCATION
|
import org.microg.nlp.ui.BackendType.LOCATION
|
||||||
|
@ -73,40 +77,82 @@ class BackendDetailsFragment : Fragment(R.layout.backend_details) {
|
||||||
return ColorStateList(arrayOf(emptyArray<Int>().toIntArray()), arrayOf(withAlpha).toIntArray())
|
return ColorStateList(arrayOf(emptyArray<Int>().toIntArray()), arrayOf(withAlpha).toIntArray())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private lateinit var binding: BackendDetailsBinding
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
val binding = BackendDetailsBinding.inflate(inflater, container, false)
|
binding = BackendDetailsBinding.inflate(inflater, container, false)
|
||||||
binding.fragment = this
|
binding.fragment = this
|
||||||
binding.switchWidget.trackTintList = switchBarTrackTintColor
|
|
||||||
lifecycleScope.launchWhenStarted {
|
|
||||||
val entry = createBackendInfo()
|
|
||||||
binding.entry = entry
|
|
||||||
binding.executePendingBindings()
|
|
||||||
if (entry?.type == LOCATION) {
|
|
||||||
val client = UnifiedLocationClient[entry.context]
|
|
||||||
|
|
||||||
val location = client.getLastLocationForBackend(entry.serviceInfo.packageName, entry.serviceInfo.name, entry.firstSignatureDigest)
|
|
||||||
?: return@launchWhenStarted
|
|
||||||
var locationString = "${location.latitude.toStringWithDigits(6)}, ${location.longitude.toStringWithDigits(6)}"
|
|
||||||
|
|
||||||
val address = client.getFromLocation(location.latitude, location.longitude, 1, Locale.getDefault().toString()).singleOrNull()
|
|
||||||
if (address != null) {
|
|
||||||
val addressLine = StringBuilder()
|
|
||||||
var i = 0
|
|
||||||
addressLine.append(address.getAddressLine(i))
|
|
||||||
while (addressLine.length < 10 && address.maxAddressLineIndex > i) {
|
|
||||||
i++
|
|
||||||
addressLine.append(", ")
|
|
||||||
addressLine.append(address.getAddressLine(i))
|
|
||||||
}
|
|
||||||
locationString = addressLine.toString()
|
|
||||||
}
|
|
||||||
binding.lastLocationString = locationString
|
|
||||||
binding.executePendingBindings()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
binding.switchWidget.trackTintList = switchBarTrackTintColor
|
||||||
|
lifecycleScope.launchWhenStarted { initContent(createBackendInfo()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun initContent(entry: BackendInfo?) {
|
||||||
|
binding.entry = entry
|
||||||
|
binding.executePendingBindings()
|
||||||
|
updateContent(entry)
|
||||||
|
entry?.addOnPropertyChangedCallback(object : OnPropertyChangedCallback() {
|
||||||
|
override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
|
||||||
|
if (propertyId == BR.enabled) {
|
||||||
|
lifecycleScope.launchWhenStarted { initContent(entry) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private var updateInProgress = false
|
||||||
|
private suspend fun updateContent(entry: BackendInfo?) {
|
||||||
|
if (entry?.type == LOCATION && entry.enabled) {
|
||||||
|
if (updateInProgress) return
|
||||||
|
updateInProgress = true
|
||||||
|
val client = UnifiedLocationClient[entry.context]
|
||||||
|
|
||||||
|
val locationTemp = client.getLastLocationForBackend(entry.serviceInfo.packageName, entry.serviceInfo.name, entry.firstSignatureDigest)
|
||||||
|
val location = when (locationTemp) {
|
||||||
|
null -> {
|
||||||
|
delay(500L) // Wait short time to ensure backend was activated
|
||||||
|
Log.d(TAG, "Location was not available, requesting once")
|
||||||
|
client.forceNextUpdate = true
|
||||||
|
client.getSingleLocation()
|
||||||
|
val secondAttempt = client.getLastLocationForBackend(entry.serviceInfo.packageName, entry.serviceInfo.name, entry.firstSignatureDigest)
|
||||||
|
if (secondAttempt == null) {
|
||||||
|
Log.d(TAG, "Location still not available, waiting or giving up")
|
||||||
|
delay(WAIT_FOR_RESULT)
|
||||||
|
client.getLastLocationForBackend(entry.serviceInfo.packageName, entry.serviceInfo.name, entry.firstSignatureDigest)
|
||||||
|
} else {
|
||||||
|
secondAttempt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> locationTemp
|
||||||
|
} ?: return
|
||||||
|
var locationString = "${location.latitude.toStringWithDigits(6)}, ${location.longitude.toStringWithDigits(6)}"
|
||||||
|
|
||||||
|
val address = client.getFromLocation(location.latitude, location.longitude, 1, Locale.getDefault().toString()).singleOrNull()
|
||||||
|
if (address != null) {
|
||||||
|
val addressLine = StringBuilder()
|
||||||
|
var i = 0
|
||||||
|
addressLine.append(address.getAddressLine(i))
|
||||||
|
while (addressLine.length < 10 && address.maxAddressLineIndex > i) {
|
||||||
|
i++
|
||||||
|
addressLine.append(", ")
|
||||||
|
addressLine.append(address.getAddressLine(i))
|
||||||
|
}
|
||||||
|
locationString = addressLine.toString()
|
||||||
|
}
|
||||||
|
updateInProgress = false
|
||||||
|
binding.lastLocationString = locationString
|
||||||
|
binding.executePendingBindings()
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "Location is not available for this backend (type: ${entry?.type}, enabled ${entry?.enabled}")
|
||||||
|
binding.lastLocationString = ""
|
||||||
|
binding.executePendingBindings()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun onBackendEnabledChanged(entry: BackendInfo) {
|
fun onBackendEnabledChanged(entry: BackendInfo) {
|
||||||
entry.enabled = !entry.enabled
|
entry.enabled = !entry.enabled
|
||||||
}
|
}
|
||||||
|
@ -130,6 +176,14 @@ class BackendDetailsFragment : Fragment(R.layout.backend_details) {
|
||||||
entry.settingsActivity?.let { activityName -> startExternalActivity(entry.serviceInfo.packageName, activityName) }
|
entry.settingsActivity?.let { activityName -> startExternalActivity(entry.serviceInfo.packageName, activityName) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onAppClicked(entry: BackendInfo) {
|
||||||
|
val intent = Intent()
|
||||||
|
intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
|
||||||
|
val uri = Uri.fromParts("package", entry.serviceInfo.packageName, null)
|
||||||
|
intent.data = uri
|
||||||
|
requireContext().startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun createBackendInfo(): BackendInfo? {
|
private suspend fun createBackendInfo(): BackendInfo? {
|
||||||
val activity = activity ?: return null
|
val activity = activity ?: return null
|
||||||
val intent = activity.intent ?: return null
|
val intent = activity.intent ?: return null
|
||||||
|
@ -147,9 +201,11 @@ class BackendDetailsFragment : Fragment(R.layout.backend_details) {
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val ACTION = "org.microg.nlp.ui.BACKEND_DETAILS"
|
const val ACTION = "org.microg.nlp.ui.BACKEND_DETAILS"
|
||||||
val EXTRA_TYPE = "org.microg.nlp.ui.BackendDetailsFragment.type"
|
const val EXTRA_TYPE = "org.microg.nlp.ui.BackendDetailsFragment.type"
|
||||||
val EXTRA_PACKAGE = "org.microg.nlp.ui.BackendDetailsFragment.package"
|
const val EXTRA_PACKAGE = "org.microg.nlp.ui.BackendDetailsFragment.package"
|
||||||
val EXTRA_NAME = "org.microg.nlp.ui.BackendDetailsFragment.name"
|
const val EXTRA_NAME = "org.microg.nlp.ui.BackendDetailsFragment.name"
|
||||||
|
private const val TAG = "USettings"
|
||||||
|
private const val WAIT_FOR_RESULT = 5000L
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -62,6 +62,9 @@ class BackendInfo(val context: Context, val serviceInfo: ServiceInfo, val type:
|
||||||
val settingsActivity: String? by lazy { serviceInfo.metaData?.getString(Constants.METADATA_BACKEND_SETTINGS_ACTIVITY) }
|
val settingsActivity: String? by lazy { serviceInfo.metaData?.getString(Constants.METADATA_BACKEND_SETTINGS_ACTIVITY) }
|
||||||
val aboutActivity: String? by lazy { serviceInfo.metaData?.getString(Constants.METADATA_BACKEND_ABOUT_ACTIVITY) }
|
val aboutActivity: String? by lazy { serviceInfo.metaData?.getString(Constants.METADATA_BACKEND_ABOUT_ACTIVITY) }
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return other is BackendInfo && other.name == name && other.enabled == enabled && other.appName == appName && other.unsignedComponent == unsignedComponent && other.backendSummary == backendSummary
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class BackendType { LOCATION, GEOCODER }
|
enum class BackendType { LOCATION, GEOCODER }
|
||||||
|
|
|
@ -16,7 +16,9 @@ import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import org.microg.nlp.api.Constants.*
|
import org.microg.nlp.api.Constants.*
|
||||||
import org.microg.nlp.client.UnifiedLocationClient
|
import org.microg.nlp.client.UnifiedLocationClient
|
||||||
import org.microg.nlp.ui.BackendDetailsFragment.Companion.EXTRA_NAME
|
import org.microg.nlp.ui.BackendDetailsFragment.Companion.EXTRA_NAME
|
||||||
|
@ -32,36 +34,39 @@ class BackendListFragment : Fragment(R.layout.backend_list) {
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
val binding = BackendListBinding.inflate(inflater, container, false)
|
val binding = BackendListBinding.inflate(inflater, container, false)
|
||||||
binding.fragment = this
|
binding.fragment = this
|
||||||
lifecycleScope.launchWhenStarted { updateAdapters() }
|
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onBackendSelected(entry: BackendInfo) {
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
lifecycleScope.launchWhenStarted { updateAdapters() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onBackendSelected(entry: BackendInfo?) {
|
||||||
|
if (entry == null) return
|
||||||
val intent = Intent(BackendDetailsFragment.ACTION)
|
val intent = Intent(BackendDetailsFragment.ACTION)
|
||||||
//intent.`package` = requireContext().packageName
|
intent.`package` = requireContext().packageName
|
||||||
intent.putExtra(EXTRA_TYPE, entry.type.name)
|
intent.putExtra(EXTRA_TYPE, entry.type.name)
|
||||||
intent.putExtra(EXTRA_PACKAGE, entry.serviceInfo.packageName)
|
intent.putExtra(EXTRA_PACKAGE, entry.serviceInfo.packageName)
|
||||||
intent.putExtra(EXTRA_NAME, entry.serviceInfo.name)
|
intent.putExtra(EXTRA_NAME, entry.serviceInfo.name)
|
||||||
context?.packageManager?.queryIntentActivities(intent, 0)?.forEach {
|
|
||||||
Log.d("USettings", it.activityInfo.name)
|
|
||||||
}
|
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun updateAdapters() {
|
private suspend fun updateAdapters() {
|
||||||
val context = requireContext()
|
val context = requireContext()
|
||||||
locationAdapter.entries = createBackendInfoList(context, Intent(ACTION_LOCATION_BACKEND), UnifiedLocationClient[context].getLocationBackends(), BackendType.LOCATION)
|
locationAdapter.setEntries(createBackendInfoList(context, Intent(ACTION_LOCATION_BACKEND), UnifiedLocationClient[context].getLocationBackends(), BackendType.LOCATION))
|
||||||
geocoderAdapter.entries = createBackendInfoList(context, Intent(ACTION_GEOCODER_BACKEND), UnifiedLocationClient[context].getGeocoderBackends(), BackendType.GEOCODER)
|
geocoderAdapter.setEntries(createBackendInfoList(context, Intent(ACTION_GEOCODER_BACKEND), UnifiedLocationClient[context].getGeocoderBackends(), BackendType.GEOCODER))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createBackendInfoList(context: Context, intent: Intent, enabledBackends: Array<String>, type: BackendType): Array<BackendInfo> {
|
private fun createBackendInfoList(context: Context, intent: Intent, enabledBackends: Array<String>, type: BackendType): Array<BackendInfo?> {
|
||||||
val backends = context.packageManager.queryIntentServices(intent, GET_META_DATA).map { BackendInfo(context, it.serviceInfo, type, lifecycleScope, enabledBackends) }
|
val backends = context.packageManager.queryIntentServices(intent, GET_META_DATA).map { BackendInfo(context, it.serviceInfo, type, lifecycleScope, enabledBackends) }
|
||||||
|
if (backends.isEmpty()) return arrayOf(null)
|
||||||
return backends.toTypedArray()
|
return backends.toTypedArray()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class BackendSettingsLineViewHolder(val binding: BackendListEntryBinding) : RecyclerView.ViewHolder(binding.root) {
|
class BackendSettingsLineViewHolder(val binding: BackendListEntryBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||||
fun bind(fragment: BackendListFragment, entry: BackendInfo) {
|
fun bind(fragment: BackendListFragment, entry: BackendInfo?) {
|
||||||
binding.fragment = fragment
|
binding.fragment = fragment
|
||||||
binding.entry = entry
|
binding.entry = entry
|
||||||
binding.executePendingBindings()
|
binding.executePendingBindings()
|
||||||
|
@ -69,11 +74,47 @@ class BackendSettingsLineViewHolder(val binding: BackendListEntryBinding) : Recy
|
||||||
}
|
}
|
||||||
|
|
||||||
class BackendSettingsLineAdapter(val fragment: BackendListFragment) : RecyclerView.Adapter<BackendSettingsLineViewHolder>() {
|
class BackendSettingsLineAdapter(val fragment: BackendListFragment) : RecyclerView.Adapter<BackendSettingsLineViewHolder>() {
|
||||||
var entries: Array<BackendInfo> = emptyArray()
|
private val entries: MutableList<BackendInfo?> = arrayListOf()
|
||||||
set(value) {
|
|
||||||
field = value
|
fun addOrUpdateEntry(entry: BackendInfo?) {
|
||||||
notifyDataSetChanged()
|
if (entry == null) {
|
||||||
|
if (entries.contains(null)) return
|
||||||
|
entries.add(entry)
|
||||||
|
notifyItemInserted(entries.size - 1)
|
||||||
|
} else {
|
||||||
|
val oldIndex = entries.indexOfFirst { it?.unsignedComponent == entry.unsignedComponent }
|
||||||
|
if (oldIndex != -1) {
|
||||||
|
if (entries[oldIndex] == entry) return
|
||||||
|
entries.removeAt(oldIndex)
|
||||||
|
}
|
||||||
|
val targetIndex = when (val i = entries.indexOfFirst { it == null || it.name.toString() > entry.name.toString() }) {
|
||||||
|
-1 -> entries.size
|
||||||
|
else -> i
|
||||||
|
}
|
||||||
|
entries.add(targetIndex, entry)
|
||||||
|
when (oldIndex) {
|
||||||
|
targetIndex -> notifyItemChanged(targetIndex)
|
||||||
|
-1 -> notifyItemInserted(targetIndex)
|
||||||
|
else -> notifyItemMoved(oldIndex, targetIndex)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeEntry(entry: BackendInfo?) {
|
||||||
|
val index = entries.indexOfFirst { it == entry || it?.unsignedComponent == entry?.unsignedComponent }
|
||||||
|
entries.removeAt(index)
|
||||||
|
notifyItemRemoved(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setEntries(entries: Array<BackendInfo?>) {
|
||||||
|
val oldEntries = this.entries.toTypedArray()
|
||||||
|
for (oldEntry in oldEntries) {
|
||||||
|
if (!entries.any { it == oldEntry || it?.unsignedComponent == oldEntry?.unsignedComponent }) {
|
||||||
|
removeEntry(oldEntry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entries.forEach { addOrUpdateEntry(it) }
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BackendSettingsLineViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BackendSettingsLineViewHolder {
|
||||||
return BackendSettingsLineViewHolder(BackendListEntryBinding.inflate(LayoutInflater.from(parent.context), parent, false))
|
return BackendSettingsLineViewHolder(BackendListEntryBinding.inflate(LayoutInflater.from(parent.context), parent, false))
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_centerHorizontal="true"
|
android:layout_centerHorizontal="true"
|
||||||
|
android:onClick='@{() -> fragment.onAppClicked(entry)}'
|
||||||
android:gravity="center_horizontal"
|
android:gravity="center_horizontal"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
@ -262,7 +263,7 @@
|
||||||
android:paddingLeft="?attr/listPreferredItemPaddingLeft"
|
android:paddingLeft="?attr/listPreferredItemPaddingLeft"
|
||||||
android:paddingEnd="?attr/listPreferredItemPaddingEnd"
|
android:paddingEnd="?attr/listPreferredItemPaddingEnd"
|
||||||
android:paddingRight="?attr/listPreferredItemPaddingRight"
|
android:paddingRight="?attr/listPreferredItemPaddingRight"
|
||||||
android:visibility='@{lastLocationString == null ? View.GONE : View.VISIBLE}'>
|
android:visibility='@{lastLocationString == null || lastLocationString == "" ? View.GONE : View.VISIBLE}'>
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:background="?android:attr/selectableItemBackground"
|
android:background="?android:attr/selectableItemBackground"
|
||||||
android:clickable="true"
|
android:clickable='@{entry != null}'
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:gravity="start|center_vertical"
|
android:gravity="start|center_vertical"
|
||||||
|
@ -73,7 +73,7 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:ellipsize="marquee"
|
android:ellipsize="marquee"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:text='@{entry.name}'
|
android:text='@{entry.name ?? "No provider installed"}'
|
||||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||||
tools:text="Mozilla Location Service" />
|
tools:text="Mozilla Location Service" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
@ -86,7 +86,8 @@
|
||||||
android:gravity="start|center_vertical"
|
android:gravity="start|center_vertical"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:paddingTop="16dp"
|
android:paddingTop="16dp"
|
||||||
android:paddingBottom="16dp">
|
android:paddingBottom="16dp"
|
||||||
|
android:visibility='@{entry == null ? View.GONE : View.VISIBLE}'>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:layout_width="1dp"
|
android:layout_width="1dp"
|
||||||
|
@ -99,7 +100,8 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:minWidth="64dp"
|
android:minWidth="64dp"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical"
|
||||||
|
android:visibility='@{entry == null ? View.GONE : View.VISIBLE}'>
|
||||||
|
|
||||||
<androidx.appcompat.widget.SwitchCompat
|
<androidx.appcompat.widget.SwitchCompat
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
Loading…
Reference in a new issue