Bug fixes

This commit is contained in:
Marvin W 2020-06-08 22:52:19 +02:00
parent 60f7532abf
commit 484d01fb83
No known key found for this signature in database
GPG Key ID: 072E9235DB996F2A
7 changed files with 178 additions and 76 deletions

View File

@ -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)
}
}
}

View File

@ -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()
}

View File

@ -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
}
}

View File

@ -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 }

View File

@ -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))

View File

@ -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"

View File

@ -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"