addrs);
43 |
44 | /**
45 | * Returns the Binder interface for the geocode provider.
46 | * This is intended to be used for the onBind() method of
47 | * a service that implements a geocoder service.
48 | *
49 | * @return the IBinder instance for the provider
50 | */
51 | public IBinder getBinder() {
52 | return null;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/compat/src/current/java/com/android/location/provider/LocationProviderBase.java:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2010, The Android Open Source Project
3 | * SPDX-FileCopyrightText: 2014, microG Project Team
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 |
7 | package com.android.location.provider;
8 |
9 | import android.location.Location;
10 | import android.location.LocationManager;
11 | import android.os.Bundle;
12 | import android.os.IBinder;
13 | import android.os.WorkSource;
14 |
15 | import java.io.FileDescriptor;
16 | import java.io.PrintWriter;
17 |
18 | /**
19 | * Base class for location providers implemented as unbundled services.
20 | *
21 | * The network location provider must export a service with action
22 | * "com.android.location.service.v2.NetworkLocationProvider"
23 | * and a valid minor version in a meta-data field on the service, and
24 | * then return the result of {@link #getBinder()} on service binding.
25 | *
26 | * The fused location provider must export a service with action
27 | * "com.android.location.service.FusedLocationProvider"
28 | * and a valid minor version in a meta-data field on the service, and
29 | * then return the result of {@link #getBinder()} on service binding.
30 | *
31 | * IMPORTANT: This class is effectively a public API for unbundled
32 | * applications, and must remain API stable. See README.txt in the root
33 | * of this package for more information.
34 | */
35 | public abstract class LocationProviderBase {
36 |
37 | /**
38 | * Bundle key for a version of the location containing no GPS data.
39 | * Allows location providers to flag locations as being safe to
40 | * feed to LocationFudger.
41 | */
42 | public static final String EXTRA_NO_GPS_LOCATION = Location.EXTRA_NO_GPS_LOCATION;
43 |
44 | /**
45 | * Name of the Fused location provider.
46 | *
47 | * This provider combines inputs for all possible location sources
48 | * to provide the best possible Location fix.
49 | */
50 | public static final String FUSED_PROVIDER = LocationManager.FUSED_PROVIDER;
51 |
52 | public LocationProviderBase(String tag, ProviderPropertiesUnbundled properties) {
53 | }
54 |
55 | public IBinder getBinder() {
56 | return null;
57 | }
58 |
59 | /**
60 | * Used by the location provider to report new locations.
61 | *
62 | * @param location new Location to report
63 | *
64 | * Requires the android.permission.INSTALL_LOCATION_PROVIDER permission.
65 | */
66 | public final void reportLocation(Location location) {
67 | }
68 |
69 | /**
70 | * Enable the location provider.
71 | * The provider may initialize resources, but does
72 | * not yet need to report locations.
73 | */
74 | public abstract void onEnable();
75 |
76 | /**
77 | * Disable the location provider.
78 | *
The provider must release resources, and stop
79 | * performing work. It may no longer report locations.
80 | */
81 | public abstract void onDisable();
82 |
83 | /**
84 | * Set the {@link ProviderRequest} requirements for this provider.
85 | *
Each call to this method overrides all previous requests.
86 | *
This method might trigger the provider to start returning
87 | * locations, or to stop returning locations, depending on the
88 | * parameters in the request.
89 | */
90 | public abstract void onSetRequest(ProviderRequestUnbundled request, WorkSource source);
91 |
92 | /**
93 | * Dump debug information.
94 | */
95 | public void onDump(FileDescriptor fd, PrintWriter pw, String[] args) {
96 | }
97 |
98 | /**
99 | * Returns a information on the status of this provider.
100 | *
{@link android.location.LocationProvider#OUT_OF_SERVICE} is returned if the provider is
101 | * out of service, and this is not expected to change in the near
102 | * future; {@link android.location.LocationProvider#TEMPORARILY_UNAVAILABLE} is returned if
103 | * the provider is temporarily unavailable but is expected to be
104 | * available shortly; and {@link android.location.LocationProvider#AVAILABLE} is returned
105 | * if the provider is currently available.
106 | *
107 | * If extras is non-null, additional status information may be
108 | * added to it in the form of provider-specific key/value pairs.
109 | */
110 | public abstract int onGetStatus(Bundle extras);
111 |
112 | /**
113 | * Returns the time at which the status was last updated. It is the
114 | * responsibility of the provider to appropriately set this value using
115 | * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}.
116 | * there is a status update that it wishes to broadcast to all its
117 | * listeners. The provider should be careful not to broadcast
118 | * the same status again.
119 | *
120 | * @return time of last status update in millis since last reboot
121 | */
122 | public abstract long onGetStatusUpdateTime();
123 |
124 | /**
125 | * Implements addditional location provider specific additional commands.
126 | *
127 | * @param command name of the command to send to the provider.
128 | * @param extras optional arguments for the command (or null).
129 | * The provider may optionally fill the extras Bundle with results from the command.
130 | * @return true if the command succeeds.
131 | */
132 | public boolean onSendExtraCommand(String command, Bundle extras) {
133 | return false;
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/compat/src/current/java/com/android/location/provider/LocationRequestUnbundled.java:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2012, The Android Open Source Project
3 | * SPDX-FileCopyrightText: 2014, microG Project Team
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 |
7 | package com.android.location.provider;
8 |
9 | import android.location.LocationRequest;
10 |
11 | /**
12 | * This class is an interface to LocationRequests for unbundled applications.
13 | *
14 | * IMPORTANT: This class is effectively a public API for unbundled
15 | * applications, and must remain API stable. See README.txt in the root
16 | * of this package for more information.
17 | */
18 | public final class LocationRequestUnbundled {
19 | /**
20 | * Returned by {@link #getQuality} when requesting the most accurate locations available.
21 | *
22 | * This may be up to 1 meter accuracy, although this is implementation dependent.
23 | */
24 | public static final int ACCURACY_FINE = LocationRequest.ACCURACY_FINE;
25 |
26 | /**
27 | * Returned by {@link #getQuality} when requesting "block" level accuracy.
28 | *
29 | * Block level accuracy is considered to be about 100 meter accuracy,
30 | * although this is implementation dependent. Using a coarse accuracy
31 | * such as this often consumes less power.
32 | */
33 | public static final int ACCURACY_BLOCK = LocationRequest.ACCURACY_BLOCK;
34 |
35 | /**
36 | * Returned by {@link #getQuality} when requesting "city" level accuracy.
37 | *
38 | * City level accuracy is considered to be about 10km accuracy,
39 | * although this is implementation dependent. Using a coarse accuracy
40 | * such as this often consumes less power.
41 | */
42 | public static final int ACCURACY_CITY = LocationRequest.ACCURACY_CITY;
43 |
44 | /**
45 | * Returned by {@link #getQuality} when requiring no direct power impact (passive locations).
46 | *
47 | * This location request will not trigger any active location requests,
48 | * but will receive locations triggered by other applications. Your application
49 | * will not receive any direct power blame for location work.
50 | */
51 | public static final int POWER_NONE = LocationRequest.POWER_NONE;
52 |
53 | /**
54 | * Returned by {@link #getQuality} when requesting low power impact.
55 | *
56 | * This location request will avoid high power location work where
57 | * possible.
58 | */
59 | public static final int POWER_LOW = LocationRequest.POWER_LOW;
60 |
61 | /**
62 | * Returned by {@link #getQuality} when allowing high power consumption for location.
63 | *
64 | * This location request will allow high power location work.
65 | */
66 | public static final int POWER_HIGH = LocationRequest.POWER_HIGH;
67 |
68 | /**
69 | * Get the desired interval of this request, in milliseconds.
70 | *
71 | * @return desired interval in milliseconds, inexact
72 | */
73 | public long getInterval() {
74 | return 0;
75 | }
76 |
77 | /**
78 | * Get the fastest interval of this request, in milliseconds.
79 | *
80 | * The system will never provide location updates faster
81 | * than the minimum of {@link #getFastestInterval} and
82 | * {@link #getInterval}.
83 | *
84 | * @return fastest interval in milliseconds, exact
85 | */
86 | public long getFastestInterval() {
87 | return 0;
88 | }
89 |
90 | /**
91 | * Get the quality of the request.
92 | *
93 | * @return an accuracy or power constant
94 | */
95 | public int getQuality() {
96 | return 0;
97 | }
98 |
99 | /**
100 | * Get the minimum distance between location updates, in meters.
101 | *
102 | * @return minimum distance between location updates in meters
103 | */
104 | public float getSmallestDisplacement() {
105 | return 0;
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/compat/src/current/java/com/android/location/provider/ProviderPropertiesUnbundled.java:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2012, The Android Open Source Project
3 | * SPDX-FileCopyrightText: 2014, microG Project Team
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 |
7 | package com.android.location.provider;
8 |
9 | import com.android.internal.location.ProviderProperties;
10 |
11 | /**
12 | * This class is an interface to Provider Properties for unbundled applications.
13 | *
14 | * IMPORTANT: This class is effectively a public API for unbundled
15 | * applications, and must remain API stable. See README.txt in the root
16 | * of this package for more information.
17 | */
18 | public final class ProviderPropertiesUnbundled {
19 | public static ProviderPropertiesUnbundled create(boolean requiresNetwork,
20 | boolean requiresSatellite, boolean requiresCell, boolean hasMonetaryCost,
21 | boolean supportsAltitude, boolean supportsSpeed, boolean supportsBearing,
22 | int powerRequirement, int accuracy) {
23 | return null;
24 | }
25 |
26 | public ProviderProperties getProviderProperties() {
27 | return null;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/compat/src/current/java/com/android/location/provider/ProviderRequestUnbundled.java:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2012, The Android Open Source Project
3 | * SPDX-FileCopyrightText: 2014, microG Project Team
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 |
7 | package com.android.location.provider;
8 |
9 | import com.android.internal.location.ProviderRequest;
10 |
11 | import java.util.List;
12 |
13 | /**
14 | * This class is an interface to Provider Requests for unbundled applications.
15 | *
16 | * IMPORTANT: This class is effectively a public API for unbundled
17 | * applications, and must remain API stable. See README.txt in the root
18 | * of this package for more information.
19 | */
20 | public final class ProviderRequestUnbundled {
21 | public ProviderRequestUnbundled(ProviderRequest request) {
22 | }
23 |
24 | public boolean getReportLocation() {
25 | return false;
26 | }
27 |
28 | public long getInterval() {
29 | return 0;
30 | }
31 |
32 | /**
33 | * Never null.
34 | */
35 | public List getLocationRequests() {
36 | return null;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/docs/backend-sample/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2013, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | apply plugin: 'com.android.application'
7 |
8 | dependencies {
9 | implementation project(':api')
10 | }
11 |
12 | android {
13 | compileSdkVersion androidCompileSdk
14 | buildToolsVersion "$androidBuildVersionTools"
15 |
16 | defaultConfig {
17 | minSdkVersion androidMinSdk
18 | targetSdkVersion androidTargetSdk
19 | }
20 |
21 | compileOptions {
22 | sourceCompatibility JavaVersion.VERSION_1_8
23 | targetCompatibility JavaVersion.VERSION_1_8
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/docs/backend-sample/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
16 |
17 |
22 |
23 |
24 |
25 |
26 |
27 |
32 |
33 |
34 |
35 |
38 |
39 |
40 |
44 |
45 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/docs/backend-sample/src/main/java/org/microg/nlp/api/sample/SampleBackendService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2013, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.api.sample;
7 |
8 | import android.location.Location;
9 | import android.util.Log;
10 | import org.microg.nlp.api.LocationBackendService;
11 | import org.microg.nlp.api.LocationHelper;
12 |
13 | public class SampleBackendService extends LocationBackendService {
14 | private static final String TAG = SampleBackendService.class.getName();
15 |
16 | @Override
17 | protected Location update() {
18 | if (System.currentTimeMillis() % 60000 > 2000) {
19 | Log.d(TAG, "I decided not to answer now...");
20 | return null;
21 | }
22 | Location location = LocationHelper.create("sample", 42, 42, 42);
23 | Log.d(TAG, "I was asked for location and I answer: " + location);
24 | return location;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/docs/backend-sample/src/main/java/org/microg/nlp/api/sample/SecondSampleService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2013, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.api.sample;
7 |
8 | import android.location.Location;
9 | import android.util.Log;
10 | import org.microg.nlp.api.LocationBackendService;
11 | import org.microg.nlp.api.LocationHelper;
12 |
13 | public class SecondSampleService extends LocationBackendService {
14 | private static final String TAG = SecondSampleService.class.getName();
15 |
16 | @Override
17 | protected Location update() {
18 | Location location = LocationHelper.create("second-sample", 13, 13, (System.currentTimeMillis() / 1000) % 100);
19 | Log.d(TAG, "I was asked for location and I answer: " + location);
20 | return location;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/docs/backend-sample/src/main/java/org/microg/nlp/api/sample/SecondSettings.java:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2013, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.api.sample;
7 |
8 | import android.app.Activity;
9 | import android.os.Bundle;
10 |
11 | public class SecondSettings extends Activity {
12 | public void onCreate(Bundle savedInstanceState) {
13 | super.onCreate(savedInstanceState);
14 | }
15 | }
--------------------------------------------------------------------------------
/docs/backend-sample/src/main/java/org/microg/nlp/api/sample/ThirdSampleService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2013, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.api.sample;
7 |
8 | import android.location.Location;
9 | import android.util.Log;
10 | import org.microg.nlp.api.LocationBackendService;
11 | import org.microg.nlp.api.LocationHelper;
12 |
13 | import java.util.Random;
14 |
15 | public class ThirdSampleService extends LocationBackendService {
16 | private static final String TAG = ThirdSampleService.class.getName();
17 |
18 | private Thread regular;
19 | private final Random random = new Random();
20 |
21 | @Override
22 | protected void onOpen() {
23 | super.onOpen();
24 | regular = new Thread(new Runnable() {
25 | @Override
26 | public void run() {
27 | synchronized (regular) {
28 | while (!regular.isInterrupted()) {
29 | try {
30 | regular.wait(60000);
31 | } catch (InterruptedException e) {
32 | return;
33 | }
34 | Location location = LocationHelper.create("random", random.nextDouble() * 90, random.nextDouble() * 90, random.nextFloat() * 90);
35 | Log.d(TAG, "Just reported: " + location);
36 | report(location);
37 | }
38 | }
39 | }
40 | });
41 | regular.start();
42 | }
43 |
44 | @Override
45 | protected void onClose() {
46 | if (regular != null && regular.isAlive()) {
47 | regular.interrupt();
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/docs/backend-sample/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | NetworkLocationV2-SamplePlugin
9 |
10 |
--------------------------------------------------------------------------------
/geocode-v1/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2019, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | apply plugin: 'com.android.library'
7 | apply plugin: 'kotlin-android'
8 | apply plugin: 'maven-publish'
9 | apply plugin: 'signing'
10 |
11 | android {
12 | compileSdkVersion androidCompileSdk
13 | buildToolsVersion "$androidBuildVersionTools"
14 |
15 | defaultConfig {
16 | versionName version
17 | minSdkVersion androidMinSdk
18 | targetSdkVersion androidTargetSdk
19 | }
20 |
21 | compileOptions {
22 | sourceCompatibility = 1.8
23 | targetCompatibility = 1.8
24 | }
25 | }
26 |
27 | apply from: '../gradle/publish.gradle'
28 |
29 | description = 'UnifiedNlp service to implement Geocode API v1'
30 |
31 | dependencies {
32 | implementation project(':client')
33 | compileOnly project(':compat')
34 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
35 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion"
36 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutineVersion"
37 | implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion"
38 | implementation "androidx.lifecycle:lifecycle-service:$lifecycleVersion"
39 | }
40 |
--------------------------------------------------------------------------------
/geocode-v1/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
19 |
23 |
24 |
25 |
26 |
27 |
28 |
31 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/geocode-v1/src/main/java/org/microg/nlp/geocode/v1/GeocodeProvider.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2019, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 | package org.microg.nlp.geocode.v1
6 |
7 | import android.content.Context
8 | import android.location.Address
9 | import android.location.GeocoderParams
10 | import android.os.Bundle
11 | import android.util.Log
12 | import androidx.lifecycle.Lifecycle
13 | import androidx.lifecycle.LifecycleOwner
14 | import com.android.location.provider.GeocodeProvider
15 | import org.microg.nlp.client.GeocodeClient
16 | import org.microg.nlp.service.api.GeocodeRequest
17 | import org.microg.nlp.service.api.LatLon
18 | import org.microg.nlp.service.api.LatLonBounds
19 | import org.microg.nlp.service.api.ReverseGeocodeRequest
20 |
21 | class GeocodeProvider(context: Context, lifecycle: Lifecycle) : GeocodeProvider() {
22 | private val client: GeocodeClient = GeocodeClient(context, lifecycle)
23 |
24 | override fun onGetFromLocation(latitude: Double, longitude: Double, maxResults: Int, params: GeocoderParams, addrs: MutableList): String? {
25 | return try {
26 | handleResult(addrs, client.requestReverseGeocodeSync(
27 | ReverseGeocodeRequest(LatLon(latitude, longitude), maxResults, params.locale),
28 | Bundle().apply { putString("packageName", params.clientPackage) }
29 | ))
30 | } catch (e: Exception) {
31 | Log.w(TAG, e)
32 | e.message
33 | }
34 | }
35 |
36 | override fun onGetFromLocationName(locationName: String?, lowerLeftLatitude: Double, lowerLeftLongitude: Double, upperRightLatitude: Double, upperRightLongitude: Double, maxResults: Int, params: GeocoderParams, addrs: MutableList): String? {
37 | return try {
38 | handleResult(addrs, client.requestGeocodeSync(
39 | GeocodeRequest(locationName!!, LatLonBounds(LatLon(lowerLeftLatitude, lowerLeftLongitude), LatLon(upperRightLatitude, upperRightLongitude)), maxResults, params.locale),
40 | Bundle().apply { putString("packageName", params.clientPackage) }
41 | ))
42 | } catch (e: Exception) {
43 | Log.w(TAG, e)
44 | e.message
45 | }
46 | }
47 |
48 | private fun handleResult(realResult: MutableList, fuserResult: List): String? {
49 | return if (fuserResult.isEmpty()) {
50 | "no result"
51 | } else {
52 | realResult.addAll(fuserResult)
53 | null
54 | }
55 | }
56 |
57 | suspend fun connect() = client.connect()
58 | suspend fun disconnect() = client.disconnect()
59 |
60 | companion object {
61 | private const val TAG = "GeocodeProvider"
62 | private const val TIMEOUT: Long = 10000
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/geocode-v1/src/main/java/org/microg/nlp/geocode/v1/GeocodeService.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2019, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 | package org.microg.nlp.geocode.v1
6 |
7 | import android.content.Intent
8 | import android.os.IBinder
9 | import android.util.Log
10 | import androidx.lifecycle.LifecycleService
11 | import androidx.lifecycle.lifecycleScope
12 | import kotlinx.coroutines.delay
13 | import kotlinx.coroutines.runBlocking
14 |
15 | class GeocodeService : LifecycleService() {
16 | private lateinit var provider: GeocodeProvider
17 |
18 | override fun onCreate() {
19 | super.onCreate()
20 | Log.d(TAG, "Creating system service...")
21 | provider = GeocodeProvider(this, lifecycle)
22 | lifecycleScope.launchWhenStarted {
23 | delay(5000)
24 | provider.connect()
25 | }
26 | Log.d(TAG, "Created system service.")
27 | }
28 |
29 | override fun onBind(intent: Intent): IBinder? {
30 | super.onBind(intent)
31 | Log.d(TAG, "onBind: $intent")
32 | return provider.binder
33 | }
34 |
35 | override fun onUnbind(intent: Intent): Boolean {
36 | return super.onUnbind(intent)
37 | }
38 |
39 | override fun onDestroy() {
40 | runBlocking { provider.disconnect() }
41 | super.onDestroy()
42 | }
43 |
44 | companion object {
45 | private const val TAG = "GeocodeService"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2020, microG Project Team
2 | # SPDX-License-Identifier: CC0-1.0
3 |
4 | #android.enableJetifier=true
5 | android.useAndroidX=true
6 |
--------------------------------------------------------------------------------
/gradle/publish.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2020 microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | task androidSourcesJar(type: Jar) {
7 | classifier = 'sources'
8 | from android.sourceSets.main.java.source
9 | }
10 |
11 | artifacts {
12 | archives androidSourcesJar
13 | }
14 |
15 | afterEvaluate {
16 | publishing {
17 | publications {
18 | release(MavenPublication) {
19 | pom {
20 | name = project.name
21 | description = project.description
22 | url = 'https://github.com/microg/UnifiedNlp'
23 | licenses {
24 | license {
25 | name = 'The Apache Software License, Version 2.0'
26 | url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
27 | }
28 | }
29 | developers {
30 | developer {
31 | id = 'microg'
32 | name = 'microG Team'
33 | }
34 | }
35 | scm {
36 | url = 'https://github.com/microg/UnifiedNlp'
37 | connection = 'scm:git:https://github.com/microg/UnifiedNlp.git'
38 | developerConnection = 'scm:git:ssh://github.com/microg/UnifiedNlp.git'
39 | }
40 | }
41 |
42 | from components.release
43 | artifact androidSourcesJar
44 | }
45 | }
46 | if (project.hasProperty('sonatype.username')) {
47 | repositories {
48 | maven {
49 | name = 'sonatype'
50 | url = 'https://oss.sonatype.org/service/local/staging/deploy/maven2/'
51 | credentials {
52 | username project.getProperty('sonatype.username')
53 | password project.getProperty('sonatype.password')
54 | }
55 | }
56 | }
57 | }
58 | }
59 | if (project.hasProperty('signing.keyId')) {
60 | signing {
61 | sign publishing.publications
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microg/UnifiedNlp/38a857f034d688fe19bcf41030d31478d90668a0/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar.license:
--------------------------------------------------------------------------------
1 | Copyright 2015 the original author or authors.
2 | SPDX-License-Identifier: Apache-2.0
3 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | # SPDX-FileCopyrightText: 2015, microG Project Team
2 | # SPDX-License-Identifier: CC0-1.0
3 |
4 | distributionBase=GRADLE_USER_HOME
5 | distributionPath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
7 | zipStoreBase=GRADLE_USER_HOME
8 | zipStorePath=wrapper/dists
9 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem SPDX-License-Identifier: Apache-2.0
2 | @rem
3 | @rem Copyright 2015 the original author or authors.
4 | @rem
5 | @rem Licensed under the Apache License, Version 2.0 (the "License");
6 | @rem you may not use this file except in compliance with the License.
7 | @rem You may obtain a copy of the License at
8 | @rem
9 | @rem https://www.apache.org/licenses/LICENSE-2.0
10 | @rem
11 | @rem Unless required by applicable law or agreed to in writing, software
12 | @rem distributed under the License is distributed on an "AS IS" BASIS,
13 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | @rem See the License for the specific language governing permissions and
15 | @rem limitations under the License.
16 | @rem
17 |
18 | @if "%DEBUG%" == "" @echo off
19 | @rem ##########################################################################
20 | @rem
21 | @rem Gradle startup script for Windows
22 | @rem
23 | @rem ##########################################################################
24 |
25 | @rem Set local scope for the variables with windows NT shell
26 | if "%OS%"=="Windows_NT" setlocal
27 |
28 | set DIRNAME=%~dp0
29 | if "%DIRNAME%" == "" set DIRNAME=.
30 | set APP_BASE_NAME=%~n0
31 | set APP_HOME=%DIRNAME%
32 |
33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35 |
36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
38 |
39 | @rem Find java.exe
40 | if defined JAVA_HOME goto findJavaFromJavaHome
41 |
42 | set JAVA_EXE=java.exe
43 | %JAVA_EXE% -version >NUL 2>&1
44 | if "%ERRORLEVEL%" == "0" goto execute
45 |
46 | echo.
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
48 | echo.
49 | echo Please set the JAVA_HOME variable in your environment to match the
50 | echo location of your Java installation.
51 |
52 | goto fail
53 |
54 | :findJavaFromJavaHome
55 | set JAVA_HOME=%JAVA_HOME:"=%
56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 |
58 | if exist "%JAVA_EXE%" goto execute
59 |
60 | echo.
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
62 | echo.
63 | echo Please set the JAVA_HOME variable in your environment to match the
64 | echo location of your Java installation.
65 |
66 | goto fail
67 |
68 | :execute
69 | @rem Setup the command line
70 |
71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72 |
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/location-v2/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2019, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | apply plugin: 'com.android.library'
7 | apply plugin: 'kotlin-android'
8 | apply plugin: 'maven-publish'
9 | apply plugin: 'signing'
10 |
11 | android {
12 | compileSdkVersion androidCompileSdk
13 | buildToolsVersion "$androidBuildVersionTools"
14 |
15 | defaultConfig {
16 | versionName version
17 | minSdkVersion androidMinSdk
18 | targetSdkVersion androidTargetSdk
19 | }
20 |
21 | compileOptions {
22 | sourceCompatibility = 1.8
23 | targetCompatibility = 1.8
24 | }
25 | }
26 |
27 | apply from: '../gradle/publish.gradle'
28 |
29 | description = 'UnifiedNlp service to implement Location API v2'
30 |
31 | dependencies {
32 | implementation project(':client')
33 | compileOnly project(':compat')
34 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
35 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion"
36 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutineVersion"
37 | implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion"
38 | implementation "androidx.lifecycle:lifecycle-service:$lifecycleVersion"
39 | }
40 |
--------------------------------------------------------------------------------
/location-v2/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
26 |
27 |
28 |
29 |
30 |
33 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/location-v2/src/main/java/org/microg/nlp/location/v2/LocationProvider.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2019, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 | package org.microg.nlp.location.v2
6 |
7 | import android.content.Context
8 | import android.location.Criteria
9 | import android.location.Location
10 | import android.location.LocationProvider
11 | import android.os.Bundle
12 | import android.os.SystemClock
13 | import android.os.WorkSource
14 | import android.util.Log
15 | import androidx.lifecycle.Lifecycle
16 | import androidx.lifecycle.LifecycleOwner
17 | import androidx.lifecycle.lifecycleScope
18 | import com.android.location.provider.LocationProviderBase
19 | import com.android.location.provider.ProviderPropertiesUnbundled
20 | import com.android.location.provider.ProviderRequestUnbundled
21 | import kotlinx.coroutines.launch
22 | import org.microg.nlp.client.LocationClient
23 | import org.microg.nlp.service.api.Constants.STATUS_OK
24 | import org.microg.nlp.service.api.ILocationListener
25 | import org.microg.nlp.service.api.LocationRequest
26 | import java.io.FileDescriptor
27 | import java.io.PrintWriter
28 | import java.util.*
29 |
30 | class LocationProvider(private val context: Context, private val lifecycle: Lifecycle) : LocationProviderBase(TAG, ProviderPropertiesUnbundled.create(false, false, false, false, true, true, true, Criteria.POWER_LOW, Criteria.ACCURACY_COARSE)), LifecycleOwner {
31 | private val client: LocationClient = LocationClient(context, lifecycle)
32 | private val id = UUID.randomUUID().toString()
33 | private var statusUpdateTime = SystemClock.elapsedRealtime()
34 | private val listener = object : ILocationListener.Stub() {
35 | override fun onLocation(statusCode: Int, location: Location?) {
36 | if (statusCode == STATUS_OK && location != null) {
37 | val reportableLocation = Location(location)
38 | for (key in reportableLocation.extras.keySet().toList()) {
39 | if (key?.startsWith("org.microg.nlp.") == true) {
40 | reportableLocation.extras.remove(key)
41 | }
42 | }
43 | Log.d(TAG, "reportLocation: $reportableLocation")
44 | reportLocation(reportableLocation)
45 | }
46 | }
47 | }
48 | private var opPackageName: String? = null
49 | private var opPackageNames: Set = emptySet()
50 | private var autoTime = Long.MAX_VALUE
51 | private var autoUpdate = false
52 |
53 | init {
54 | client.defaultOptions.putString("source", "LocationProvider")
55 | client.defaultOptions.putString("requestId", id)
56 | }
57 |
58 | override fun onEnable() {
59 | Log.d(TAG, "onEnable")
60 | statusUpdateTime = SystemClock.elapsedRealtime()
61 | }
62 |
63 | override fun onDisable() {
64 | Log.d(TAG, "onDisable")
65 | unsetRequest()
66 | statusUpdateTime = SystemClock.elapsedRealtime()
67 | }
68 |
69 | override fun onSetRequest(requests: ProviderRequestUnbundled, source: WorkSource) {
70 | Log.v(TAG, "onSetRequest: $requests by $source")
71 | opPackageName = null
72 | try {
73 | val namesField = WorkSource::class.java.getDeclaredField("mNames")
74 | namesField.isAccessible = true
75 | val names = namesField[source] as Array
76 | if (names != null) {
77 | opPackageNames = setOfNotNull(*names)
78 | for (name in names) {
79 | if (!EXCLUDED_PACKAGES.contains(name)) {
80 | opPackageName = name
81 | break
82 | }
83 | }
84 | if (opPackageName == null && names.isNotEmpty()) opPackageName = names[0]
85 | } else {
86 | opPackageNames = emptySet()
87 | }
88 | } catch (ignored: Exception) {
89 | }
90 | autoTime = requests.interval.coerceAtLeast(FASTEST_REFRESH_INTERVAL)
91 | autoUpdate = requests.reportLocation
92 | Log.v(TAG, "using autoUpdate=$autoUpdate autoTime=$autoTime")
93 | lifecycleScope.launch {
94 | updateRequest()
95 | }
96 | }
97 |
98 | suspend fun updateRequest() {
99 | if (client.isConnected()) {
100 | if (autoUpdate) {
101 | client.packageName = opPackageName ?: context.packageName
102 | client.updateLocationRequest(LocationRequest(listener, autoTime, Int.MAX_VALUE, id))
103 | } else {
104 | client.cancelLocationRequestById(id)
105 | }
106 | }
107 | }
108 |
109 | fun unsetRequest() {
110 | lifecycleScope.launch {
111 | client.cancelLocationRequestById(id)
112 | }
113 | }
114 |
115 | override fun onGetStatus(extras: Bundle?): Int {
116 | return LocationProvider.AVAILABLE
117 | }
118 |
119 | override fun onGetStatusUpdateTime(): Long {
120 | return statusUpdateTime
121 | }
122 |
123 | override fun onSendExtraCommand(command: String?, extras: Bundle?): Boolean {
124 | Log.d(TAG, "onSendExtraCommand: $command, $extras")
125 | return false
126 | }
127 |
128 | override fun onDump(fd: FileDescriptor?, pw: PrintWriter?, args: Array?) {
129 | dump(pw)
130 | }
131 |
132 | fun dump(writer: PrintWriter?) {
133 | writer?.println("ID: $id")
134 | writer?.println("connected: ${client.isConnectedUnsafe}")
135 | writer?.println("active: $autoUpdate")
136 | writer?.println("interval: $autoTime")
137 | writer?.println("${opPackageNames.size} sources:")
138 | for (packageName in opPackageNames) {
139 | writer?.println(" $packageName")
140 | }
141 | }
142 |
143 | suspend fun connect() {
144 | Log.d(TAG, "Connecting to userspace service...")
145 | client.connect()
146 | updateRequest()
147 | Log.d(TAG, "Connected to userspace service.")
148 | }
149 | suspend fun disconnect() = client.disconnect()
150 |
151 | override fun getLifecycle(): Lifecycle = lifecycle
152 |
153 | companion object {
154 | private val EXCLUDED_PACKAGES = listOf("android", "com.android.location.fused", "com.google.android.gms")
155 | private const val FASTEST_REFRESH_INTERVAL: Long = 2500
156 | private const val TAG = "LocationProvider"
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/location-v2/src/main/java/org/microg/nlp/location/v2/LocationService.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2019, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 | package org.microg.nlp.location.v2
6 |
7 | import androidx.lifecycle.LifecycleService
8 | import android.content.Intent
9 | import android.os.IBinder
10 | import android.util.Log
11 | import androidx.lifecycle.lifecycleScope
12 | import kotlinx.coroutines.delay
13 | import kotlinx.coroutines.runBlocking
14 | import java.io.FileDescriptor
15 | import java.io.PrintWriter
16 |
17 | open class LocationService : LifecycleService() {
18 | private lateinit var provider: LocationProvider
19 |
20 | override fun onCreate() {
21 | super.onCreate()
22 | Log.d(TAG, "Creating system service...")
23 | provider = LocationProvider(this, lifecycle)
24 | lifecycleScope.launchWhenStarted {
25 | delay(5000)
26 | provider.connect()
27 | }
28 | Log.d(TAG, "Created system service.")
29 | }
30 |
31 | override fun onBind(intent: Intent): IBinder? {
32 | super.onBind(intent)
33 | Log.d(TAG, "onBind: $intent")
34 | return provider.binder
35 | }
36 |
37 | override fun onDestroy() {
38 | runBlocking { provider.disconnect() }
39 | super.onDestroy()
40 | }
41 |
42 | override fun dump(fd: FileDescriptor?, writer: PrintWriter?, args: Array?) {
43 | if (!this::provider.isInitialized) {
44 | writer?.println("Not yet initialized")
45 | }
46 | provider.dump(writer)
47 | }
48 |
49 | companion object {
50 | private const val TAG = "LocationService"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/location-v3/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2019, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | apply plugin: 'com.android.library'
7 | apply plugin: 'kotlin-android'
8 | apply plugin: 'maven-publish'
9 | apply plugin: 'signing'
10 |
11 | android {
12 | compileSdkVersion androidCompileSdk
13 | buildToolsVersion "$androidBuildVersionTools"
14 |
15 | defaultConfig {
16 | versionName version
17 | minSdkVersion androidMinSdk
18 | targetSdkVersion androidTargetSdk
19 | }
20 |
21 | compileOptions {
22 | sourceCompatibility = 1.8
23 | targetCompatibility = 1.8
24 | }
25 | }
26 |
27 | apply from: '../gradle/publish.gradle'
28 |
29 | description = 'UnifiedNlp service to implement Location API v3'
30 |
31 | dependencies {
32 | implementation project(':location-v2')
33 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
34 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion"
35 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutineVersion"
36 | implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion"
37 | implementation "androidx.lifecycle:lifecycle-service:$lifecycleVersion"
38 | }
39 |
--------------------------------------------------------------------------------
/location-v3/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
25 |
26 |
27 |
28 |
29 |
32 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/location-v3/src/main/java/org/microg/nlp/location/v3/LocationService.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2019, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 | package org.microg.nlp.location.v3
6 |
7 | import org.microg.nlp.location.v2.LocationService
8 |
9 | class LocationService : LocationService()
10 |
--------------------------------------------------------------------------------
/patches/android_frameworks_base-N.patch:
--------------------------------------------------------------------------------
1 | Remove PackageManager.MATCH_SYSTEM_ONLY flag in ServiceWatcher
2 | Patch for Android 7 "Nougat"
3 |
4 | SPDX-FileCopyrightText: 2016, microg Project Team
5 | SPDX-License-Identifier: CC0-1.0
6 |
7 | diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
8 | index 383e25a..31ae918 100644
9 | --- a/services/core/java/com/android/server/ServiceWatcher.java
10 | +++ b/services/core/java/com/android/server/ServiceWatcher.java
11 | @@ -92,8 +92,7 @@ public class ServiceWatcher implements ServiceConnection {
12 | String pkg = initialPackageNames.get(i);
13 | try {
14 | HashSet set = new HashSet();
15 | - Signature[] sigs = pm.getPackageInfo(pkg, PackageManager.MATCH_SYSTEM_ONLY
16 | - | PackageManager.GET_SIGNATURES).signatures;
17 | + Signature[] sigs = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES).signatures;
18 | set.addAll(Arrays.asList(sigs));
19 | sigSets.add(set);
20 | } catch (NameNotFoundException e) {
21 |
--------------------------------------------------------------------------------
/service-api/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2022, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | apply plugin: 'com.android.library'
7 | apply plugin: 'maven-publish'
8 | apply plugin: 'signing'
9 |
10 | android {
11 | compileSdkVersion androidCompileSdk
12 | buildToolsVersion "$androidBuildVersionTools"
13 |
14 | defaultConfig {
15 | versionName version
16 | minSdkVersion androidMinSdk
17 | targetSdkVersion androidTargetSdk
18 | }
19 |
20 | compileOptions {
21 | sourceCompatibility = 1.8
22 | targetCompatibility = 1.8
23 | }
24 | }
25 |
26 | dependencies {
27 | api "org.microg:safe-parcel:1.7.0"
28 | }
29 |
30 | apply from: '../gradle/publish.gradle'
31 |
32 | description = 'API interfaces and helpers to access UnifiedNlp service'
33 |
--------------------------------------------------------------------------------
/service-api/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/service-api/src/main/aidl/org/microg/nlp/service/api/GeocodeRequest.aidl:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2022, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.service.api;
7 |
8 | parcelable GeocodeRequest;
9 |
--------------------------------------------------------------------------------
/service-api/src/main/aidl/org/microg/nlp/service/api/IAddressesCallback.aidl:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2022, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.service.api;
7 |
8 | import android.location.Address;
9 |
10 | interface IAddressesCallback {
11 | oneway void onAddresses(int statusCode, in List location) ;
12 | }
13 |
--------------------------------------------------------------------------------
/service-api/src/main/aidl/org/microg/nlp/service/api/IGeocodeService.aidl:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2022, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.service.api;
7 |
8 | import android.location.Address;
9 | import org.microg.nlp.service.api.GeocodeRequest;
10 | import org.microg.nlp.service.api.IAddressesCallback;
11 | import org.microg.nlp.service.api.IStatusCallback;
12 | import org.microg.nlp.service.api.IStringsCallback;
13 | import org.microg.nlp.service.api.ReverseGeocodeRequest;
14 |
15 | interface IGeocodeService {
16 | oneway void requestGeocode(in GeocodeRequest request, IAddressesCallback callback, in Bundle options) = 0;
17 | oneway void requestReverseGeocode(in ReverseGeocodeRequest request, IAddressesCallback callback, in Bundle options) = 1;
18 | List requestGeocodeSync(in GeocodeRequest request, in Bundle options) = 2;
19 | List requestReverseGeocodeSync(in ReverseGeocodeRequest request, in Bundle options) = 3;
20 |
21 | oneway void reloadPreferences(IStatusCallback callback, in Bundle options) = 20;
22 | oneway void getGeocodeBackends(IStringsCallback callback, in Bundle options) = 21;
23 | oneway void setGeocodeBackends(in List backends, IStatusCallback callback, in Bundle options) = 22;
24 | }
25 |
--------------------------------------------------------------------------------
/service-api/src/main/aidl/org/microg/nlp/service/api/ILocationListener.aidl:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2022, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.service.api;
7 |
8 | import android.location.Location;
9 |
10 | interface ILocationListener {
11 | oneway void onLocation(int statusCode, in Location location);
12 | }
13 |
--------------------------------------------------------------------------------
/service-api/src/main/aidl/org/microg/nlp/service/api/ILocationService.aidl:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2022, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.service.api;
7 |
8 | import android.location.Location;
9 | import org.microg.nlp.service.api.ILocationListener;
10 | import org.microg.nlp.service.api.IStatusCallback;
11 | import org.microg.nlp.service.api.IStringsCallback;
12 | import org.microg.nlp.service.api.LocationRequest;
13 |
14 | interface ILocationService {
15 | oneway void getLastLocation(ILocationListener listener, in Bundle options) = 0;
16 | oneway void getLastLocationForBackend(String packageName, String className, String signatureDigest, ILocationListener listener, in Bundle options) = 1;
17 |
18 | oneway void updateLocationRequest(in LocationRequest request, IStatusCallback callback, in Bundle options) = 10;
19 | oneway void cancelLocationRequestByListener(ILocationListener listener, IStatusCallback callback, in Bundle options) = 11;
20 | oneway void cancelLocationRequestById(String id, IStatusCallback callback, in Bundle options) = 12;
21 | oneway void forceLocationUpdate(IStatusCallback callback, in Bundle options) = 13;
22 |
23 | oneway void reloadPreferences(IStatusCallback callback, in Bundle options) = 20;
24 | oneway void getLocationBackends(IStringsCallback callback, in Bundle options) = 21;
25 | oneway void setLocationBackends(in List backends, IStatusCallback callback, in Bundle options) = 22;
26 | }
27 |
--------------------------------------------------------------------------------
/service-api/src/main/aidl/org/microg/nlp/service/api/IStatusCallback.aidl:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2022, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.service.api;
7 |
8 | interface IStatusCallback {
9 | oneway void onStatus(int statusCode);
10 | }
11 |
--------------------------------------------------------------------------------
/service-api/src/main/aidl/org/microg/nlp/service/api/IStringsCallback.aidl:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2022, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.service.api;
7 |
8 | interface IStringsCallback {
9 | oneway void onStrings(int statusCode, in List strings);
10 | }
11 |
--------------------------------------------------------------------------------
/service-api/src/main/aidl/org/microg/nlp/service/api/LocationRequest.aidl:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2022, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.service.api;
7 |
8 | parcelable LocationRequest;
9 |
--------------------------------------------------------------------------------
/service-api/src/main/aidl/org/microg/nlp/service/api/ReverseGeocodeRequest.aidl:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2022, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.service.api;
7 |
8 | parcelable ReverseGeocodeRequest;
9 |
--------------------------------------------------------------------------------
/service-api/src/main/java/org/microg/nlp/service/api/Constants.java:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2020, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.service.api;
7 |
8 | public final class Constants {
9 | public static final int STATUS_OK = 0;
10 | public static final int STATUS_NOT_IMPLEMENTED = 1;
11 | public static final int STATUS_PERMISSION_ERROR = 2;
12 | public static final int STATUS_INVALID_ARGS = 3;
13 |
14 | public static final String ACTION_LOCATION = "org.microg.nlp.service.LOCATION";
15 | public static final String ACTION_GEOCODE = "org.microg.nlp.service.GEOCODE";
16 |
17 | public static final String LOCATION_EXTRA_BACKEND_PROVIDER = "org.microg.nlp.extra.SERVICE_BACKEND_PROVIDER";
18 | public static final String LOCATION_EXTRA_BACKEND_COMPONENT = "org.microg.nlp.extra.SERVICE_BACKEND_COMPONENT";
19 | public static final String LOCATION_EXTRA_OTHER_BACKENDS = "org.microg.nlp.extra.OTHER_BACKEND_RESULTS";
20 | }
21 |
--------------------------------------------------------------------------------
/service-api/src/main/java/org/microg/nlp/service/api/GeocodeRequest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2020, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.service.api;
7 |
8 | import org.microg.safeparcel.AutoSafeParcelable;
9 |
10 | import java.util.Locale;
11 |
12 | public class GeocodeRequest extends AutoSafeParcelable {
13 | @Field(1)
14 | public String locationName;
15 | @Field(2)
16 | public LatLonBounds bounds;
17 | @Field(3)
18 | public int maxResults;
19 | @Field(4)
20 | public String locale;
21 |
22 | private GeocodeRequest() {
23 | }
24 |
25 | public GeocodeRequest(String locationName, LatLonBounds bounds) {
26 | this(locationName, bounds, 1);
27 | }
28 |
29 | public GeocodeRequest(String locationName, LatLonBounds bounds, int maxResults) {
30 | this(locationName, bounds, maxResults, Locale.getDefault());
31 | }
32 |
33 | public GeocodeRequest(String locationName, LatLonBounds bounds, int maxResults, Locale locale) {
34 | this(locationName, bounds, maxResults, locale.toString());
35 | }
36 |
37 | public GeocodeRequest(String locationName, LatLonBounds bounds, int maxResults, String locale) {
38 | this.locationName = locationName;
39 | this.bounds = bounds;
40 | this.maxResults = maxResults;
41 | this.locale = locale;
42 | }
43 |
44 | public static final Creator CREATOR = new AutoCreator<>(GeocodeRequest.class);
45 | }
46 |
--------------------------------------------------------------------------------
/service-api/src/main/java/org/microg/nlp/service/api/LatLon.java:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2020, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.service.api;
7 |
8 | import android.os.Parcel;
9 | import android.os.Parcelable;
10 |
11 | public class LatLon implements Parcelable {
12 | private double latitude;
13 | private double longitude;
14 |
15 | public LatLon(double latitude, double longitude) {
16 | this.latitude = latitude;
17 | this.longitude = longitude;
18 | }
19 |
20 | public double getLatitude() {
21 | return latitude;
22 | }
23 |
24 | public double getLongitude() {
25 | return longitude;
26 | }
27 |
28 | public static final Creator CREATOR = new Creator() {
29 | @Override
30 | public LatLon createFromParcel(Parcel source) {
31 | return new LatLon(source.readDouble(), source.readDouble());
32 | }
33 |
34 | @Override
35 | public LatLon[] newArray(int size) {
36 | return new LatLon[size];
37 | }
38 | };
39 |
40 | @Override
41 | public int describeContents() {
42 | return 0;
43 | }
44 |
45 | @Override
46 | public void writeToParcel(Parcel dest, int flags) {
47 | dest.writeDouble(latitude);
48 | dest.writeDouble(longitude);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/service-api/src/main/java/org/microg/nlp/service/api/LatLonBounds.java:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2020, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.service.api;
7 |
8 | import org.microg.safeparcel.AutoSafeParcelable;
9 |
10 | public class LatLonBounds extends AutoSafeParcelable {
11 | @Field(1)
12 | public LatLon lowerLeft;
13 | @Field(2)
14 | public LatLon upperRight;
15 |
16 | public LatLonBounds(LatLon lowerLeft, LatLon upperRight) {
17 | this.lowerLeft = lowerLeft;
18 | this.upperRight = upperRight;
19 | }
20 |
21 | public static final Creator CREATOR = new AutoCreator<>(LatLonBounds.class);
22 | }
23 |
--------------------------------------------------------------------------------
/service-api/src/main/java/org/microg/nlp/service/api/LocationRequest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2020, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.service.api;
7 |
8 | import org.microg.safeparcel.AutoSafeParcelable;
9 |
10 | import java.util.UUID;
11 |
12 | public class LocationRequest extends AutoSafeParcelable {
13 | @Field(1)
14 | public ILocationListener listener;
15 | @Field(2)
16 | public long interval;
17 | @Field(3)
18 | public int numUpdates;
19 | @Field(4)
20 | public String id;
21 |
22 | private LocationRequest() {
23 | }
24 |
25 | public LocationRequest(ILocationListener listener, long interval) {
26 | this(listener, interval, Integer.MAX_VALUE, UUID.randomUUID().toString());
27 | }
28 |
29 | public LocationRequest(ILocationListener listener, long interval, int numUpdates) {
30 | this(listener, interval, numUpdates, UUID.randomUUID().toString());
31 | }
32 |
33 | public LocationRequest(ILocationListener listener, long interval, int numUpdates, String id) {
34 | this.listener = listener;
35 | this.interval = interval;
36 | this.numUpdates = numUpdates;
37 | this.id = id;
38 | }
39 |
40 | public static final Creator CREATOR = new AutoCreator<>(LocationRequest.class);
41 | }
42 |
--------------------------------------------------------------------------------
/service-api/src/main/java/org/microg/nlp/service/api/ReverseGeocodeRequest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2020, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.service.api;
7 |
8 | import org.microg.safeparcel.AutoSafeParcelable;
9 |
10 | import java.util.Locale;
11 |
12 | public class ReverseGeocodeRequest extends AutoSafeParcelable {
13 | @Field(1)
14 | public LatLon location;
15 | @Field(2)
16 | public int maxResults;
17 | @Field(3)
18 | public String locale;
19 |
20 | private ReverseGeocodeRequest() {
21 | }
22 |
23 | public ReverseGeocodeRequest(LatLon location) {
24 | this(location, 1);
25 | }
26 |
27 | public ReverseGeocodeRequest(LatLon location, int maxResults) {
28 | this(location, maxResults, Locale.getDefault());
29 | }
30 |
31 | public ReverseGeocodeRequest(LatLon location, int maxResults, Locale locale) {
32 | this(location, maxResults, locale.toString());
33 | }
34 |
35 | public ReverseGeocodeRequest(LatLon location, int maxResults, String locale) {
36 | this.location = location;
37 | this.maxResults = maxResults;
38 | this.locale = locale;
39 | }
40 |
41 | public static final Creator CREATOR = new AutoCreator<>(ReverseGeocodeRequest.class);
42 | }
43 |
--------------------------------------------------------------------------------
/service/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2019, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 |
7 | apply plugin: 'com.android.library'
8 | apply plugin: 'kotlin-android'
9 | apply plugin: 'maven-publish'
10 | apply plugin: 'signing'
11 |
12 | android {
13 | compileSdkVersion androidCompileSdk
14 | buildToolsVersion "$androidBuildVersionTools"
15 |
16 | defaultConfig {
17 | versionName version
18 | minSdkVersion androidMinSdk
19 | targetSdkVersion androidTargetSdk
20 | buildConfigField "String", "VERSION_NAME", "\"$version\""
21 | }
22 |
23 | sourceSets {
24 | main.java.srcDirs += 'src/main/kotlin'
25 | }
26 |
27 | compileOptions {
28 | sourceCompatibility = 1.8
29 | targetCompatibility = 1.8
30 | }
31 | }
32 |
33 | apply from: '../gradle/publish.gradle'
34 |
35 | description = 'UnifiedNlp service library'
36 |
37 | dependencies {
38 | implementation project(':api')
39 | implementation project(':service-api')
40 | implementation project(':client')
41 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
42 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion"
43 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutineVersion"
44 | implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion"
45 | implementation "androidx.lifecycle:lifecycle-service:$lifecycleVersion"
46 | }
47 |
--------------------------------------------------------------------------------
/service/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
23 |
24 |
25 |
26 |
27 |
28 |
31 |
32 |
33 |
34 |
35 |
36 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/service/src/main/kotlin/org/microg/nlp/service/AbstractBackendHelper.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2014, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.service
7 |
8 | import android.annotation.SuppressLint
9 | import android.content.ComponentName
10 | import android.content.Context
11 | import android.content.Intent
12 | import android.content.ServiceConnection
13 | import android.content.pm.PackageInfo
14 | import android.content.pm.PackageManager
15 | import android.os.IBinder
16 | import android.util.Log
17 | import androidx.lifecycle.Lifecycle
18 | import androidx.lifecycle.LifecycleOwner
19 | import kotlinx.coroutines.CoroutineScope
20 | import java.io.PrintWriter
21 |
22 | import java.security.MessageDigest
23 | import java.security.NoSuchAlgorithmException
24 |
25 | fun Array?.isNotNullOrEmpty(): Boolean {
26 | return this != null && this.isNotEmpty()
27 | }
28 |
29 | abstract class AbstractBackendHelper(private val TAG: String, private val context: Context, private val lifecycle: Lifecycle, val serviceIntent: Intent, val signatureDigest: String?) : ServiceConnection, LifecycleOwner {
30 | private var bound: Boolean = false
31 |
32 | protected abstract suspend fun close()
33 |
34 | override fun getLifecycle(): Lifecycle = lifecycle
35 |
36 | protected abstract fun hasBackend(): Boolean
37 |
38 | override fun onServiceConnected(name: ComponentName, service: IBinder) {
39 | bound = true
40 | Log.d(TAG, "Bound to: $name")
41 | }
42 |
43 | override fun onServiceDisconnected(name: ComponentName) {
44 | bound = false
45 | Log.d(TAG, "Unbound from: $name")
46 | }
47 |
48 | suspend fun unbind() {
49 | if (bound) {
50 | if (hasBackend()) {
51 | try {
52 | close()
53 | } catch (e: Exception) {
54 | Log.w(TAG, e)
55 | }
56 | }
57 | unbindNow()
58 | }
59 | }
60 |
61 | fun unbindNow() {
62 | if (bound) {
63 | try {
64 | Log.d(TAG, "Unbinding from: $serviceIntent")
65 | context.unbindService(this)
66 | } catch (e: Exception) {
67 | Log.w(TAG, e)
68 | }
69 |
70 | bound = false
71 | }
72 | }
73 |
74 | fun bind() {
75 | if (!bound) {
76 | Log.d(TAG, "Binding to: $serviceIntent sig: $signatureDigest")
77 | if (serviceIntent.getPackage() == null) {
78 | Log.w(TAG, "Intent is not properly resolved, can't verify signature. Aborting.")
79 | return
80 | }
81 | val computedDigest = firstSignatureDigest(context, serviceIntent.getPackage(), "SHA-256")
82 | if (signatureDigest != null && signatureDigest != computedDigest) {
83 | Log.w(TAG, "Target signature does not match selected package ($signatureDigest != $computedDigest). Aborting.")
84 | return
85 | }
86 | try {
87 | context.bindService(serviceIntent, this, Context.BIND_AUTO_CREATE)
88 | } catch (e: Exception) {
89 | Log.w(TAG, e)
90 | }
91 |
92 | }
93 | }
94 |
95 | open fun dump(writer: PrintWriter?) {
96 | writer?.println(" ${javaClass.simpleName} $serviceIntent bound=$bound")
97 | }
98 |
99 | companion object {
100 | @Suppress("DEPRECATION")
101 | @SuppressLint("PackageManagerGetSignatures")
102 | fun firstSignatureDigest(context: Context, packageName: String?, algorithm: String): String? {
103 | val packageManager = context.packageManager
104 | val info: PackageInfo?
105 | try {
106 | info = packageManager.getPackageInfo(packageName!!, PackageManager.GET_SIGNATURES)
107 | } catch (e: PackageManager.NameNotFoundException) {
108 | return null
109 | }
110 |
111 | if (info?.signatures.isNotNullOrEmpty()) {
112 | for (sig in info.signatures) {
113 | digest(sig.toByteArray(), algorithm)?.let { return it }
114 | }
115 | }
116 | return null
117 | }
118 |
119 | private fun digest(bytes: ByteArray, algorithm: String): String? {
120 | try {
121 | val md = MessageDigest.getInstance(algorithm)
122 | val digest = md.digest(bytes)
123 | val sb = StringBuilder(2 * digest.size)
124 | for (b in digest) {
125 | sb.append(String.format("%02x", b))
126 | }
127 | return sb.toString()
128 | } catch (e: NoSuchAlgorithmException) {
129 | return null
130 | }
131 | }
132 | }
133 |
134 | }
135 |
--------------------------------------------------------------------------------
/service/src/main/kotlin/org/microg/nlp/service/AsyncLocationBackend.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2020, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.service
7 |
8 | import android.content.Intent
9 | import android.location.Location
10 | import android.os.*
11 | import kotlinx.coroutines.sync.Mutex
12 | import kotlinx.coroutines.sync.withLock
13 | import org.microg.nlp.api.LocationBackend
14 | import org.microg.nlp.api.LocationCallback
15 | import java.lang.Exception
16 | import java.util.concurrent.CountDownLatch
17 | import kotlin.coroutines.resume
18 | import kotlin.coroutines.suspendCoroutine
19 |
20 | class AsyncLocationBackend(binder: IBinder, name: String = "location-backend-thread") : Thread(name) {
21 | private lateinit var looper: Looper
22 | private lateinit var handler: Handler
23 | private val mutex = Mutex(true)
24 | private val backend = LocationBackend.Stub.asInterface(binder)
25 |
26 | override fun run() {
27 | Looper.prepare()
28 | looper = Looper.myLooper()!!
29 | handler = Handler(looper)
30 | handler.post {
31 | mutex.unlock()
32 | }
33 | Looper.loop()
34 | }
35 |
36 | suspend fun updateWithOptions(options: Bundle?): Location = mutex.withLock {
37 | suspendCoroutine {
38 | handler.post {
39 | val result = try {
40 | Result.success(backend.updateWithOptions(options))
41 | } catch (e: Exception) {
42 | Result.failure(e)
43 | }
44 | it.resumeWith(result)
45 | }
46 | }
47 | }
48 |
49 | suspend fun getSettingsIntent(): Intent = mutex.withLock {
50 | suspendCoroutine {
51 | handler.post {
52 | val result = try {
53 | Result.success(backend.settingsIntent)
54 | } catch (e: Exception) {
55 | Result.failure(e)
56 | }
57 | it.resumeWith(result)
58 | }
59 | }
60 | }
61 |
62 | suspend fun getInitIntent(): Intent = mutex.withLock {
63 | suspendCoroutine {
64 | handler.post {
65 | val result = try {
66 | Result.success(backend.initIntent)
67 | } catch (e: Exception) {
68 | Result.failure(e)
69 | }
70 | it.resumeWith(result)
71 | }
72 | }
73 | }
74 |
75 | suspend fun open(callback: LocationCallback) {
76 | start()
77 | mutex.withLock {
78 | suspendCoroutine {
79 | handler.post {
80 | val result = try {
81 | backend.open(callback)
82 | Result.success(Unit)
83 | } catch (e: Exception) {
84 | Result.failure(e)
85 | }
86 | it.resumeWith(result)
87 | }
88 | }
89 | }
90 | }
91 |
92 | suspend fun getAboutIntent(): Intent = mutex.withLock {
93 | suspendCoroutine {
94 | handler.post {
95 | val result = try {
96 | Result.success(backend.aboutIntent)
97 | } catch (e: Exception) {
98 | Result.failure(e)
99 | }
100 | it.resumeWith(result)
101 | }
102 | }
103 | }
104 |
105 | suspend fun update(): Location = mutex.withLock {
106 | suspendCoroutine {
107 | handler.post {
108 | val result = try {
109 | Result.success(backend.update())
110 | } catch (e: Exception) {
111 | Result.failure(e)
112 | }
113 | it.resumeWith(result)
114 | }
115 | }
116 | }
117 |
118 | suspend fun close() {
119 | mutex.withLock {
120 | suspendCoroutine {
121 | handler.post {
122 | val result = try {
123 | backend.close()
124 | Result.success(Unit)
125 | } catch (e: Exception) {
126 | Result.failure(e)
127 | }
128 | it.resumeWith(result)
129 | }
130 | }
131 | }
132 | looper.quit()
133 | }
134 | }
--------------------------------------------------------------------------------
/service/src/main/kotlin/org/microg/nlp/service/PackageChangedReceiver.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2019, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.service
7 |
8 | import android.content.BroadcastReceiver
9 | import android.content.Context
10 | import android.content.Intent
11 | import android.content.Intent.*
12 | import android.util.Log
13 |
14 | @Deprecated("Registered in LocationService or GeocodeService")
15 | class PackageChangedReceiver : BroadcastReceiver() {
16 |
17 | private fun isProtectedAction(action: String) = when (action) {
18 | ACTION_PACKAGE_CHANGED, ACTION_PACKAGE_REMOVED, ACTION_PACKAGE_REPLACED, ACTION_PACKAGE_RESTARTED -> true
19 | else -> false
20 | }
21 |
22 | override fun onReceive(context: Context, intent: Intent) {
23 | Log.d(TAG, "Intent received: $intent")
24 | if (intent.action?.let { isProtectedAction(it) } != true) return
25 |
26 | val packageName = intent.data!!.schemeSpecificPart
27 | val preferences = Preferences(context)
28 | for (backend in preferences.locationBackends) {
29 | if (backend.startsWith("$packageName/")) {
30 | Log.d(TAG, "Reloading location service for $packageName")
31 | UnifiedLocationServiceEntryPoint.reloadPreferences()
32 |
33 | return
34 | }
35 | }
36 | for (backend in preferences.geocoderBackends) {
37 | if (backend.startsWith("$packageName/")) {
38 | Log.d(TAG, "Reloading geocoding service for $packageName")
39 | UnifiedLocationServiceEntryPoint.reloadPreferences()
40 | return
41 | }
42 | }
43 | }
44 |
45 | companion object {
46 | private const val TAG = "UnifiedService"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/service/src/main/kotlin/org/microg/nlp/service/Preferences.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2014, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.service
7 |
8 | import android.content.Context
9 | import android.content.SharedPreferences
10 | import android.os.Build
11 | import java.io.File
12 |
13 |
14 | class Preferences(private val context: Context) {
15 |
16 | private val preferences: SharedPreferences
17 | get() = context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE)
18 |
19 | private val oldPreferences: SharedPreferences
20 | get() = context.getSharedPreferences(context.packageName + "_preferences", Context.MODE_PRIVATE)
21 |
22 | private val systemDefaultPreferences: SharedPreferences?
23 | get() = try {
24 | Context::class.java.getDeclaredMethod("getSharedPreferences", File::class.java, Int::class.javaPrimitiveType).invoke(context, File("/system/etc/microg.xml"), Context.MODE_PRIVATE) as SharedPreferences
25 | } catch (e: java.lang.Exception) {
26 | null
27 | }
28 |
29 | private fun SharedPreferences.getStringSetCompat(key: String, defValues: Set? = null): Set? {
30 | if (Build.VERSION.SDK_INT >= 11) {
31 | try {
32 | val res = getStringSet(key, null)
33 | if (res != null) return res.filter { it.isNotEmpty() }.toSet()
34 | } catch (ignored: Exception) {
35 | // Ignore
36 | }
37 | }
38 | try {
39 | val str = getString(key, null)
40 | if (str != null) return str.split("\\|".toRegex()).filter { it.isNotEmpty() }.toSet()
41 | } catch (ignored: Exception) {
42 | // Ignore
43 | }
44 | return defValues
45 | }
46 |
47 | private fun SharedPreferences.Editor.putStringSetCompat(key: String, values: Set): SharedPreferences.Editor {
48 | return if (Build.VERSION.SDK_INT >= 11) {
49 | putStringSet(key, values.filter { it.isNotEmpty() }.toSet())
50 | } else {
51 | putString(key, values.filter { it.isNotEmpty() }.joinToString("|"))
52 | }
53 | }
54 |
55 | private fun getStringSetFromAny(key: String): Set? {
56 | migratePreference(key)
57 | val fromNewSettings = preferences.getStringSetCompat(key)
58 | if (fromNewSettings != null) return fromNewSettings
59 | return systemDefaultPreferences?.getStringSetCompat(key)
60 | }
61 |
62 | private fun migratePreference(key: String): Set? {
63 | val fromOldSettings = oldPreferences.getStringSetCompat(key)
64 | if (fromOldSettings != null) {
65 | var newSettings: MutableSet = mutableSetOf()
66 | newSettings.addAll(preferences.getStringSetCompat(key).orEmpty())
67 | for (oldBackend in fromOldSettings) {
68 | // Get package name and sha1
69 | val parts = oldBackend.split("/".toRegex()).dropLastWhile(String::isEmpty).toTypedArray()
70 | if (parts.size < 3) continue // skip unsigned
71 | val pkgName = parts[0]
72 | val component = parts[1]
73 | val oldSig = parts[2]
74 | if (oldSig?.length != 40) continue // skip if not sha1
75 | // Get matching sha256
76 | val sha1 = AbstractBackendHelper.firstSignatureDigest(context, pkgName, "SHA-1")
77 | val sha256 = AbstractBackendHelper.firstSignatureDigest(context, pkgName, "SHA-256")
78 | // If the system sha1 matches what we had stored
79 | if (oldSig == sha1) {
80 | // Replace it with the sha256
81 | val newBackend = "${pkgName}/${component}/${sha256}"
82 | newSettings.add(newBackend)
83 | }
84 | }
85 | if (preferences.edit().putStringSetCompat(key, newSettings.toSet()).commit()) {
86 | // Only delete the old preference once committed.
87 | oldPreferences.edit().remove(key).apply()
88 | }
89 | }
90 | return null
91 | }
92 |
93 | var locationBackends: Set
94 | get() = getStringSetFromAny(PREF_LOCATION_BACKENDS) ?: emptySet()
95 | set(backends) {
96 | preferences.edit().putStringSetCompat(PREF_LOCATION_BACKENDS, backends).apply()
97 | }
98 |
99 | var geocoderBackends: Set
100 | get() = getStringSetFromAny(PREF_GEOCODER_BACKENDS) ?: emptySet()
101 | set(backends) {
102 | preferences.edit().putStringSetCompat(PREF_GEOCODER_BACKENDS, backends).apply()
103 | }
104 |
105 | companion object {
106 | private const val PREFERENCES_NAME = "unified_nlp"
107 | private const val PREF_LOCATION_BACKENDS = "location_backends"
108 | private const val PREF_GEOCODER_BACKENDS = "geocoder_backends"
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/service/src/main/kotlin/org/microg/nlp/service/UnifiedLocationServiceEntryPoint.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2019, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.service
7 |
8 | import android.content.Intent
9 | import android.os.IBinder
10 | import android.util.Log
11 | import androidx.lifecycle.LifecycleService
12 | import androidx.lifecycle.lifecycleScope
13 | import java.io.FileDescriptor
14 | import java.io.PrintWriter
15 |
16 | @Deprecated("Use LocationService or GeocodeService")
17 | class UnifiedLocationServiceEntryPoint : LifecycleService() {
18 | private var root: UnifiedLocationServiceRoot = UnifiedLocationServiceRoot(this, lifecycle)
19 |
20 | override fun onCreate() {
21 | singleton = this
22 | super.onCreate()
23 | Log.d(TAG, "onCreate")
24 | lifecycleScope.launchWhenStarted { root.reset() }
25 | }
26 |
27 | override fun onBind(intent: Intent): IBinder? {
28 | super.onBind(intent)
29 | Log.d(TAG, "onBind: $intent")
30 | return root.asBinder()
31 | }
32 |
33 | override fun onDestroy() {
34 | super.onDestroy()
35 | Log.d(TAG, "onDestroy")
36 | root.destroy()
37 | singleton = null
38 | }
39 |
40 | override fun dump(fd: FileDescriptor?, writer: PrintWriter?, args: Array?) {
41 | writer?.println("Singleton: ${singleton != null}")
42 | root.dump(writer)
43 | }
44 |
45 | companion object {
46 | private val TAG = "ULocService"
47 | private var singleton: UnifiedLocationServiceEntryPoint? = null
48 |
49 | fun reloadPreferences() {
50 | singleton?.root?.reloadPreferences()
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/service/src/main/kotlin/org/microg/nlp/service/UnifiedLocationServiceInstance.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2019, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.service
7 |
8 | import android.app.ActivityManager
9 | import android.content.Context
10 | import android.content.pm.PackageManager.PERMISSION_GRANTED
11 | import android.location.Location
12 | import android.os.Binder.getCallingPid
13 | import android.os.Binder.getCallingUid
14 | import android.os.Bundle
15 | import android.os.RemoteException
16 | import android.util.Log
17 | import androidx.lifecycle.lifecycleScope
18 | import org.microg.nlp.client.UnifiedLocationClient.Companion.KEY_FORCE_NEXT_UPDATE
19 | import org.microg.nlp.client.UnifiedLocationClient.Companion.KEY_OP_PACKAGE_NAME
20 | import org.microg.nlp.client.UnifiedLocationClient.Companion.PERMISSION_SERVICE_ADMIN
21 | import java.io.PrintWriter
22 |
23 | @Deprecated("Use LocationService or GeocodeService")
24 | class UnifiedLocationServiceInstance(private val root: UnifiedLocationServiceRoot) : UnifiedLocationService.Default() {
25 | private var callback: LocationCallback? = null
26 | private var interval: Long = 0
27 | private var singleUpdatePending = false
28 | val callingPackage = root.context.getCallingPackage()
29 |
30 | private var opPackage: String? = null
31 | private val debugPackageString: String?
32 | get() {
33 | if (opPackage == callingPackage || opPackage == null) return callingPackage
34 | return "$callingPackage for $opPackage"
35 | }
36 |
37 | private fun Context.getCallingPackage(): String? {
38 | val manager = getSystemService(Context.ACTIVITY_SERVICE) as? ActivityManager
39 | val callingPid = getCallingPid()
40 | if (manager != null && callingPid > 0) {
41 | manager.runningAppProcesses.find { it.pid == callingPid }?.pkgList?.singleOrNull()?.let { return it }
42 | }
43 | return packageManager.getPackagesForUid(getCallingUid())?.singleOrNull()
44 | }
45 |
46 | fun reportLocation(location: Location) {
47 | try {
48 | if (callback != null) {
49 | callback!!.onLocationUpdate(location)
50 | }
51 | if (singleUpdatePending) {
52 | singleUpdatePending = false
53 | root.updateLocationInterval()
54 | }
55 | } catch (e: RemoteException) {
56 | root.onDisconnected(this)
57 | }
58 | }
59 |
60 | fun getInterval(): Long {
61 | // TODO: Do not report interval if client should no longer receive
62 | return if (singleUpdatePending) UnifiedLocationServiceRoot.MIN_LOCATION_INTERVAL else interval
63 | }
64 |
65 | override fun registerLocationCallback(callback: LocationCallback, options: Bundle) {
66 | if (root.context.checkCallingPermission(PERMISSION_SERVICE_ADMIN) == PERMISSION_GRANTED && options.containsKey(KEY_OP_PACKAGE_NAME)) {
67 | opPackage = options.getString(KEY_OP_PACKAGE_NAME)
68 | }
69 | Log.d(TAG, "registerLocationCallback[$callingPackage]")
70 | this.callback = callback
71 | }
72 |
73 | override fun setUpdateInterval(interval: Long, options: Bundle) {
74 | if (root.context.checkCallingPermission(PERMISSION_SERVICE_ADMIN) == PERMISSION_GRANTED && options.containsKey(KEY_OP_PACKAGE_NAME)) {
75 | opPackage = options.getString(KEY_OP_PACKAGE_NAME)
76 | }
77 | Log.d(TAG, "setUpdateInterval[$debugPackageString] interval: $interval")
78 | this.interval = interval
79 | root.updateLocationInterval()
80 | }
81 |
82 | override fun requestSingleUpdate(options: Bundle) {
83 | if (root.context.checkCallingPermission(PERMISSION_SERVICE_ADMIN) == PERMISSION_GRANTED && options.containsKey(KEY_OP_PACKAGE_NAME)) {
84 | opPackage = options.getString(KEY_OP_PACKAGE_NAME)
85 | }
86 | val lastLocation = root.lastReportedLocation
87 | if (lastLocation == null || lastLocation.time < System.currentTimeMillis() - UnifiedLocationServiceRoot.MAX_LOCATION_AGE || options.getBoolean(KEY_FORCE_NEXT_UPDATE, false)) {
88 | Log.d(TAG, "requestSingleUpdate[$debugPackageString] requesting new location")
89 | singleUpdatePending = true
90 | root.lifecycleScope.launchWhenStarted {
91 | root.locationFuser.update()
92 | root.updateLocationInterval()
93 | }
94 | } else if (callback != null) {
95 | Log.d(TAG, "requestSingleUpdate[$debugPackageString] using last location ")
96 | try {
97 | this.callback!!.onLocationUpdate(lastLocation)
98 | } catch (e: RemoteException) {
99 | root.onDisconnected(this)
100 | throw e
101 | }
102 | }
103 | }
104 |
105 | fun dump(writer: PrintWriter?) {
106 | writer?.println("$debugPackageString: interval $interval, single $singleUpdatePending")
107 | }
108 |
109 | companion object {
110 | private val TAG = "ULocService"
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2015, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | include ':api'
7 | include ':service'
8 | include ':service-api'
9 | include ':compat'
10 | include ':client'
11 | include ':location-v1'
12 | include ':location-v2'
13 | include ':location-v3'
14 | include ':geocode-v1'
15 |
16 | include ':ui'
17 |
18 | //include ':service-app'
19 | //include ':client-app'
20 | //include ':app'
21 |
22 | include ':docs:backend-sample'
23 |
--------------------------------------------------------------------------------
/ui/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2019, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 |
7 | apply plugin: 'com.android.library'
8 | apply plugin: 'kotlin-android'
9 | apply plugin: 'kotlin-kapt'
10 | apply plugin: 'kotlin-android-extensions'
11 | apply plugin: 'maven-publish'
12 | apply plugin: 'signing'
13 |
14 | android {
15 | compileSdkVersion androidCompileSdk
16 | buildToolsVersion "$androidBuildVersionTools"
17 |
18 | buildFeatures {
19 | dataBinding = true
20 | }
21 |
22 | defaultConfig {
23 | versionName version
24 | minSdkVersion Math.max(androidMinSdk, 14)
25 | targetSdkVersion androidTargetSdk
26 | }
27 |
28 | sourceSets {
29 | main.java.srcDirs += 'src/main/kotlin'
30 | }
31 |
32 | compileOptions {
33 | sourceCompatibility = 1.8
34 | targetCompatibility = 1.8
35 | }
36 |
37 | kotlinOptions {
38 | jvmTarget = "1.8"
39 | }
40 |
41 | lintOptions {
42 | warning "MissingTranslation"
43 | }
44 | }
45 |
46 | apply from: '../gradle/publish.gradle'
47 |
48 | description = 'UnifiedNlp UI library for common configuration fragments'
49 |
50 | dependencies {
51 | implementation project(':api')
52 | implementation project(':client')
53 |
54 | // Kotlin
55 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
56 |
57 | // AndroidX UI
58 | implementation "androidx.appcompat:appcompat:$appcompatVersion"
59 | implementation "androidx.fragment:fragment:$fragmentVersion"
60 | implementation "androidx.recyclerview:recyclerview:$recyclerviewVersion"
61 | implementation "androidx.preference:preference:$preferenceVersion"
62 |
63 | // Kotlin coroutine for android
64 | implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion"
65 | implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion"
66 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion"
67 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutineVersion"
68 |
69 | // Navigation
70 | implementation "androidx.navigation:navigation-fragment:$navigationVersion"
71 | implementation "androidx.navigation:navigation-ui:$navigationVersion"
72 | implementation "androidx.navigation:navigation-fragment-ktx:$navigationVersion"
73 | implementation "androidx.navigation:navigation-ui-ktx:$navigationVersion"
74 | }
75 |
--------------------------------------------------------------------------------
/ui/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/ui/src/main/kotlin/org/microg/nlp/ui/ActivityResultProcessor.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2020 microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.ui
7 |
8 | import android.content.Intent
9 | import android.os.Bundle
10 | import android.util.Log
11 | import android.util.SparseArray
12 | import androidx.core.util.set
13 | import androidx.fragment.app.Fragment
14 | import java.util.concurrent.atomic.AtomicInteger
15 | import kotlin.coroutines.resume
16 | import kotlin.coroutines.suspendCoroutine
17 |
18 | private val requestCodeCounter = AtomicInteger(1)
19 | private val continuationMap = SparseArray<(Int, Intent?) -> Unit>()
20 |
21 | fun Fragment.startActivityForResult(intent: Intent, options: Bundle? = null, callback: (Int, Intent?) -> Unit) {
22 | val requestCode = requestCodeCounter.getAndIncrement()
23 | continuationMap.put(requestCode, callback)
24 | startActivityForResult(intent, requestCode, options)
25 | }
26 |
27 | suspend fun Fragment.startActivityForResultCode(intent: Intent, options: Bundle? = null): Int = suspendCoroutine { continuation ->
28 | startActivityForResult(intent, options) { responseCode, _ ->
29 | continuation.resume(responseCode)
30 | }
31 | }
32 |
33 | fun handleActivityResult(requestCode: Int, responseCode: Int, data: Intent?) {
34 | Log.d("ActivityResultProc", "handleActivityResult: $requestCode, $responseCode")
35 | try {
36 | continuationMap[requestCode]?.let { it(responseCode, data) }
37 | } catch (e: Exception) {
38 | Log.w("ActivityResultProc", "Error while handling activity result", e)
39 | }
40 | continuationMap.remove(requestCode)
41 | }
42 |
--------------------------------------------------------------------------------
/ui/src/main/kotlin/org/microg/nlp/ui/BackendSettingsActivity.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2020, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.ui
7 |
8 | import android.os.Bundle
9 | import androidx.appcompat.app.AppCompatActivity
10 | import androidx.navigation.NavController
11 | import androidx.navigation.fragment.NavHostFragment
12 | import androidx.navigation.ui.AppBarConfiguration
13 | import androidx.navigation.ui.navigateUp
14 | import androidx.navigation.ui.setupActionBarWithNavController
15 |
16 | class BackendSettingsActivity : AppCompatActivity() {
17 | private lateinit var appBarConfiguration: AppBarConfiguration
18 |
19 | private val navController: NavController
20 | get() = (supportFragmentManager.findFragmentById(R.id.navhost) as NavHostFragment).navController
21 |
22 | override fun onCreate(savedInstanceState: Bundle?) {
23 | super.onCreate(savedInstanceState)
24 | setContentView(R.layout.backend_settings_activity)
25 |
26 | appBarConfiguration = AppBarConfiguration(navController.graph)
27 | setupActionBarWithNavController(navController, appBarConfiguration)
28 | }
29 |
30 | override fun onSupportNavigateUp(): Boolean {
31 | return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
32 | }
33 | }
--------------------------------------------------------------------------------
/ui/src/main/kotlin/org/microg/nlp/ui/binding/BindingAdapter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2020, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.ui.binding
7 |
8 | import android.content.Context
9 | import android.util.TypedValue
10 | import android.view.View
11 | import androidx.annotation.AttrRes
12 | import androidx.annotation.ColorInt
13 | import androidx.core.content.ContextCompat
14 | import androidx.databinding.BindingAdapter
15 |
16 | @ColorInt
17 | private fun Context.resolveColor(@AttrRes resid: Int): Int? {
18 | val typedValue = TypedValue()
19 | if (!theme.resolveAttribute(resid, typedValue, true)) return null
20 | val colorRes = if (typedValue.resourceId != 0) typedValue.resourceId else typedValue.data
21 | return ContextCompat.getColor(this, colorRes)
22 | }
23 |
24 | @BindingAdapter("app:backgroundColorAttr")
25 | fun View.setBackgroundColorAttribute(@AttrRes resId: Int) = context.resolveColor(resId)?.let { setBackgroundColor(it) }
26 |
--------------------------------------------------------------------------------
/ui/src/main/kotlin/org/microg/nlp/ui/model/BackendDetailsCallback.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2020, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.ui.model
7 |
8 | interface BackendDetailsCallback {
9 | fun onEnabledChange(entry: BackendInfo?, newValue: Boolean)
10 | fun onAppClicked(entry: BackendInfo?)
11 | fun onAboutClicked(entry: BackendInfo?)
12 | fun onConfigureClicked(entry: BackendInfo?)
13 | }
14 |
--------------------------------------------------------------------------------
/ui/src/main/kotlin/org/microg/nlp/ui/model/BackendInfo.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2020, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.ui.model
7 |
8 | import android.content.Intent
9 | import android.content.pm.ServiceInfo
10 | import android.graphics.drawable.Drawable
11 | import androidx.databinding.ObservableBoolean
12 | import androidx.databinding.ObservableField
13 |
14 | class BackendInfo(val serviceInfo: ServiceInfo, val type: BackendType, val firstSignatureDigest: String?) {
15 | val enabled = ObservableBoolean()
16 | val appIcon = ObservableField()
17 | val name = ObservableField()
18 | val appName = ObservableField()
19 | val summary = ObservableField()
20 |
21 | val loaded = ObservableBoolean()
22 |
23 | val initIntent = ObservableField()
24 | val aboutIntent = ObservableField()
25 | val settingsIntent = ObservableField()
26 |
27 | val unsignedComponent: String = "${serviceInfo.packageName}/${serviceInfo.name}"
28 | val signedComponent: String = "${serviceInfo.packageName}/${serviceInfo.name}/$firstSignatureDigest"
29 |
30 | override fun equals(other: Any?): Boolean {
31 | return other is BackendInfo && other.name == name && other.enabled == enabled && other.appName == appName && other.unsignedComponent == unsignedComponent && other.summary == summary
32 | }
33 | }
34 |
35 | enum class BackendType { LOCATION, GEOCODER }
36 |
--------------------------------------------------------------------------------
/ui/src/main/kotlin/org/microg/nlp/ui/model/BackendListEntryCallback.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-FileCopyrightText: 2020, microG Project Team
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | package org.microg.nlp.ui.model
7 |
8 | interface BackendListEntryCallback {
9 | fun onEnabledChange(entry: BackendInfo?, newValue: Boolean)
10 | fun onOpenDetails(entry: BackendInfo?)
11 | }
12 |
--------------------------------------------------------------------------------
/ui/src/main/res/layout/backend_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 |
16 |
17 |
18 |
22 |
23 |
33 |
34 |
46 |
47 |
48 |
56 |
57 |
61 |
62 |
72 |
73 |
85 |
86 |
87 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/ui/src/main/res/layout/backend_list_entry.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
9 |
10 |
11 |
12 |
13 |
14 |
17 |
18 |
21 |
22 |
23 |
30 |
31 |
45 |
46 |
56 |
57 |
63 |
64 |
65 |
70 |
71 |
80 |
81 |
82 |
83 |
93 |
94 |
98 |
99 |
100 |
108 |
109 |
122 |
123 |
124 |
125 |
--------------------------------------------------------------------------------
/ui/src/main/res/layout/backend_settings_activity.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
11 |
12 |
19 |
20 |
--------------------------------------------------------------------------------
/ui/src/main/res/navigation/nav_unlp.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
11 |
12 |
17 |
20 |
21 |
26 |
29 |
32 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/ui/src/main/res/values-be/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | Навігацыйныя модулі
9 | Падрабязнасці пра модуль
10 |
11 | Сецевыя модулі геалакацыі
12 | Модулі пошуку адрасоў
13 | Усталяваныя модулі адсутнічаюць
14 |
15 | Наладзіць
16 | Аб праграме
17 | Выкарыстоўваць модуль
18 | Апісанне
19 | Апошняе месцазнаходжанне
20 |
21 |
--------------------------------------------------------------------------------
/ui/src/main/res/values-de/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | Module für Funknetz-basierte Ortung
9 | Module für Adressauflösung
10 |
11 | Konfigurieren
12 | Infos
13 | Modul benutzen
14 | Beschreibung
15 | Letzte Ortung
16 |
17 |
--------------------------------------------------------------------------------
/ui/src/main/res/values-eo/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | Pozici‑trovaj subservoj
9 | Detaloj pri la subservo
10 |
11 | Ret‑bazitaj pozici‑trovaj subservoj
12 | Adres‑trovaj subservoj
13 | Neniu instalita subservo
14 |
15 | Agordi
16 | Pri
17 | Uzi subservon
18 | Priskribo
19 | Antaŭa pozicio
20 |
21 |
22 |
--------------------------------------------------------------------------------
/ui/src/main/res/values-es/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | Módulos de localización
9 | Detalles del módulo de localización
10 |
11 | Módulos de geolocalización en red
12 | Módulos de búsqueda de direcciones
13 | No hay ningún módulo instalado
14 |
15 | Configurar
16 | Acerca
17 | Usar módulo
18 | Descripción
19 | Última localización
20 |
--------------------------------------------------------------------------------
/ui/src/main/res/values-fr/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | Configurer
9 | À propos
10 |
11 |
--------------------------------------------------------------------------------
/ui/src/main/res/values-in/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | Modul lokasi
9 | Detail modul lokasi
10 |
11 | Modul Geolokasi berbasis jaringan
12 | Modul pencarian alamat
13 | Tidak ada modul yang dipasang
14 |
15 | Konfigurasi
16 | Tentang
17 | Gunakan modul
18 | Deskripsi
19 | Lokasi terakhir
20 |
21 |
--------------------------------------------------------------------------------
/ui/src/main/res/values-it/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | Moduli di geolocalizzazione
9 | Dettagli del modulo di geolocalizzazione
10 |
11 | Moduli di localizzazione basati sulle reti senza fili
12 | Moduli di geocodifica degli indirizzi
13 | Nessun modulo installato
14 |
15 | Configura
16 | Informazioni
17 | Abilita il modulo
18 | Descrizione
19 | Ultima posizione rilevata
20 |
21 |
--------------------------------------------------------------------------------
/ui/src/main/res/values-pl/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | Usługi lokaliacji
9 | Szczeguły usługi
10 |
11 | Usługi lokalizacji oparte o sieć
12 | Usługi wyszukiwania adresów
13 | Brak zainstalowanych usług
14 |
15 | Konfiguruj
16 | O programie
17 | Użyj usługi
18 | Opis
19 | Ostatnia lokalizacja
20 |
21 |
22 |
--------------------------------------------------------------------------------
/ui/src/main/res/values-ro/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | Configuraţi
9 |
10 |
--------------------------------------------------------------------------------
/ui/src/main/res/values-ru/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | Навигационные модули
9 | Подробности о модуле
10 |
11 | Сетевые модули геолокации
12 | Модули поиска адресов
13 | Установленные модули отсутствуют
14 |
15 | Настроить
16 | О программе
17 | Использовать модуль
18 | Описание
19 | Последнее местоположение
20 |
21 |
--------------------------------------------------------------------------------
/ui/src/main/res/values-sr/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | Подеси
9 | О програму
10 |
11 |
--------------------------------------------------------------------------------
/ui/src/main/res/values-uk/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | Сконфігурувати
9 | Про
10 |
11 |
--------------------------------------------------------------------------------
/ui/src/main/res/values-zh-rTW/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | 設定
9 | 關於
10 |
11 |
--------------------------------------------------------------------------------
/ui/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | Location modules
9 | Location module details
10 |
11 | Network-based Geolocation modules
12 | Address lookup modules
13 | No module installed
14 |
15 | Configure
16 | About
17 | Use module
18 | Description
19 | Last location
20 |
21 |
--------------------------------------------------------------------------------