Bug fixes
This commit is contained in:
parent
60f7532abf
commit
484d01fb83
|
@ -212,11 +212,7 @@ class UnifiedLocationClient private constructor(context: Context) {
|
|||
|
||||
@Synchronized
|
||||
fun requestLocationUpdates(listener: LocationListener, interval: Long, count: Int) {
|
||||
for (request in requests) {
|
||||
if (request.listener === listener) {
|
||||
requests.remove(request)
|
||||
}
|
||||
}
|
||||
requests.removeAll(requests.filter { it.listener === listener })
|
||||
requests.add(LocationRequest(listener, interval, count))
|
||||
updateServiceInterval()
|
||||
updateBinding()
|
||||
|
@ -224,13 +220,7 @@ class UnifiedLocationClient private constructor(context: Context) {
|
|||
|
||||
@Synchronized
|
||||
fun removeLocationUpdates(listener: LocationListener) {
|
||||
for (request in requests) {
|
||||
if (request.listener === listener) {
|
||||
requests.remove(request)
|
||||
}
|
||||
}
|
||||
updateServiceInterval()
|
||||
updateBinding()
|
||||
removeRequests(requests.filter { it.listener === listener })
|
||||
}
|
||||
|
||||
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))
|
||||
configureContinuationTimeout(continuation, timeout)
|
||||
}
|
||||
} catch (e: RemoteException) {
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to request geocode", e)
|
||||
return emptyList()
|
||||
} finally {
|
||||
|
@ -368,7 +358,7 @@ class UnifiedLocationClient private constructor(context: Context) {
|
|||
|
||||
suspend fun setGeocoderBackends(backends: Array<String>) {
|
||||
try {
|
||||
refAndGetService().locationBackends = backends
|
||||
refAndGetService().geocoderBackends = backends
|
||||
} catch (e: RemoteException) {
|
||||
Log.w(TAG, "Failed to handle request", e)
|
||||
} finally {
|
||||
|
@ -399,10 +389,16 @@ class UnifiedLocationClient private constructor(context: Context) {
|
|||
}
|
||||
|
||||
@Synchronized
|
||||
private fun removeRequest(request: LocationRequest) {
|
||||
requests.remove(request)
|
||||
updateServiceInterval()
|
||||
updateBinding()
|
||||
private fun removeRequestPendingRemoval() {
|
||||
removeRequests(requests.filter { it.needsRemoval })
|
||||
}
|
||||
|
||||
private fun removeRequests(removalNeeded: List<LocationRequest>) {
|
||||
if (removalNeeded.isNotEmpty()) {
|
||||
requests.removeAll(removalNeeded)
|
||||
updateServiceInterval()
|
||||
updateBinding()
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
|
@ -459,6 +455,7 @@ class UnifiedLocationClient private constructor(context: Context) {
|
|||
for (request in requests) {
|
||||
request.handleLocation(location)
|
||||
}
|
||||
removeRequestPendingRemoval()
|
||||
}
|
||||
}, options)
|
||||
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 var lastUpdate: Long = 0
|
||||
|
||||
private var failed: Boolean = false
|
||||
val needsRemoval: Boolean
|
||||
get() = pendingCount <= 0 || failed
|
||||
|
||||
fun reset(interval: Long, count: Int) {
|
||||
this.interval = interval
|
||||
this.pendingCount = count
|
||||
|
@ -508,6 +509,7 @@ class UnifiedLocationClient private constructor(context: Context) {
|
|||
|
||||
@Synchronized
|
||||
fun handleLocation(location: Location) {
|
||||
if (needsRemoval) return
|
||||
if (lastUpdate > System.currentTimeMillis()) {
|
||||
lastUpdate = System.currentTimeMillis()
|
||||
}
|
||||
|
@ -520,13 +522,10 @@ class UnifiedLocationClient private constructor(context: Context) {
|
|||
listener.onLocation(location)
|
||||
} catch (e: Exception) {
|
||||
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>) {
|
||||
if (Binder.getCallingUid() != myUid()) throw SecurityException("Only allowed from same UID")
|
||||
Preferences(service).locationBackends = backends
|
||||
Preferences(service).geocoderBackends = backends
|
||||
reloadPreferences()
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,10 @@ import android.content.Intent.ACTION_VIEW
|
|||
import android.content.pm.PackageManager.GET_META_DATA
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
|
@ -21,9 +24,10 @@ import androidx.annotation.AttrRes
|
|||
import androidx.annotation.ColorInt
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.databinding.Observable
|
||||
import androidx.databinding.Observable.OnPropertyChangedCallback
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.delay
|
||||
import org.microg.nlp.client.UnifiedLocationClient
|
||||
import org.microg.nlp.ui.BackendType.GEOCODER
|
||||
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())
|
||||
}
|
||||
|
||||
private lateinit var binding: BackendDetailsBinding
|
||||
|
||||
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.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
|
||||
}
|
||||
|
||||
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) {
|
||||
entry.enabled = !entry.enabled
|
||||
}
|
||||
|
@ -130,6 +176,14 @@ class BackendDetailsFragment : Fragment(R.layout.backend_details) {
|
|||
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? {
|
||||
val activity = activity ?: return null
|
||||
val intent = activity.intent ?: return null
|
||||
|
@ -147,9 +201,11 @@ class BackendDetailsFragment : Fragment(R.layout.backend_details) {
|
|||
}
|
||||
|
||||
companion object {
|
||||
val ACTION = "org.microg.nlp.ui.BACKEND_DETAILS"
|
||||
val EXTRA_TYPE = "org.microg.nlp.ui.BackendDetailsFragment.type"
|
||||
val EXTRA_PACKAGE = "org.microg.nlp.ui.BackendDetailsFragment.package"
|
||||
val EXTRA_NAME = "org.microg.nlp.ui.BackendDetailsFragment.name"
|
||||
const val ACTION = "org.microg.nlp.ui.BACKEND_DETAILS"
|
||||
const val EXTRA_TYPE = "org.microg.nlp.ui.BackendDetailsFragment.type"
|
||||
const val EXTRA_PACKAGE = "org.microg.nlp.ui.BackendDetailsFragment.package"
|
||||
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 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 }
|
||||
|
|
|
@ -16,7 +16,9 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.coroutines.delay
|
||||
import org.microg.nlp.api.Constants.*
|
||||
import org.microg.nlp.client.UnifiedLocationClient
|
||||
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? {
|
||||
val binding = BackendListBinding.inflate(inflater, container, false)
|
||||
binding.fragment = this
|
||||
lifecycleScope.launchWhenStarted { updateAdapters() }
|
||||
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)
|
||||
//intent.`package` = requireContext().packageName
|
||||
intent.`package` = requireContext().packageName
|
||||
intent.putExtra(EXTRA_TYPE, entry.type.name)
|
||||
intent.putExtra(EXTRA_PACKAGE, entry.serviceInfo.packageName)
|
||||
intent.putExtra(EXTRA_NAME, entry.serviceInfo.name)
|
||||
context?.packageManager?.queryIntentActivities(intent, 0)?.forEach {
|
||||
Log.d("USettings", it.activityInfo.name)
|
||||
}
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
private suspend fun updateAdapters() {
|
||||
val context = requireContext()
|
||||
locationAdapter.entries = createBackendInfoList(context, Intent(ACTION_LOCATION_BACKEND), UnifiedLocationClient[context].getLocationBackends(), BackendType.LOCATION)
|
||||
geocoderAdapter.entries = createBackendInfoList(context, Intent(ACTION_GEOCODER_BACKEND), UnifiedLocationClient[context].getGeocoderBackends(), BackendType.GEOCODER)
|
||||
locationAdapter.setEntries(createBackendInfoList(context, Intent(ACTION_LOCATION_BACKEND), UnifiedLocationClient[context].getLocationBackends(), BackendType.LOCATION))
|
||||
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) }
|
||||
if (backends.isEmpty()) return arrayOf(null)
|
||||
return backends.toTypedArray()
|
||||
}
|
||||
}
|
||||
|
||||
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.entry = entry
|
||||
binding.executePendingBindings()
|
||||
|
@ -69,11 +74,47 @@ class BackendSettingsLineViewHolder(val binding: BackendListEntryBinding) : Recy
|
|||
}
|
||||
|
||||
class BackendSettingsLineAdapter(val fragment: BackendListFragment) : RecyclerView.Adapter<BackendSettingsLineViewHolder>() {
|
||||
var entries: Array<BackendInfo> = emptyArray()
|
||||
set(value) {
|
||||
field = value
|
||||
notifyDataSetChanged()
|
||||
private val entries: MutableList<BackendInfo?> = arrayListOf()
|
||||
|
||||
fun addOrUpdateEntry(entry: BackendInfo?) {
|
||||
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 {
|
||||
return BackendSettingsLineViewHolder(BackendListEntryBinding.inflate(LayoutInflater.from(parent.context), parent, false))
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:onClick='@{() -> fragment.onAppClicked(entry)}'
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical">
|
||||
|
||||
|
@ -262,7 +263,7 @@
|
|||
android:paddingLeft="?attr/listPreferredItemPaddingLeft"
|
||||
android:paddingEnd="?attr/listPreferredItemPaddingEnd"
|
||||
android:paddingRight="?attr/listPreferredItemPaddingRight"
|
||||
android:visibility='@{lastLocationString == null ? View.GONE : View.VISIBLE}'>
|
||||
android:visibility='@{lastLocationString == null || lastLocationString == "" ? View.GONE : View.VISIBLE}'>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:clickable='@{entry != null}'
|
||||
android:clipToPadding="false"
|
||||
android:focusable="true"
|
||||
android:gravity="start|center_vertical"
|
||||
|
@ -73,7 +73,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:ellipsize="marquee"
|
||||
android:singleLine="true"
|
||||
android:text='@{entry.name}'
|
||||
android:text='@{entry.name ?? "No provider installed"}'
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
tools:text="Mozilla Location Service" />
|
||||
</RelativeLayout>
|
||||
|
@ -86,7 +86,8 @@
|
|||
android:gravity="start|center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="16dp">
|
||||
android:paddingBottom="16dp"
|
||||
android:visibility='@{entry == null ? View.GONE : View.VISIBLE}'>
|
||||
|
||||
<View
|
||||
android:layout_width="1dp"
|
||||
|
@ -99,7 +100,8 @@
|
|||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:minWidth="64dp"
|
||||
android:orientation="vertical">
|
||||
android:orientation="vertical"
|
||||
android:visibility='@{entry == null ? View.GONE : View.VISIBLE}'>
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:layout_width="match_parent"
|
||||
|
|
Loading…
Reference in New Issue