[android-auto] Tell user to enable location services

Signed-off-by: Andrew Shkrob <andrew.shkrob.social@yandex.by>
This commit is contained in:
Andrew Shkrob 2024-12-29 15:51:20 +01:00
parent 05ae910403
commit 65e69c1b96
6 changed files with 124 additions and 28 deletions

View file

@ -1983,10 +1983,6 @@ public class MwmActivity extends BaseMwmFragmentActivity
Logger.w(POWER_MANAGEMENT_TAG, "Power Save mode wasn't disabled on the device");
}
/**
* Called by GoogleFusedLocationProvider to request to GPS and/or Wi-Fi.
* @param pendingIntent an intent to launch.
*/
@Override
@UiThread
public void onLocationResolutionRequired(@NonNull PendingIntent pendingIntent)
@ -2028,9 +2024,6 @@ public class MwmActivity extends BaseMwmFragmentActivity
}
}
/**
* Called by AndroidNativeLocationProvider when no suitable location methods are available.
*/
@Override
@UiThread
public void onLocationDisabled()

View file

@ -1,10 +1,13 @@
package app.organicmaps.car;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import android.content.Intent;
import android.content.res.Configuration;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresPermission;
import androidx.car.app.Screen;
import androidx.car.app.ScreenManager;
import androidx.car.app.Session;
@ -31,6 +34,7 @@ import app.organicmaps.car.util.ThemeUtils;
import app.organicmaps.display.DisplayChangedListener;
import app.organicmaps.display.DisplayManager;
import app.organicmaps.display.DisplayType;
import app.organicmaps.location.LocationHelper;
import app.organicmaps.location.LocationState;
import app.organicmaps.routing.RoutingController;
import app.organicmaps.util.Config;
@ -197,12 +201,25 @@ public final class CarAppSession extends Session implements DefaultLifecycleObse
return screensStack.get(screensStack.size() - 1);
}
@RequiresPermission(ACCESS_FINE_LOCATION)
@Override
public void onMyPositionModeChanged(int newMode)
{
final LocationHelper locationHelper = LocationHelper.from(getCarContext());
// Check if location was disabled by the user.
if (LocationState.getMode() == LocationState.NOT_FOLLOW_NO_POSITION)
{
Logger.i(TAG, "Location updates are stopped by the user manually.");
if (locationHelper.isActive())
locationHelper.stop();
}
final Screen screen = mScreenManager.getTop();
if (screen instanceof BaseMapScreen)
screen.invalidate();
if (LocationState.getMode() != LocationState.NOT_FOLLOW_NO_POSITION)
locationHelper.restartWithNewMode();
}
@Override

View file

@ -12,9 +12,18 @@ import androidx.car.app.model.Template;
import app.organicmaps.R;
import app.organicmaps.car.screens.base.BaseScreen;
import app.organicmaps.car.util.Colors;
import app.organicmaps.util.concurrency.ThreadPool;
import app.organicmaps.util.concurrency.UiThread;
import java.util.concurrent.ExecutorService;
public class ErrorScreen extends BaseScreen
{
public interface FinishPrecondition
{
boolean canFinish();
}
@StringRes
private final int mTitle;
@StringRes
@ -30,6 +39,11 @@ public class ErrorScreen extends BaseScreen
@Nullable
private final Runnable mNegativeButtonCallback;
@Nullable
private final FinishPrecondition mFinishPrecondition;
@Nullable
private ExecutorService mExecutorService;
private ErrorScreen(@NonNull Builder builder)
{
super(builder.mCarContext);
@ -39,6 +53,7 @@ public class ErrorScreen extends BaseScreen
mPositiveButtonCallback = builder.mPositiveButtonCallback;
mNegativeButtonText = builder.mNegativeButtonText;
mNegativeButtonCallback = builder.mNegativeButtonCallback;
mFinishPrecondition = builder.mFinishPrecondition;
}
@NonNull
@ -67,7 +82,11 @@ public class ErrorScreen extends BaseScreen
.setOnClickListener(this::onNegativeButton).build()
);
}
if (mFinishPrecondition != null && mExecutorService == null)
{
mExecutorService = ThreadPool.getWorker();
mExecutorService.execute(this::checkFinishPrecondition);
}
return builder.build();
}
@ -85,6 +104,17 @@ public class ErrorScreen extends BaseScreen
finish();
}
private void checkFinishPrecondition()
{
assert mFinishPrecondition != null;
assert mExecutorService != null;
if (mFinishPrecondition.canFinish())
UiThread.runLater(this::finish);
else
mExecutorService.execute(this::checkFinishPrecondition);
}
public static class Builder
{
@NonNull
@ -105,6 +135,9 @@ public class ErrorScreen extends BaseScreen
@Nullable
private Runnable mNegativeButtonCallback;
@Nullable
private FinishPrecondition mFinishPrecondition;
public Builder(@NonNull CarContext carContext)
{
mCarContext = carContext;
@ -136,8 +169,16 @@ public class ErrorScreen extends BaseScreen
return this;
}
public Builder setFinishPrecondition(@NonNull FinishPrecondition precondition)
{
mFinishPrecondition = precondition;
return this;
}
public ErrorScreen build()
{
if (mFinishPrecondition != null && (mPositiveButtonCallback != null || mNegativeButtonCallback != null))
throw new IllegalStateException("Finish precondition is not compatible with positive or negative button callbacks");
return new ErrorScreen(this);
}
}

View file

@ -2,11 +2,13 @@ package app.organicmaps.car.util;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import android.app.PendingIntent;
import android.location.Location;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresPermission;
import androidx.car.app.CarContext;
import androidx.car.app.ScreenManager;
import androidx.car.app.hardware.CarHardwareManager;
import androidx.car.app.hardware.common.CarValue;
import androidx.car.app.hardware.info.CarHardwareLocation;
@ -15,8 +17,13 @@ import androidx.car.app.hardware.info.Compass;
import androidx.core.content.ContextCompat;
import app.organicmaps.Map;
import app.organicmaps.R;
import app.organicmaps.car.screens.ErrorScreen;
import app.organicmaps.location.LocationHelper;
import app.organicmaps.location.LocationListener;
import app.organicmaps.location.LocationState;
import app.organicmaps.location.SensorHelper;
import app.organicmaps.util.LocationUtils;
import app.organicmaps.util.log.Logger;
import java.util.List;
@ -30,6 +37,29 @@ public class CarSensorsManager
private final CarContext mCarContext;
@NonNull
private final CarSensors mCarSensors;
@NonNull
private final LocationHelper mLocationHelper;
private final LocationListener mLocationListener = new LocationListener()
{
@Override
public void onLocationUpdated(@NonNull Location location)
{
// No op.
}
@Override
public void onLocationResolutionRequired(@NonNull PendingIntent pendingIntent)
{
onLocationError();
}
@Override
public void onLocationDisabled()
{
onLocationError();
}
};
private boolean mIsCarCompassUsed = true;
private boolean mIsCarLocationUsed = true;
@ -38,6 +68,7 @@ public class CarSensorsManager
{
mCarContext = context;
mCarSensors = mCarContext.getCarService(CarHardwareManager.class).getCarSensors();
mLocationHelper = LocationHelper.from(mCarContext);
}
@RequiresPermission(ACCESS_FINE_LOCATION)
@ -50,8 +81,9 @@ public class CarSensorsManager
else
SensorHelper.from(mCarContext).addListener(this::onCompassUpdated);
if (!LocationHelper.from(mCarContext).isActive())
LocationHelper.from(mCarContext).start();
if (!mLocationHelper.isActive())
mLocationHelper.start();
mLocationHelper.addListener(mLocationListener);
if (mIsCarLocationUsed)
mCarSensors.addCarHardwareLocationListener(CarSensors.UPDATE_RATE_FASTEST, executor, this::onCarLocationDataAvailable);
@ -66,6 +98,8 @@ public class CarSensorsManager
if (mIsCarLocationUsed)
mCarSensors.removeCarHardwareLocationListener(this::onCarLocationDataAvailable);
mLocationHelper.removeListener(mLocationListener);
}
private void onCarCompassDataAvailable(@NonNull final Compass compass)
@ -115,4 +149,23 @@ public class CarSensorsManager
mCarSensors.removeCompassListener(this::onCarCompassDataAvailable);
SensorHelper.from(mCarContext).addListener(this::onCompassUpdated);
}
private void onLocationError()
{
Logger.d(TAG);
LocationState.nativeOnLocationError(LocationState.ERROR_GPS_OFF);
final ErrorScreen errorScreen = new ErrorScreen.Builder(mCarContext)
.setTitle(R.string.enable_location_services)
.setErrorMessage(R.string.location_is_disabled_long_text)
.setFinishPrecondition(() -> {
if (LocationUtils.areLocationServicesTurnedOn(mCarContext))
{
if (LocationState.getMode() == LocationState.NOT_FOLLOW_NO_POSITION)
LocationState.nativeSwitchToNextMode();
return true;
}
return false;
}).build();
mCarContext.getCarService(ScreenManager.class).push(errorScreen);
}
}

View file

@ -14,11 +14,18 @@ public interface LocationListener
// No op.
}
/**
* Called by AndroidNativeLocationProvider when no suitable location methods are available.
*/
default void onLocationDisabled()
{
// No op.
}
/**
* Called by GoogleFusedLocationProvider to request to GPS and/or Wi-Fi.
* @param pendingIntent an intent to launch.
*/
default void onLocationResolutionRequired(@NonNull PendingIntent pendingIntent)
{
// No op.

View file

@ -7,12 +7,11 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import android.content.Context;
import android.location.Location;
import android.location.LocationManager;
import android.os.Build;
import android.provider.Settings;
import android.view.Surface;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.core.location.LocationManagerCompat;
public class LocationUtils
{
@ -95,22 +94,8 @@ public class LocationUtils
public static boolean areLocationServicesTurnedOn(@NonNull Context context)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
{
final LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
return lm.isLocationEnabled();
}
try
{
return Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.LOCATION_MODE)
!= Settings.Secure.LOCATION_MODE_OFF;
}
catch (Settings.SettingNotFoundException e)
{
e.printStackTrace();
return false;
}
final LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
return LocationManagerCompat.isLocationEnabled(lm);
}
public static boolean checkFineLocationPermission(@NonNull Context context)