Loading client/src/main/kotlin/org/microg/nlp/client/UnifiedLocationClient.kt +21 −22 Original line number Diff line number Diff line Loading @@ -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() Loading @@ -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) } Loading Loading @@ -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 { Loading Loading @@ -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 { Loading Loading @@ -399,11 +389,17 @@ class UnifiedLocationClient private constructor(context: Context) { } @Synchronized private fun removeRequest(request: LocationRequest) { requests.remove(request) private fun removeRequestPendingRemoval() { removeRequests(requests.filter { it.needsRemoval }) } private fun removeRequests(removalNeeded: List<LocationRequest>) { if (removalNeeded.isNotEmpty()) { requests.removeAll(removalNeeded) updateServiceInterval() updateBinding() } } @Synchronized private fun updateServiceInterval() { Loading Loading @@ -459,6 +455,7 @@ class UnifiedLocationClient private constructor(context: Context) { for (request in requests) { request.handleLocation(location) } removeRequestPendingRemoval() } }, options) updateServiceInterval() Loading Loading @@ -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 Loading @@ -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() } Loading @@ -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) } } } Loading service/src/main/kotlin/org/microg/nlp/service/UnifiedLocationServiceRoot.kt +1 −1 Original line number Diff line number Diff line Loading @@ -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() } Loading ui/src/main/kotlin/org/microg/nlp/ui/BackendDetailsFragment.kt +84 −28 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -73,19 +77,58 @@ 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 return binding.root } override fun onResume() { super.onResume() binding.switchWidget.trackTintList = switchBarTrackTintColor lifecycleScope.launchWhenStarted { val entry = createBackendInfo() lifecycleScope.launchWhenStarted { initContent(createBackendInfo()) } } private suspend fun initContent(entry: BackendInfo?) { binding.entry = entry binding.executePendingBindings() if (entry?.type == LOCATION) { 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 location = client.getLastLocationForBackend(entry.serviceInfo.packageName, entry.serviceInfo.name, entry.firstSignatureDigest) ?: return@launchWhenStarted 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() Loading @@ -100,12 +143,15 @@ class BackendDetailsFragment : Fragment(R.layout.backend_details) { } 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() } } return binding.root } fun onBackendEnabledChanged(entry: BackendInfo) { entry.enabled = !entry.enabled Loading @@ -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 Loading @@ -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 } } No newline at end of file ui/src/main/kotlin/org/microg/nlp/ui/BackendInfo.kt +3 −0 Original line number Diff line number Diff line Loading @@ -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 } Loading ui/src/main/kotlin/org/microg/nlp/ui/BackendListFragment.kt +55 −14 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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() Loading @@ -69,10 +74,46 @@ 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 { Loading Loading
client/src/main/kotlin/org/microg/nlp/client/UnifiedLocationClient.kt +21 −22 Original line number Diff line number Diff line Loading @@ -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() Loading @@ -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) } Loading Loading @@ -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 { Loading Loading @@ -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 { Loading Loading @@ -399,11 +389,17 @@ class UnifiedLocationClient private constructor(context: Context) { } @Synchronized private fun removeRequest(request: LocationRequest) { requests.remove(request) private fun removeRequestPendingRemoval() { removeRequests(requests.filter { it.needsRemoval }) } private fun removeRequests(removalNeeded: List<LocationRequest>) { if (removalNeeded.isNotEmpty()) { requests.removeAll(removalNeeded) updateServiceInterval() updateBinding() } } @Synchronized private fun updateServiceInterval() { Loading Loading @@ -459,6 +455,7 @@ class UnifiedLocationClient private constructor(context: Context) { for (request in requests) { request.handleLocation(location) } removeRequestPendingRemoval() } }, options) updateServiceInterval() Loading Loading @@ -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 Loading @@ -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() } Loading @@ -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) } } } Loading
service/src/main/kotlin/org/microg/nlp/service/UnifiedLocationServiceRoot.kt +1 −1 Original line number Diff line number Diff line Loading @@ -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() } Loading
ui/src/main/kotlin/org/microg/nlp/ui/BackendDetailsFragment.kt +84 −28 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -73,19 +77,58 @@ 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 return binding.root } override fun onResume() { super.onResume() binding.switchWidget.trackTintList = switchBarTrackTintColor lifecycleScope.launchWhenStarted { val entry = createBackendInfo() lifecycleScope.launchWhenStarted { initContent(createBackendInfo()) } } private suspend fun initContent(entry: BackendInfo?) { binding.entry = entry binding.executePendingBindings() if (entry?.type == LOCATION) { 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 location = client.getLastLocationForBackend(entry.serviceInfo.packageName, entry.serviceInfo.name, entry.firstSignatureDigest) ?: return@launchWhenStarted 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() Loading @@ -100,12 +143,15 @@ class BackendDetailsFragment : Fragment(R.layout.backend_details) { } 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() } } return binding.root } fun onBackendEnabledChanged(entry: BackendInfo) { entry.enabled = !entry.enabled Loading @@ -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 Loading @@ -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 } } No newline at end of file
ui/src/main/kotlin/org/microg/nlp/ui/BackendInfo.kt +3 −0 Original line number Diff line number Diff line Loading @@ -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 } Loading
ui/src/main/kotlin/org/microg/nlp/ui/BackendListFragment.kt +55 −14 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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() Loading @@ -69,10 +74,46 @@ 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 { Loading