diff --git a/play-services-location/build.gradle b/play-services-location/build.gradle index e05b44a95a..9533fe5157 100644 --- a/play-services-location/build.gradle +++ b/play-services-location/build.gradle @@ -38,4 +38,6 @@ dependencies { api project(':play-services-base') api project(':play-services-basement') api project(':play-services-tasks') + + annotationProcessor project(':safe-parcel-processor') } diff --git a/play-services-location/core/build.gradle b/play-services-location/core/build.gradle index e449c1da6c..a48c2259b1 100644 --- a/play-services-location/core/build.gradle +++ b/play-services-location/core/build.gradle @@ -11,6 +11,7 @@ dependencies { implementation project(':play-services-base-core') implementation project(':play-services-location-core-base') + implementation "androidx.core:core-ktx:$coreVersion" implementation "androidx.lifecycle:lifecycle-service:$lifecycleVersion" implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion" implementation "androidx.navigation:navigation-fragment-ktx:$navigationVersion" diff --git a/play-services-location/core/src/main/AndroidManifest.xml b/play-services-location/core/src/main/AndroidManifest.xml index 95464876e0..ae3209048c 100644 --- a/play-services-location/core/src/main/AndroidManifest.xml +++ b/play-services-location/core/src/main/AndroidManifest.xml @@ -21,6 +21,19 @@ tools:ignore="ProtectedPermissions" /> + + + + + + + + = 31) FLAG_MUTABLE else 0) or FLAG_UPDATE_CURRENT) + coarsePendingIntent = PendingIntentCompat.getService(context, 0, intent, FLAG_UPDATE_CURRENT, true) lastLocationCapsule.start() requestManager.start() } diff --git a/play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/LocationManagerInstance.kt b/play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/LocationManagerInstance.kt index 689d780af8..0e1e7cc4dc 100644 --- a/play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/LocationManagerInstance.kt +++ b/play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/LocationManagerInstance.kt @@ -7,16 +7,22 @@ package org.microg.gms.location.manager import android.Manifest.permission.* import android.app.PendingIntent +import android.bluetooth.BluetoothManager import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager import android.content.pm.PackageManager.PERMISSION_GRANTED import android.location.Location import android.location.LocationManager.GPS_PROVIDER import android.location.LocationManager.NETWORK_PROVIDER import android.os.Binder +import android.os.Build.VERSION.SDK_INT import android.os.IBinder import android.os.Parcel import android.os.SystemClock +import android.provider.Settings import android.util.Log +import androidx.core.app.PendingIntentCompat import androidx.core.content.getSystemService import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner @@ -25,13 +31,14 @@ import com.google.android.gms.common.api.CommonStatusCodes import com.google.android.gms.common.api.Status import com.google.android.gms.common.api.internal.IStatusCallback import com.google.android.gms.common.internal.ICancelToken +import com.google.android.gms.common.internal.safeparcel.SafeParcelableSerializer import com.google.android.gms.location.* import com.google.android.gms.location.internal.* import com.google.android.gms.location.internal.DeviceOrientationRequestUpdateData.REMOVE_UPDATES import com.google.android.gms.location.internal.DeviceOrientationRequestUpdateData.REQUEST_UPDATES import kotlinx.coroutines.* -import org.microg.gms.common.NonCancelToken import org.microg.gms.location.hasNetworkLocationServiceBuiltIn +import org.microg.gms.location.settings.* import org.microg.gms.utils.warnOnTransactionIssues class LocationManagerInstance( @@ -209,17 +216,36 @@ class LocationManagerInstance( } override fun requestLocationSettingsDialog(settingsRequest: LocationSettingsRequest?, callback: ISettingsCallbacks?, packageName: String?) { - Log.d(TAG, "requestLocationSettingsDialog by ${getClientIdentity().packageName}") + Log.d(TAG, "requestLocationSettingsDialog by ${getClientIdentity().packageName} $settingsRequest") val clientIdentity = getClientIdentity() lifecycleScope.launchWhenStarted { - val locationManager = context.getSystemService() - val gpsPresent = locationManager?.allProviders?.contains(GPS_PROVIDER) == true - val networkPresent = locationManager?.allProviders?.contains(NETWORK_PROVIDER) == true || context.hasNetworkLocationServiceBuiltIn() - val gpsUsable = gpsPresent && locationManager?.isProviderEnabled(GPS_PROVIDER) == true && - context.packageManager.checkPermission(ACCESS_FINE_LOCATION, clientIdentity.packageName) == PERMISSION_GRANTED - val networkUsable = networkPresent && locationManager?.isProviderEnabled(NETWORK_PROVIDER) == true && - context.packageManager.checkPermission(ACCESS_COARSE_LOCATION, clientIdentity.packageName) == PERMISSION_GRANTED - runCatching { callback?.onLocationSettingsResult(LocationSettingsResult(LocationSettingsStates(gpsUsable, networkUsable, false, gpsPresent, networkPresent, true), Status.SUCCESS)) } + val states = context.getDetailedLocationSettingsStates() + val requests = settingsRequest?.requests?.map { + it.priority to (if (it.granularity == Granularity.GRANULARITY_PERMISSION_LEVEL) context.granularityFromPermission(clientIdentity) else it.granularity) + }.orEmpty() + val gpsRequested = requests.any { it.first == Priority.PRIORITY_HIGH_ACCURACY && it.second == Granularity.GRANULARITY_FINE } + val networkLocationRequested = requests.any { it.first <= Priority.PRIORITY_LOW_POWER && it.second >= Granularity.GRANULARITY_COARSE } + val bleRequested = settingsRequest?.needBle == true + val statusCode = when { + gpsRequested && states.gpsPresent && !states.gpsUsable -> CommonStatusCodes.RESOLUTION_REQUIRED + networkLocationRequested && states.networkLocationPresent && !states.networkLocationUsable -> CommonStatusCodes.RESOLUTION_REQUIRED + bleRequested && states.blePresent && !states.bleUsable -> CommonStatusCodes.RESOLUTION_REQUIRED + gpsRequested && !states.gpsPresent -> LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE + networkLocationRequested && !states.networkLocationPresent -> LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE + bleRequested && !states.blePresent -> LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE + else -> CommonStatusCodes.SUCCESS + } + + val resolution = if (statusCode == CommonStatusCodes.RESOLUTION_REQUIRED) { + val intent = Intent(ACTION_LOCATION_SETTINGS_CHECKER) + intent.setPackage(context.packageName) + intent.putExtra(EXTRA_ORIGINAL_PACKAGE_NAME, clientIdentity.packageName) + intent.putExtra(EXTRA_SETTINGS_REQUEST, SafeParcelableSerializer.serializeToBytes(settingsRequest)) + PendingIntentCompat.getActivity(context, clientIdentity.packageName.hashCode(), intent, PendingIntent.FLAG_UPDATE_CURRENT, true) + } else null + val status = Status(statusCode, LocationSettingsStatusCodes.getStatusCodeString(statusCode), resolution) + Log.d(TAG, "requestLocationSettingsDialog by ${getClientIdentity().packageName} returns $status") + runCatching { callback?.onLocationSettingsResult(LocationSettingsResult(status, states.toApi())) } } } diff --git a/play-services-location/core/src/main/kotlin/org/microg/gms/location/settings/DetailedLocationSettingsStates.kt b/play-services-location/core/src/main/kotlin/org/microg/gms/location/settings/DetailedLocationSettingsStates.kt new file mode 100644 index 0000000000..13f6ef45a6 --- /dev/null +++ b/play-services-location/core/src/main/kotlin/org/microg/gms/location/settings/DetailedLocationSettingsStates.kt @@ -0,0 +1,72 @@ +/* + * SPDX-FileCopyrightText: 2024 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.location.settings + +import android.Manifest.permission.* +import android.bluetooth.BluetoothManager +import android.content.Context +import android.content.pm.PackageManager.* +import android.location.LocationManager +import android.location.LocationManager.GPS_PROVIDER +import android.location.LocationManager.NETWORK_PROVIDER +import android.os.Build.VERSION.SDK_INT +import android.provider.Settings +import androidx.core.content.getSystemService +import com.google.android.gms.location.LocationSettingsStates +import org.microg.gms.location.hasNetworkLocationServiceBuiltIn + +data class DetailedLocationSettingsStates( + val gpsSystemFeature: Boolean, + val networkLocationSystemFeature: Boolean, + val bluetoothLeSystemFeature: Boolean, + val gpsProviderEnabled: Boolean, + val networkLocationProviderEnabled: Boolean, + val networkLocationProviderBuiltIn: Boolean, + val fineLocationPermission: Boolean, + val coarseLocationPermission: Boolean, + val backgroundLocationPermission: Boolean, + val blePresent: Boolean, + val bleEnabled: Boolean, + val bleScanAlways: Boolean, + val airplaneMode: Boolean, +) { + val gpsPresent: Boolean + get() = gpsSystemFeature + val networkLocationPresent: Boolean + get() = networkLocationSystemFeature || networkLocationProviderBuiltIn + val gpsUsable: Boolean + get() = gpsProviderEnabled && fineLocationPermission && backgroundLocationPermission + val networkLocationUsable: Boolean + get() = (networkLocationProviderEnabled || networkLocationProviderBuiltIn) && coarseLocationPermission && backgroundLocationPermission + val bleUsable: Boolean + get() = blePresent && (bleEnabled || (bleScanAlways && !airplaneMode)) + + fun toApi() = LocationSettingsStates(gpsUsable, networkLocationUsable, bleUsable, gpsPresent, networkLocationPresent, blePresent) +} + +fun Context.getDetailedLocationSettingsStates(): DetailedLocationSettingsStates { + val bluetoothLeSystemFeature = packageManager.hasSystemFeature(FEATURE_BLUETOOTH_LE) + val locationManager = getSystemService() + val bluetoothManager = if (bluetoothLeSystemFeature) getSystemService() else null + val bleAdapter = bluetoothManager?.adapter + + return DetailedLocationSettingsStates( + gpsSystemFeature = packageManager.hasSystemFeature(FEATURE_LOCATION_GPS), + networkLocationSystemFeature = packageManager.hasSystemFeature(FEATURE_LOCATION_NETWORK), + bluetoothLeSystemFeature = bluetoothLeSystemFeature, + gpsProviderEnabled = locationManager?.isProviderEnabled(GPS_PROVIDER) == true, + networkLocationProviderEnabled = locationManager?.isProviderEnabled(NETWORK_PROVIDER) == true, + networkLocationProviderBuiltIn = hasNetworkLocationServiceBuiltIn(), + fineLocationPermission = packageManager.checkPermission(ACCESS_FINE_LOCATION, packageName) == PERMISSION_GRANTED, + coarseLocationPermission = packageManager.checkPermission(ACCESS_COARSE_LOCATION, packageName) == PERMISSION_GRANTED, + backgroundLocationPermission = if (SDK_INT < 29) true else + packageManager.checkPermission(ACCESS_BACKGROUND_LOCATION, packageName) == PERMISSION_GRANTED, + blePresent = bleAdapter != null, + bleEnabled = bleAdapter?.isEnabled == true, + bleScanAlways = Settings.Global.getInt(contentResolver, "ble_scan_always_enabled", 0) == 1, + airplaneMode = Settings.Global.getInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0) != 0 + ) +} \ No newline at end of file diff --git a/play-services-location/core/src/main/kotlin/org/microg/gms/location/settings/LocationSettingsCheckerActivity.kt b/play-services-location/core/src/main/kotlin/org/microg/gms/location/settings/LocationSettingsCheckerActivity.kt new file mode 100644 index 0000000000..d30fc22ebf --- /dev/null +++ b/play-services-location/core/src/main/kotlin/org/microg/gms/location/settings/LocationSettingsCheckerActivity.kt @@ -0,0 +1,62 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.location.settings + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.location.LocationManager +import android.os.Bundle +import android.provider.Settings +import android.util.Log +import android.widget.TextView +import org.microg.gms.location.core.R + +const val ACTION_LOCATION_SETTINGS_CHECKER = "com.google.android.gms.location.settings.CHECK_SETTINGS" + +private const val REQUEST_CODE_LOCATION = 120 +const val EXTRA_ORIGINAL_PACKAGE_NAME = "originalPackageName" +const val EXTRA_SETTINGS_REQUEST = "locationSettingsRequests" + +class LocationSettingsCheckerActivity : Activity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_location_setting_checker) + + findViewById(R.id.location_setting_checker_sure).setOnClickListener { + // TODO: We also should handle permissions, Airplane Mode and BLE here. + val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS) + startActivityForResult(intent, REQUEST_CODE_LOCATION) + } + findViewById(R.id.location_setting_checker_cancel).setOnClickListener { + checkerBack(RESULT_CANCELED) + } + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + if (requestCode == REQUEST_CODE_LOCATION && isLocationEnabled(this)) { + checkerBack(RESULT_OK) + } else { + super.onActivityResult(requestCode, resultCode, data) + } + } + + override fun onBackPressed() { + checkerBack(RESULT_CANCELED) + } + + private fun checkerBack(resultCode: Int) { + setResult(resultCode) + finish() + } + + private fun isLocationEnabled(context: Context): Boolean { + val locationManager = context.getSystemService(LOCATION_SERVICE) as LocationManager + return locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) || locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) + } + +} \ No newline at end of file diff --git a/play-services-location/core/src/main/res/drawable/custom_dialog_background.xml b/play-services-location/core/src/main/res/drawable/custom_dialog_background.xml new file mode 100644 index 0000000000..c4f7f602ab --- /dev/null +++ b/play-services-location/core/src/main/res/drawable/custom_dialog_background.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/play-services-location/core/src/main/res/layout/activity_location_setting_checker.xml b/play-services-location/core/src/main/res/layout/activity_location_setting_checker.xml new file mode 100644 index 0000000000..0c0af3c6ad --- /dev/null +++ b/play-services-location/core/src/main/res/layout/activity_location_setting_checker.xml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/play-services-location/core/src/main/res/values-zh-rCN/strings.xml b/play-services-location/core/src/main/res/values-zh-rCN/strings.xml index 3d81736e20..8acdb05dec 100644 --- a/play-services-location/core/src/main/res/values-zh-rCN/strings.xml +++ b/play-services-location/core/src/main/res/values-zh-rCN/strings.xml @@ -21,4 +21,13 @@ 基于 Wi-Fi 的位置信息 地址解析器 从 Mozilla 位置服务获取移动网络信号塔位置 + + 要继续,请开启设备位置信息功能\n(使用位置信息服务) + 您的设备将需要: + 使用GPS、WLAN、移动网络和传感器 + 使用位置信息服务;在提供该服务的过程中,可能会定期收集位置数据,并以匿名方式使用这类数据,从而提高位置信息的精准度并改善基于地理位置的服务。 + 有关详细信息,请访问 位置信息设置。 + 不用了 + 确定 + \ No newline at end of file diff --git a/play-services-location/core/src/main/res/values/strings.xml b/play-services-location/core/src/main/res/values/strings.xml index c228158735..6bb75c0f0d 100644 --- a/play-services-location/core/src/main/res/values/strings.xml +++ b/play-services-location/core/src/main/res/values/strings.xml @@ -27,4 +27,13 @@ Force coarse location Always return coarse locations to this app, ignoring its permission level. + + To continue, turn on device location, which uses location service + Your device will need to: + Use GPS, Wi‑Fi, cell networks, and sensors + Use location service; as part of this service, may collect location data periodically and use this data in an anonymous way to improve location accuracy and location-based services. + For details, go to the location settings. + No,thanks + OK + \ No newline at end of file diff --git a/play-services-location/core/src/main/res/values/styles.xml b/play-services-location/core/src/main/res/values/styles.xml new file mode 100644 index 0000000000..98bbf26a73 --- /dev/null +++ b/play-services-location/core/src/main/res/values/styles.xml @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/play-services-location/src/main/aidl/com/google/android/gms/location/internal/IGoogleLocationManagerService.aidl b/play-services-location/src/main/aidl/com/google/android/gms/location/internal/IGoogleLocationManagerService.aidl index 132bad1c1e..d848199f71 100644 --- a/play-services-location/src/main/aidl/com/google/android/gms/location/internal/IGoogleLocationManagerService.aidl +++ b/play-services-location/src/main/aidl/com/google/android/gms/location/internal/IGoogleLocationManagerService.aidl @@ -133,4 +133,6 @@ interface IGoogleLocationManagerService { // int getActivityRecognitionMode() = 77; // void injectLocatinWithCallback(in Location mockLocation, int injectionType, IStatusCallback callback) = 85; + +// void isGoogleLocationAccuracyEnabled(in IBooleanStatusCallback callback) = 94; } diff --git a/play-services-location/src/main/java/com/google/android/gms/location/LocationSettingsRequest.java b/play-services-location/src/main/java/com/google/android/gms/location/LocationSettingsRequest.java index f77a392ef1..786d6cb4eb 100644 --- a/play-services-location/src/main/java/com/google/android/gms/location/LocationSettingsRequest.java +++ b/play-services-location/src/main/java/com/google/android/gms/location/LocationSettingsRequest.java @@ -1,11 +1,22 @@ /* - * SPDX-FileCopyrightText: 2015, microG Project Team + * SPDX-FileCopyrightText: 2015 microG Project Team * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. */ package com.google.android.gms.location; +import android.os.Parcel; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; +import org.microg.gms.common.Hide; import org.microg.gms.common.PublicApi; +import org.microg.gms.utils.ToStringHelper; import org.microg.safeparcel.AutoSafeParcelable; import org.microg.safeparcel.SafeParceled; @@ -13,31 +24,35 @@ import java.util.Collection; import java.util.List; +/** + * Specifies the types of location services the client is interested in using. Settings will be checked for optimal functionality + * of all requested services. Use {@link LocationSettingsRequest.Builder} to construct this object. + */ @PublicApi -public class LocationSettingsRequest extends AutoSafeParcelable { +@SafeParcelable.Class +public class LocationSettingsRequest extends AbstractSafeParcelable { @Field(1000) private int versionCode = 2; @Field(value = 1, subClass = LocationRequest.class) - @PublicApi(exclude = true) - public List requests; + @Hide + public final List requests; @Field(2) - @PublicApi(exclude = true) - public boolean alwaysShow; + @Hide + public final boolean alwaysShow; @Field(3) - @PublicApi(exclude = true) - public boolean needBle; + @Hide + public final boolean needBle; @Field(5) - @PublicApi(exclude = true) - public LocationSettingsConfiguration configuration; - - private LocationSettingsRequest() { - } + @Hide + @Nullable + public final LocationSettingsConfiguration configuration; - private LocationSettingsRequest(List requests, boolean alwaysShow, boolean needBle, LocationSettingsConfiguration configuration) { + @Constructor + LocationSettingsRequest(@Param(1) List requests, @Param(2) boolean alwaysShow, @Param(3) boolean needBle, @Param(5) @Nullable LocationSettingsConfiguration configuration) { this.requests = requests; this.alwaysShow = alwaysShow; this.needBle = needBle; @@ -100,5 +115,21 @@ public Builder setNeedBle(boolean needBle) { } } - public static final Creator CREATOR = new AutoCreator(LocationSettingsRequest.class); + @Hide + @NonNull + @Override + public String toString() { + return ToStringHelper.name("LocationSettingsRequest") + .value(requests) + .field("alwaysShow", alwaysShow) + .field("needBle", needBle) + .end(); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(LocationSettingsRequest.class); + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } } diff --git a/play-services-location/src/main/java/com/google/android/gms/location/LocationSettingsResponse.java b/play-services-location/src/main/java/com/google/android/gms/location/LocationSettingsResponse.java index 60da9d3e23..dbe22a089a 100644 --- a/play-services-location/src/main/java/com/google/android/gms/location/LocationSettingsResponse.java +++ b/play-services-location/src/main/java/com/google/android/gms/location/LocationSettingsResponse.java @@ -14,6 +14,7 @@ import com.google.android.gms.common.api.ResolvableApiException; import com.google.android.gms.common.api.Response; import com.google.android.gms.tasks.Task; +import org.microg.gms.common.Hide; import org.microg.gms.common.PublicApi; /** @@ -22,7 +23,7 @@ * If a {@link Task} with this response type fails, it will receive a {@link ResolvableApiException} which may be able to resolve the failure. * See {@link SettingsClient} for more details. *

- * The current location settings states can be accessed via {@link #getLocationSettingsStates()}. See {@link LocationSettingsResult} for more details. + * The current location settings states can be accessed via {@link #getLocationSettingsStates()}. See {@link LocationSettingsStates} for more details. */ public class LocationSettingsResponse extends Response { /** @@ -33,7 +34,7 @@ public LocationSettingsStates getLocationSettingsStates() { return getResult().getLocationSettingsStates(); } - @PublicApi(exclude = true) + @Hide public LocationSettingsResponse(@NonNull LocationSettingsResult result) { super(result); } diff --git a/play-services-location/src/main/java/com/google/android/gms/location/LocationSettingsResult.java b/play-services-location/src/main/java/com/google/android/gms/location/LocationSettingsResult.java index 4269b429b4..62e00c268d 100644 --- a/play-services-location/src/main/java/com/google/android/gms/location/LocationSettingsResult.java +++ b/play-services-location/src/main/java/com/google/android/gms/location/LocationSettingsResult.java @@ -1,72 +1,86 @@ /* - * Copyright (C) 2013-2017 microG Project Team - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * SPDX-FileCopyrightText: 2015 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. */ package com.google.android.gms.location; +import android.app.Activity; +import android.os.Parcel; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.Result; import com.google.android.gms.common.api.Status; +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; +import org.microg.gms.common.Hide; import org.microg.gms.common.PublicApi; -import org.microg.safeparcel.AutoSafeParcelable; -import org.microg.safeparcel.SafeParceled; /** - * Result of checking settings via checkLocationSettings(GoogleApiClient, LocationSettingsRequest), - * indicates whether a dialog should be shown to ask the user's consent to change their settings. - * The method getStatus() can be be used to confirm if the request was successful. If the current - * location settings don't satisfy the app's requirements and the user has permission to change the - * settings, the app could use startResolutionForResult(Activity, int) to start an intent to show a - * dialog, asking for user's consent to change the settings. The current location settings states - * can be accessed via getLocationSettingsStates(). See LocationSettingsResult for more details. + * Result of checking settings via {@link SettingsApi#checkLocationSettings(GoogleApiClient, LocationSettingsRequest)}, + * indicates whether a dialog should be shown to ask the user's consent to change their + * settings. + *

+ * The method {@link #getStatus()} can be used to confirm if the request was successful. If the current location settings don't + * satisfy the app's requirements and the user has permission to change the settings, the app could use + * {@link Status#startResolutionForResult(Activity, int)} to start an intent to show a dialog, asking for user's consent to + * change the settings. + *

+ * The current location settings states can be accessed via {@link #getLocationSettingsStates()}. See + * {@link LocationSettingsStates} for more details. */ @PublicApi -public class LocationSettingsResult extends AutoSafeParcelable implements Result { +@SafeParcelable.Class +public class LocationSettingsResult extends AbstractSafeParcelable implements Result { - @SafeParceled(1000) - private int versionCode = 1; + @Field(1000) + int versionCode = 1; - @SafeParceled(1) - private Status status; + @Field(1) + @NonNull + private final Status status; - @SafeParceled(2) - private LocationSettingsStates settings; + @Field(2) + @Nullable + private final LocationSettingsStates settings; /** * Retrieves the location settings states. */ + @Nullable public LocationSettingsStates getLocationSettingsStates() { return settings; } @Override + @NonNull public Status getStatus() { return status; } - @PublicApi(exclude = true) - public LocationSettingsResult(LocationSettingsStates settings, Status status) { + @Hide + @Constructor + public LocationSettingsResult(@Param(1) @NonNull Status status, @Param(2) @Nullable LocationSettingsStates settings) { this.settings = settings; this.status = status; } - @PublicApi(exclude = true) - public LocationSettingsResult(Status status) { - this.status = status; + @Hide + public LocationSettingsResult(@NonNull Status status) { + this(status, null); } - public static final Creator CREATOR = new AutoCreator(LocationSettingsResult.class); + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(LocationSettingsResult.class); + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } } diff --git a/play-services-location/src/main/java/com/google/android/gms/location/LocationSettingsStates.java b/play-services-location/src/main/java/com/google/android/gms/location/LocationSettingsStates.java index b4c14fd439..e2f6d02bb9 100644 --- a/play-services-location/src/main/java/com/google/android/gms/location/LocationSettingsStates.java +++ b/play-services-location/src/main/java/com/google/android/gms/location/LocationSettingsStates.java @@ -10,36 +10,41 @@ import android.app.Activity; import android.content.Intent; +import android.os.Parcel; +import androidx.annotation.NonNull; +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; import com.google.android.gms.common.internal.safeparcel.SafeParcelableSerializer; import org.microg.gms.common.PublicApi; -import org.microg.safeparcel.AutoSafeParcelable; /** * Stores the current states of all location-related settings. */ @PublicApi -public class LocationSettingsStates extends AutoSafeParcelable { +@SafeParcelable.Class +public class LocationSettingsStates extends AbstractSafeParcelable { @Field(1000) - private int versionCode = 2; + int versionCode = 2; @Field(1) - private boolean gpsUsable; + private final boolean gpsUsable; @Field(2) - private boolean networkLocationUsable; + private final boolean networkLocationUsable; @Field(3) - private boolean bleUsable; + private final boolean bleUsable; @Field(4) - private boolean gpsPresent; + private final boolean gpsPresent; @Field(5) - private boolean networkLocationPresent; + private final boolean networkLocationPresent; @Field(6) - private boolean blePresent; + private final boolean blePresent; /** * Whether BLE is present on the device. @@ -101,7 +106,8 @@ public boolean isNetworkLocationUsable() { return networkLocationUsable; } - public LocationSettingsStates(boolean gpsUsable, boolean networkLocationUsable, boolean bleUsable, boolean gpsPresent, boolean networkLocationPresent, boolean blePresent) { + @Constructor + public LocationSettingsStates(@Param(1) boolean gpsUsable, @Param(2) boolean networkLocationUsable, @Param(3) boolean bleUsable, @Param(4) boolean gpsPresent, @Param(5) boolean networkLocationPresent, @Param(6) boolean blePresent) { this.gpsUsable = gpsUsable; this.networkLocationUsable = networkLocationUsable; this.bleUsable = bleUsable; @@ -111,8 +117,9 @@ public LocationSettingsStates(boolean gpsUsable, boolean networkLocationUsable, } /** - * Retrieves the location settings states from the intent extras. When the location settings dialog finishes, you can use this method to retrieve the - * current location settings states from the intent in your {@link Activity#onActivityResult(int, int, Intent)}; + * Retrieves the location settings states from the intent extras. When the location settings dialog finishes, you can use this + * method to retrieve the current location settings states from the intent in your + * {@link Activity#onActivityResult(int, int, Intent)}. */ public static LocationSettingsStates fromIntent(Intent intent) { byte[] bytes = intent.getByteArrayExtra(EXTRA_NAME); @@ -120,7 +127,12 @@ public static LocationSettingsStates fromIntent(Intent intent) { return SafeParcelableSerializer.deserializeFromBytes(bytes, CREATOR); } - public static final Creator CREATOR = new AutoCreator(LocationSettingsStates.class); + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(LocationSettingsStates.class); + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } private static final String EXTRA_NAME = "com.google.android.gms.location.LOCATION_SETTINGS_STATES"; } diff --git a/play-services-location/src/main/java/com/google/android/gms/location/LocationSettingsStatusCodes.java b/play-services-location/src/main/java/com/google/android/gms/location/LocationSettingsStatusCodes.java index 028de68c3e..4722e02d8c 100644 --- a/play-services-location/src/main/java/com/google/android/gms/location/LocationSettingsStatusCodes.java +++ b/play-services-location/src/main/java/com/google/android/gms/location/LocationSettingsStatusCodes.java @@ -1,24 +1,18 @@ /* - * Copyright (C) 2017 microG Project Team - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * SPDX-FileCopyrightText: 2017 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + * Notice: Portions of this file are reproduced from work created and shared by Google and used + * according to terms described in the Creative Commons 4.0 Attribution License. + * See https://developers.google.com/readme/policies for details. */ package com.google.android.gms.location; +import androidx.annotation.NonNull; import com.google.android.gms.common.api.CommonStatusCodes; import com.google.android.gms.common.api.Status; +import org.microg.gms.common.Hide; import org.microg.gms.common.PublicApi; /** @@ -30,4 +24,20 @@ public class LocationSettingsStatusCodes extends CommonStatusCodes { * Location settings can't be changed to meet the requirements, no dialog pops up */ public static final int SETTINGS_CHANGE_UNAVAILABLE = 8502; + + @NonNull + @Hide + public static String getStatusCodeString(int statusCode) { + switch (statusCode) { + case SETTINGS_CHANGE_UNAVAILABLE: + return "SETTINGS_CHANGE_UNAVAILABLE"; + case 8500: + case 8501: + case 8503: + case 8505: + return "INTERNAL_LOCATION_SETTINGS_STATUS_CODE"; + default: + return CommonStatusCodes.getStatusCodeString(statusCode); + } + } } diff --git a/play-services-location/src/main/java/com/google/android/gms/location/SettingsClient.java b/play-services-location/src/main/java/com/google/android/gms/location/SettingsClient.java index 07be2580ef..4c485d721b 100644 --- a/play-services-location/src/main/java/com/google/android/gms/location/SettingsClient.java +++ b/play-services-location/src/main/java/com/google/android/gms/location/SettingsClient.java @@ -8,8 +8,11 @@ package com.google.android.gms.location; +import android.location.LocationManager; +import android.provider.Settings; import com.google.android.gms.common.api.Api; import com.google.android.gms.common.api.HasApiKey; +import com.google.android.gms.tasks.Task; /** * The main entry point for interacting with the location settings-enabler APIs. @@ -18,4 +21,24 @@ * location needs. */ public interface SettingsClient extends HasApiKey { + /** + * Checks if the relevant system settings are enabled on the device to carry out the desired location requests. + * + * @param locationSettingsRequest an object that contains all the location requirements that the client is interested in. + */ + Task checkLocationSettings(LocationSettingsRequest locationSettingsRequest); + + /** + * Returns true if the Google Location Accuracy setting is currently enabled. This setting is required for Fused Location + * Provider APIs to be able to generate network (wifi, cell, etc) based locations. If Google Play services is chosen as the + * platform {@link LocationManager#NETWORK_PROVIDER} (this is the case on all GMS compliant devices, which constitute the + * vast majority of the Android ecosystem), then this setting is also required for the platform + * {@link LocationManager#NETWORK_PROVIDER} to be enabled. + *

+ * On Android P and above devices, the Google Location Accuracy setting may be found under location settings. Below + * Android P, Google Location Accuracy is tied to the device location mode - it will be enabled if the device is in + * {@link Settings.Secure#LOCATION_MODE_BATTERY_SAVING} or {@link Settings.Secure#LOCATION_MODE_HIGH_ACCURACY}, and + * disabled in {@link Settings.Secure#LOCATION_MODE_SENSORS_ONLY}. + */ + Task isGoogleLocationAccuracyEnabled(); } diff --git a/play-services-location/src/main/java/org/microg/gms/location/SettingsClientImpl.java b/play-services-location/src/main/java/org/microg/gms/location/SettingsClientImpl.java index 5e4a4b7f1c..f8d891251b 100644 --- a/play-services-location/src/main/java/org/microg/gms/location/SettingsClientImpl.java +++ b/play-services-location/src/main/java/org/microg/gms/location/SettingsClientImpl.java @@ -7,14 +7,33 @@ import android.content.Context; +import android.os.RemoteException; import com.google.android.gms.common.api.Api; import com.google.android.gms.common.api.GoogleApi; -import com.google.android.gms.location.GeofencingClient; -import com.google.android.gms.location.LocationServices; -import com.google.android.gms.location.SettingsClient; +import com.google.android.gms.location.*; +import com.google.android.gms.location.internal.ISettingsCallbacks; +import com.google.android.gms.tasks.Task; +import com.google.android.gms.tasks.TaskCompletionSource; +import com.google.android.gms.tasks.Tasks; +import org.microg.gms.common.api.PendingGoogleApiCall; public class SettingsClientImpl extends GoogleApi implements SettingsClient { public SettingsClientImpl(Context context) { super(context, LocationServices.API); } + + @Override + public Task checkLocationSettings(LocationSettingsRequest locationSettingsRequest) { + return scheduleTask((PendingGoogleApiCall) (client, completionSource) -> client.getServiceInterface().requestLocationSettingsDialog(locationSettingsRequest, new ISettingsCallbacks.Stub() { + @Override + public void onLocationSettingsResult(LocationSettingsResult result) { + completionSource.setResult(new LocationSettingsResponse(result)); + } + }, null)); + } + + @Override + public Task isGoogleLocationAccuracyEnabled() { + return Tasks.forResult(true); // TODO + } }