Add UI
This commit is contained in:
parent
9a96cc4583
commit
3d37a285d4
83
ui/build.gradle
Normal file
83
ui/build.gradle
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2019, microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
android {
|
||||
compileSdkVersion androidCompileSdk
|
||||
buildToolsVersion "$androidBuildVersionTools"
|
||||
dataBinding {
|
||||
enabled = true
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
versionName version
|
||||
minSdkVersion Math.max(androidMinSdk, 14)
|
||||
targetSdkVersion androidTargetSdk
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':api')
|
||||
implementation project(':client')
|
||||
|
||||
implementation "androidx.appcompat:appcompat:$appcompatVersion"
|
||||
implementation "androidx.fragment:fragment:$fragmentVersion"
|
||||
implementation "androidx.recyclerview:recyclerview:$recyclerviewVersion"
|
||||
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion"
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
publishing {
|
||||
publications {
|
||||
release(MavenPublication) {
|
||||
pom {
|
||||
name = 'UnifiedNlp UI'
|
||||
description = 'UnifiedNlp UI library for common configuration fragments'
|
||||
url = 'https://github.com/microg/UnifiedNlp'
|
||||
licenses {
|
||||
license {
|
||||
name = 'The Apache Software License, Version 2.0'
|
||||
url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
|
||||
}
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id = 'microg'
|
||||
name = 'microG Team'
|
||||
}
|
||||
developer {
|
||||
id = 'mar-v-in'
|
||||
name = 'Marvin W.'
|
||||
}
|
||||
}
|
||||
scm {
|
||||
url = 'https://github.com/microg/UnifiedNlp'
|
||||
connection = 'scm:git:https://github.com/microg/UnifiedNlp.git'
|
||||
developerConnection = 'scm:git:ssh://github.com/microg/UnifiedNlp.git'
|
||||
}
|
||||
}
|
||||
|
||||
from components.release
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
10
ui/src/main/AndroidManifest.xml
Normal file
10
ui/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ SPDX-FileCopyrightText: 2020, microG Project Team
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<manifest package="org.microg.nlp.ui">
|
||||
|
||||
<application />
|
||||
</manifest>
|
150
ui/src/main/kotlin/org/microg/nlp/ui/BackendDetailsFragment.kt
Normal file
150
ui/src/main/kotlin/org/microg/nlp/ui/BackendDetailsFragment.kt
Normal file
|
@ -0,0 +1,150 @@
|
|||
package org.microg.nlp.ui
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
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.os.Bundle
|
||||
import android.util.TypedValue
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.databinding.Observable
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.microg.nlp.client.UnifiedLocationClient
|
||||
import org.microg.nlp.ui.BackendType.GEOCODER
|
||||
import org.microg.nlp.ui.BackendType.LOCATION
|
||||
import org.microg.nlp.ui.databinding.BackendDetailsBinding
|
||||
import java.util.*
|
||||
|
||||
class BackendDetailsFragment : Fragment(R.layout.backend_details) {
|
||||
|
||||
fun Double.toStringWithDigits(digits: Int): String {
|
||||
val s = this.toString()
|
||||
val i = s.indexOf('.')
|
||||
if (i <= 0 || s.length - i - 1 < digits) return s
|
||||
if (digits == 0) return s.substring(0, i)
|
||||
return s.substring(0, s.indexOf('.') + digits + 1)
|
||||
}
|
||||
|
||||
fun Float.toStringWithDigits(digits: Int): String {
|
||||
val s = this.toString()
|
||||
val i = s.indexOf('.')
|
||||
if (i <= 0 || s.length - i - 1 < digits) return s
|
||||
if (digits == 0) return s.substring(0, i)
|
||||
return s.substring(0, s.indexOf('.') + digits + 1)
|
||||
}
|
||||
|
||||
@ColorInt
|
||||
fun Context.resolveColor(@AttrRes resid: Int): Int? {
|
||||
val typedValue = TypedValue()
|
||||
if (!theme.resolveAttribute(resid, typedValue, true)) return null
|
||||
val colorRes = if (typedValue.resourceId != 0) typedValue.resourceId else typedValue.data
|
||||
return ContextCompat.getColor(this, colorRes)
|
||||
}
|
||||
|
||||
val switchBarEnabledColor: Int
|
||||
get() = context?.resolveColor(androidx.appcompat.R.attr.colorControlActivated) ?: Color.RED
|
||||
|
||||
val switchBarDisabledColor: Int
|
||||
get() {
|
||||
val color = context?.resolveColor(android.R.attr.textColorSecondary) ?: Color.RED
|
||||
return Color.argb(100, Color.red(color), Color.green(color), Color.blue(color))
|
||||
}
|
||||
|
||||
val switchBarTrackTintColor: ColorStateList
|
||||
get() {
|
||||
val color = context?.resolveColor(android.R.attr.textColorPrimaryInverse)
|
||||
?: Color.RED
|
||||
val withAlpha = Color.argb(50, Color.red(color), Color.green(color), Color.blue(color))
|
||||
return ColorStateList(arrayOf(emptyArray<Int>().toIntArray()), arrayOf(withAlpha).toIntArray())
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
val 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
|
||||
}
|
||||
|
||||
fun onBackendEnabledChanged(entry: BackendInfo) {
|
||||
entry.enabled = !entry.enabled
|
||||
}
|
||||
|
||||
private fun createExternalIntent(packageName: String, activityName: String): Intent {
|
||||
val intent = Intent(ACTION_VIEW);
|
||||
intent.setPackage(packageName);
|
||||
intent.setClassName(packageName, activityName);
|
||||
return intent;
|
||||
}
|
||||
|
||||
private fun startExternalActivity(packageName: String, activityName: String) {
|
||||
requireContext().startActivity(createExternalIntent(packageName, activityName))
|
||||
}
|
||||
|
||||
fun onAboutClicked(entry: BackendInfo) {
|
||||
entry.aboutActivity?.let { activityName -> startExternalActivity(entry.serviceInfo.packageName, activityName) }
|
||||
}
|
||||
|
||||
fun onConfigureClicked(entry: BackendInfo) {
|
||||
entry.settingsActivity?.let { activityName -> startExternalActivity(entry.serviceInfo.packageName, activityName) }
|
||||
}
|
||||
|
||||
private suspend fun createBackendInfo(): BackendInfo? {
|
||||
val activity = activity ?: return null
|
||||
val intent = activity.intent ?: return null
|
||||
val type = BackendType.values().find { it.name == intent.getStringExtra(EXTRA_TYPE) }
|
||||
?: return null
|
||||
val packageName = intent.getStringExtra(EXTRA_PACKAGE) ?: return null
|
||||
val name = intent.getStringExtra(EXTRA_NAME) ?: return null
|
||||
val serviceInfo = activity.packageManager.getServiceInfo(ComponentName(packageName, name), GET_META_DATA)
|
||||
?: return null
|
||||
val enabledBackends = when (type) {
|
||||
GEOCODER -> UnifiedLocationClient[activity].getGeocoderBackends()
|
||||
LOCATION -> UnifiedLocationClient[activity].getLocationBackends()
|
||||
}
|
||||
return BackendInfo(activity, serviceInfo, type, lifecycleScope, enabledBackends)
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
95
ui/src/main/kotlin/org/microg/nlp/ui/BackendInfo.kt
Normal file
95
ui/src/main/kotlin/org/microg/nlp/ui/BackendInfo.kt
Normal file
|
@ -0,0 +1,95 @@
|
|||
package org.microg.nlp.ui
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.ServiceInfo
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.util.Log
|
||||
import androidx.databinding.BaseObservable
|
||||
import androidx.databinding.Bindable
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.microg.nlp.api.Constants
|
||||
import org.microg.nlp.client.UnifiedLocationClient
|
||||
import java.security.MessageDigest
|
||||
import java.security.NoSuchAlgorithmException
|
||||
|
||||
private val TAG: String = "ULocUI"
|
||||
|
||||
class BackendInfo(val context: Context, val serviceInfo: ServiceInfo, val type: BackendType, val coroutineScope: CoroutineScope, enabledBackends: Array<String>) : BaseObservable() {
|
||||
val firstSignatureDigest = firstSignatureDigest(context, serviceInfo.packageName)
|
||||
val unsignedComponent: String = "${serviceInfo.packageName}/${serviceInfo.name}"
|
||||
val signedComponent: String = "${serviceInfo.packageName}/${serviceInfo.name}/$firstSignatureDigest"
|
||||
|
||||
var enabled: Boolean = enabledBackends.contains(signedComponent) || enabledBackends.contains(unsignedComponent)
|
||||
@Bindable get
|
||||
set(value) {
|
||||
if (field == value) return
|
||||
field = value
|
||||
notifyPropertyChanged(BR.enabled)
|
||||
coroutineScope.launch {
|
||||
val client = UnifiedLocationClient[context]
|
||||
val withoutSelf = when (type) {
|
||||
BackendType.LOCATION -> client.getLocationBackends()
|
||||
BackendType.GEOCODER -> client.getGeocoderBackends()
|
||||
}.filterNot { it == unsignedComponent || it.startsWith("$unsignedComponent/") }.toTypedArray()
|
||||
val new = if (value) withoutSelf + signedComponent else withoutSelf
|
||||
try {
|
||||
when (type) {
|
||||
BackendType.LOCATION -> client.setLocationBackends(new)
|
||||
BackendType.GEOCODER -> client.setGeocoderBackends(new)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to change backend state", e)
|
||||
field = !value
|
||||
notifyPropertyChanged(BR.enabled)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val appIcon: Drawable by lazy { serviceInfo.loadIcon(context.packageManager) }
|
||||
val name: CharSequence by lazy { serviceInfo.loadLabel(context.packageManager) }
|
||||
val appName: CharSequence by lazy { serviceInfo.applicationInfo.loadLabel(context.packageManager) }
|
||||
|
||||
val backendSummary: String? by lazy { serviceInfo.metaData?.getString(Constants.METADATA_BACKEND_SUMMARY) }
|
||||
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) }
|
||||
|
||||
}
|
||||
|
||||
enum class BackendType { LOCATION, GEOCODER }
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
@SuppressLint("PackageManagerGetSignatures")
|
||||
fun firstSignatureDigest(context: Context, packageName: String?): String? {
|
||||
val packageManager = context.packageManager
|
||||
val info: PackageInfo?
|
||||
try {
|
||||
info = packageManager.getPackageInfo(packageName!!, PackageManager.GET_SIGNATURES)
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (info?.signatures?.isNotEmpty() == true) {
|
||||
for (sig in info.signatures) {
|
||||
sha256sum(sig.toByteArray())?.let { return it }
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun sha256sum(bytes: ByteArray): String? {
|
||||
try {
|
||||
val md = MessageDigest.getInstance("SHA-256")
|
||||
val digest = md.digest(bytes)
|
||||
val sb = StringBuilder(2 * digest.size)
|
||||
for (b in digest) {
|
||||
sb.append(String.format("%02x", b))
|
||||
}
|
||||
return sb.toString()
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
return null
|
||||
}
|
||||
}
|
86
ui/src/main/kotlin/org/microg/nlp/ui/BackendListFragment.kt
Normal file
86
ui/src/main/kotlin/org/microg/nlp/ui/BackendListFragment.kt
Normal file
|
@ -0,0 +1,86 @@
|
|||
package org.microg.nlp.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.PackageManager.GET_META_DATA
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.microg.nlp.api.Constants.*
|
||||
import org.microg.nlp.client.UnifiedLocationClient
|
||||
import org.microg.nlp.ui.BackendDetailsFragment.Companion.EXTRA_NAME
|
||||
import org.microg.nlp.ui.BackendDetailsFragment.Companion.EXTRA_PACKAGE
|
||||
import org.microg.nlp.ui.BackendDetailsFragment.Companion.EXTRA_TYPE
|
||||
import org.microg.nlp.ui.databinding.BackendListBinding
|
||||
import org.microg.nlp.ui.databinding.BackendListEntryBinding
|
||||
|
||||
class BackendListFragment : Fragment(R.layout.backend_list) {
|
||||
val locationAdapter: BackendSettingsLineAdapter = BackendSettingsLineAdapter(this)
|
||||
val geocoderAdapter: BackendSettingsLineAdapter = BackendSettingsLineAdapter(this)
|
||||
|
||||
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) {
|
||||
val intent = Intent(BackendDetailsFragment.ACTION)
|
||||
//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)
|
||||
}
|
||||
|
||||
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) }
|
||||
return backends.toTypedArray()
|
||||
}
|
||||
}
|
||||
|
||||
class BackendSettingsLineViewHolder(val binding: BackendListEntryBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||
fun bind(fragment: BackendListFragment, entry: BackendInfo) {
|
||||
binding.fragment = fragment
|
||||
binding.entry = entry
|
||||
binding.executePendingBindings()
|
||||
}
|
||||
}
|
||||
|
||||
class BackendSettingsLineAdapter(val fragment: BackendListFragment) : RecyclerView.Adapter<BackendSettingsLineViewHolder>() {
|
||||
var entries: Array<BackendInfo> = emptyArray()
|
||||
set(value) {
|
||||
field = value
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BackendSettingsLineViewHolder {
|
||||
return BackendSettingsLineViewHolder(BackendListEntryBinding.inflate(LayoutInflater.from(parent.context), parent, false))
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: BackendSettingsLineViewHolder, position: Int) {
|
||||
holder.bind(fragment, entries[position])
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return entries.size
|
||||
}
|
||||
}
|
||||
|
||||
|
301
ui/src/main/res/layout/backend_details.xml
Normal file
301
ui/src/main/res/layout/backend_details.xml
Normal file
|
@ -0,0 +1,301 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ SPDX-FileCopyrightText: 2020, microG Project Team
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
|
||||
<import type="android.view.View" />
|
||||
|
||||
<import type="org.microg.nlp.ui.BackendType" />
|
||||
|
||||
<variable
|
||||
name="fragment"
|
||||
type="org.microg.nlp.ui.BackendDetailsFragment" />
|
||||
|
||||
<variable
|
||||
name="entry"
|
||||
type="org.microg.nlp.ui.BackendInfo" />
|
||||
|
||||
<variable
|
||||
name="lastLocationString"
|
||||
type="String" />
|
||||
</data>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:animateLayoutChanges="true"
|
||||
android:orientation="vertical">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:paddingStart="?attr/listPreferredItemPaddingStart"
|
||||
android:paddingLeft="?attr/listPreferredItemPaddingLeft"
|
||||
android:paddingTop="24dp"
|
||||
android:paddingEnd="?attr/listPreferredItemPaddingEnd"
|
||||
android:paddingRight="?attr/listPreferredItemPaddingRight"
|
||||
android:paddingBottom="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:antialias="true"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@{entry.appIcon}"
|
||||
tools:src="@android:mipmap/sym_def_app_icon" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:ellipsize="marquee"
|
||||
android:gravity="center"
|
||||
android:singleLine="false"
|
||||
android:text='@{entry.name}'
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="20sp"
|
||||
tools:text="Mozilla Location Service" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/app_name"
|
||||
style="@style/TextAppearance.AppCompat.Body1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:text='@{entry.appName ?? ""}'
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:visibility='@{entry != null && entry.appName == entry.name ? View.GONE : View.VISIBLE}'
|
||||
tools:text="Mozilla UnifiedNlp Backend" />
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:gravity="center"
|
||||
android:onClick="@{() -> fragment.onBackendEnabledChanged(entry)}"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="?attr/listPreferredItemPaddingStart"
|
||||
android:paddingLeft="?attr/listPreferredItemPaddingLeft"
|
||||
android:paddingEnd="?attr/listPreferredItemPaddingEnd"
|
||||
android:paddingRight="?attr/listPreferredItemPaddingRight"
|
||||
app:backgroundColor="@{entry.enabled ? fragment.switchBarEnabledColor : fragment.switchBarDisabledColor}"
|
||||
tools:background="?attr/colorAccent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/switch_text"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:paddingStart="56dp"
|
||||
android:paddingLeft="56dp"
|
||||
android:text="Use backend"
|
||||
android:textAppearance="?attr/textAppearanceListItem"
|
||||
android:textColor="?android:attr/textColorPrimaryInverse" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/switch_widget"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:background="@null"
|
||||
android:checked="@={entry.enabled}"
|
||||
app:thumbTint="?android:attr/textColorPrimaryInverse"
|
||||
tools:checked="true" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/settingsLauncher"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:clipToPadding="false"
|
||||
android:focusable="true"
|
||||
android:gravity="start|center_vertical"
|
||||
android:minHeight="?attr/listPreferredItemHeightSmall"
|
||||
android:onClick="@{() -> fragment.onConfigureClicked(entry)}"
|
||||
android:paddingStart="?attr/listPreferredItemPaddingStart"
|
||||
android:paddingLeft="?attr/listPreferredItemPaddingLeft"
|
||||
android:paddingEnd="?attr/listPreferredItemPaddingEnd"
|
||||
android:paddingRight="?attr/listPreferredItemPaddingRight"
|
||||
android:visibility='@{entry == null || entry.settingsActivity == null ? View.GONE : View.VISIBLE}'>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="56dp"
|
||||
android:paddingLeft="56dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="marquee"
|
||||
android:singleLine="true"
|
||||
android:text="Configure"
|
||||
android:textAppearance="?attr/textAppearanceListItem" />
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/aboutLauncher"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:clipToPadding="false"
|
||||
android:focusable="true"
|
||||
android:gravity="start|center_vertical"
|
||||
android:minHeight="?attr/listPreferredItemHeightSmall"
|
||||
android:onClick="@{() -> fragment.onAboutClicked(entry)}"
|
||||
android:paddingStart="?attr/listPreferredItemPaddingStart"
|
||||
android:paddingLeft="?attr/listPreferredItemPaddingLeft"
|
||||
android:paddingEnd="?attr/listPreferredItemPaddingEnd"
|
||||
android:paddingRight="?attr/listPreferredItemPaddingRight"
|
||||
android:visibility='@{entry == null || entry.aboutActivity == null ? View.GONE : View.VISIBLE}'>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="56dp"
|
||||
android:paddingLeft="56dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/about_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="marquee"
|
||||
android:singleLine="true"
|
||||
android:text='About'
|
||||
android:textAppearance="?attr/textAppearanceListItem" />
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="?attr/dividerHorizontal"
|
||||
android:visibility='@{entry == null || (entry.backendSummary == null || entry.type != BackendType.LOCATION) || (entry.settingsActivity == null && entry.aboutActivity == null) ? View.GONE : View.VISIBLE}' />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:gravity="start|center_vertical"
|
||||
android:minHeight="?attr/listPreferredItemHeightSmall"
|
||||
android:paddingStart="?attr/listPreferredItemPaddingStart"
|
||||
android:paddingLeft="?attr/listPreferredItemPaddingLeft"
|
||||
android:paddingEnd="?attr/listPreferredItemPaddingEnd"
|
||||
android:paddingRight="?attr/listPreferredItemPaddingRight"
|
||||
android:visibility='@{entry == null || entry.backendSummary == null ? View.GONE : View.VISIBLE}'>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="56dp"
|
||||
android:paddingLeft="56dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="16dp">
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/description_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="marquee"
|
||||
android:singleLine="true"
|
||||
android:text="Description"
|
||||
android:textAppearance="?attr/textAppearanceListItem"
|
||||
android:textColor="?android:attr/textColorSecondary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/summary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/description_title"
|
||||
android:layout_alignStart="@id/description_title"
|
||||
android:layout_alignLeft="@id/description_title"
|
||||
android:maxLines="10"
|
||||
android:text='@{entry.backendSummary ?? ""}'
|
||||
android:textAppearance="?attr/textAppearanceListItemSecondary"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
tools:text="Locate using Mozilla\'s online database" />
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:gravity="start|center_vertical"
|
||||
android:minHeight="?attr/listPreferredItemHeightSmall"
|
||||
android:paddingStart="?attr/listPreferredItemPaddingStart"
|
||||
android:paddingLeft="?attr/listPreferredItemPaddingLeft"
|
||||
android:paddingEnd="?attr/listPreferredItemPaddingEnd"
|
||||
android:paddingRight="?attr/listPreferredItemPaddingRight"
|
||||
android:visibility='@{lastLocationString == null ? View.GONE : View.VISIBLE}'>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="56dp"
|
||||
android:paddingLeft="56dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="16dp">
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/last_location_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="marquee"
|
||||
android:singleLine="true"
|
||||
android:text="Last location"
|
||||
android:textAppearance="?attr/textAppearanceListItem"
|
||||
android:textColor="?android:attr/textColorSecondary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/last_location"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/last_location_title"
|
||||
android:layout_alignStart="@id/last_location_title"
|
||||
android:layout_alignLeft="@id/last_location_title"
|
||||
android:maxLines="10"
|
||||
android:text='@{lastLocationString ?? ""}'
|
||||
android:textAppearance="?attr/textAppearanceListItemSecondary"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
tools:text="Main St 23" />
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</layout>
|
101
ui/src/main/res/layout/backend_list.xml
Normal file
101
ui/src/main/res/layout/backend_list.xml
Normal file
|
@ -0,0 +1,101 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ SPDX-FileCopyrightText: 2020, microG Project Team
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
|
||||
<variable
|
||||
name="fragment"
|
||||
type="org.microg.nlp.ui.BackendListFragment" />
|
||||
</data>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:clipToPadding="false"
|
||||
android:gravity="start|center_vertical"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:paddingRight="?android:attr/listPreferredItemPaddingRight">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="marquee"
|
||||
android:letterSpacing="0.073"
|
||||
android:paddingStart="56dp"
|
||||
android:paddingLeft="56dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:singleLine="true"
|
||||
android:text="@string/network_location"
|
||||
android:textAllCaps="true"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
|
||||
android:textColor="?attr/colorAccent"
|
||||
android:textSize="11sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/network_location_backend_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:adapter="@{fragment.locationAdapter}"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:itemCount="2"
|
||||
tools:listitem="@layout/backend_list_entry" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="?android:attr/dividerHorizontal" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:clipToPadding="false"
|
||||
android:gravity="start|center_vertical"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:paddingRight="?android:attr/listPreferredItemPaddingRight">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="marquee"
|
||||
android:paddingStart="56dp"
|
||||
android:paddingLeft="56dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:singleLine="true"
|
||||
android:text="@string/geocoding"
|
||||
android:textAllCaps="true"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
|
||||
android:textColor="?attr/colorAccent"
|
||||
android:textSize="11sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/geocoding_backend_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:adapter="@{fragment.geocoderAdapter}"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:itemCount="1"
|
||||
tools:listitem="@layout/backend_list_entry" />
|
||||
</LinearLayout>
|
||||
</layout>
|
113
ui/src/main/res/layout/backend_list_entry.xml
Normal file
113
ui/src/main/res/layout/backend_list_entry.xml
Normal file
|
@ -0,0 +1,113 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ SPDX-FileCopyrightText: 2020, microG Project Team
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
|
||||
<import type="android.view.View" />
|
||||
|
||||
<variable
|
||||
name="fragment"
|
||||
type="org.microg.nlp.ui.BackendListFragment" />
|
||||
|
||||
<variable
|
||||
name="entry"
|
||||
type="org.microg.nlp.ui.BackendInfo" />
|
||||
</data>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:baselineAligned="false"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="?android:attr/listPreferredItemHeightSmall"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:clipToPadding="false"
|
||||
android:focusable="true"
|
||||
android:gravity="start|center_vertical"
|
||||
android:onClick="@{() -> fragment.onBackendSelected(entry)}"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:paddingRight="?android:attr/listPreferredItemPaddingRight">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/icon_frame"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:gravity="start|center_vertical"
|
||||
android:minWidth="56dp"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingBottom="4dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:src="@{entry.appIcon}"
|
||||
tools:src="@android:mipmap/sym_def_app_icon" />
|
||||
</LinearLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="marquee"
|
||||
android:singleLine="true"
|
||||
android:text='@{entry.name}'
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
tools:text="Mozilla Location Service" />
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/two_target_divider"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="start|center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="16dp">
|
||||
|
||||
<View
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?android:attr/listDivider" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:minWidth="64dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:checked="@={entry.enabled}"
|
||||
android:minWidth="80dp"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</layout>
|
10
ui/src/main/res/values-de/strings.xml
Normal file
10
ui/src/main/res/values-de/strings.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ SPDX-FileCopyrightText: 2014, microG Project Team
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="network_location">Funknetz-basierte Ortung</string>
|
||||
<string name="geocoding">Adressauflösung</string>
|
||||
</resources>
|
10
ui/src/main/res/values-eo/strings.xml
Normal file
10
ui/src/main/res/values-eo/strings.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ SPDX-FileCopyrightText: 2014, microG Project Team
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="network_location">Ret-bazita pozici-trovado</string>
|
||||
<string name="geocoding">Adres-elserĉado</string>
|
||||
</resources>
|
10
ui/src/main/res/values-fr/strings.xml
Normal file
10
ui/src/main/res/values-fr/strings.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ SPDX-FileCopyrightText: 2014, microG Project Team
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="network_location">Géolocalisation réseau</string>
|
||||
<string name="geocoding">Géocodage</string>
|
||||
</resources>
|
10
ui/src/main/res/values-pl/strings.xml
Normal file
10
ui/src/main/res/values-pl/strings.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ SPDX-FileCopyrightText: 2014, microG Project Team
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="network_location">Geolokalizacja oparta o sieć</string>
|
||||
<string name="geocoding">Wyszukiwanie adresów</string>
|
||||
</resources>
|
10
ui/src/main/res/values-ro/strings.xml
Normal file
10
ui/src/main/res/values-ro/strings.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ SPDX-FileCopyrightText: 2014, microG Project Team
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="network_location">Localizare geografică bazată pe rețea</string>
|
||||
<string name="geocoding">Căutare adrese</string>
|
||||
</resources>
|
10
ui/src/main/res/values-ru/strings.xml
Normal file
10
ui/src/main/res/values-ru/strings.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ SPDX-FileCopyrightText: 2014, microG Project Team
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="network_location">Местоположение по сети</string>
|
||||
<string name="geocoding">Поиск адреса</string>
|
||||
</resources>
|
10
ui/src/main/res/values-sr/strings.xml
Normal file
10
ui/src/main/res/values-sr/strings.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ SPDX-FileCopyrightText: 2014, microG Project Team
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="network_location">Мрежна геолокација</string>
|
||||
<string name="geocoding">Потрага адресе</string>
|
||||
</resources>
|
10
ui/src/main/res/values-uk/strings.xml
Normal file
10
ui/src/main/res/values-uk/strings.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ SPDX-FileCopyrightText: 2014, microG Project Team
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="network_location">Географічне позиціювання на основі мереж</string>
|
||||
<string name="geocoding">Пошук адрес</string>
|
||||
</resources>
|
10
ui/src/main/res/values-zh-rTW/strings.xml
Normal file
10
ui/src/main/res/values-zh-rTW/strings.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ SPDX-FileCopyrightText: 2014, microG Project Team
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="network_location">基於網路的地理位置定位</string>
|
||||
<string name="geocoding">地址查閱</string>
|
||||
</resources>
|
10
ui/src/main/res/values/strings.xml
Normal file
10
ui/src/main/res/values/strings.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ SPDX-FileCopyrightText: 2014, microG Project Team
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="network_location">Network-based Geolocation</string>
|
||||
<string name="geocoding">Address lookup</string>
|
||||
</resources>
|
Loading…
Reference in a new issue