mirror of
https://codeberg.org/comaps/comaps
synced 2026-01-05 12:13:54 +00:00
Compare commits
22 Commits
pastk-om-r
...
test/2026.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f71058f0f5 | ||
|
|
b07c22fc91 | ||
|
|
f5a9973c73 | ||
|
|
1d6d071c1a | ||
|
|
dc259f2b0e | ||
|
|
ca6b0286c8 | ||
|
|
f3fd814d07 | ||
|
|
6073cd1ca3 | ||
|
|
d29afb5b95 | ||
|
|
71cfe12164 | ||
|
|
59a6c6e630 | ||
|
|
374667366b | ||
|
|
e18e85a49d | ||
|
|
c7db4f9c33 | ||
|
|
266c38ed78 | ||
|
|
f4d6e32605 | ||
|
|
1b15810260 | ||
|
|
614ea4ec9c | ||
|
|
f5d3417997 | ||
|
|
f6ff08619e | ||
|
|
a4df3eaad5 | ||
|
|
55f55bbde1 |
@@ -9,7 +9,8 @@ import static app.organicmaps.leftbutton.LeftButtonsHolder.BUTTON_HELP_CODE;
|
||||
import static app.organicmaps.leftbutton.LeftButtonsHolder.BUTTON_RECORD_TRACK_CODE;
|
||||
import static app.organicmaps.leftbutton.LeftButtonsHolder.BUTTON_SETTINGS_CODE;
|
||||
import static app.organicmaps.sdk.location.LocationState.FOLLOW;
|
||||
import static app.organicmaps.sdk.location.LocationState.FOLLOW_AND_ROTATE;
|
||||
import static app.organicmaps.sdk.location.LocationState.FOLLOW_AND_ROTATE_COMPASS;
|
||||
import static app.organicmaps.sdk.location.LocationState.FOLLOW_AND_ROTATE_ROUTE;
|
||||
import static app.organicmaps.sdk.location.LocationState.LOCATION_TAG;
|
||||
import static app.organicmaps.sdk.util.PowerManagment.POWER_MANAGEMENT_TAG;
|
||||
import static app.organicmaps.sdk.util.Utils.dimen;
|
||||
@@ -1900,7 +1901,8 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
||||
|
||||
locationHelper.restartWithNewMode();
|
||||
|
||||
if ((newMode == FOLLOW || newMode == FOLLOW_AND_ROTATE) && !LocationUtils.checkFineLocationPermission(this))
|
||||
if ((newMode == FOLLOW || newMode == FOLLOW_AND_ROTATE_ROUTE || newMode == FOLLOW_AND_ROTATE_COMPASS)
|
||||
&& !LocationUtils.checkFineLocationPermission(this))
|
||||
{
|
||||
// Try to optimistically request FINE permission for FOLLOW and FOLLOW_AND_ROTATE modes.
|
||||
Logger.i(LOCATION_TAG, "Requesting ACCESS_FINE_LOCATION permission for " + LocationState.nameOf(newMode));
|
||||
|
||||
@@ -183,11 +183,16 @@ public final class UiHelpers
|
||||
drawableRes = R.drawable.ic_follow;
|
||||
tintColor = Colors.LOCATION_TINT;
|
||||
}
|
||||
case LocationState.FOLLOW_AND_ROTATE ->
|
||||
case LocationState.FOLLOW_AND_ROTATE_COMPASS ->
|
||||
{
|
||||
drawableRes = R.drawable.ic_follow_and_rotate;
|
||||
tintColor = Colors.LOCATION_TINT;
|
||||
}
|
||||
case LocationState.FOLLOW_AND_ROTATE_ROUTE ->
|
||||
{
|
||||
drawableRes = R.drawable.ic_follow_route;
|
||||
tintColor = Colors.LOCATION_TINT;
|
||||
}
|
||||
default -> throw new IllegalArgumentException("Invalid button mode: " + locationMode);
|
||||
}
|
||||
|
||||
|
||||
@@ -49,8 +49,8 @@ public class MyPositionButton
|
||||
int colorAttr = R.attr.iconTint;
|
||||
@DimenRes
|
||||
int sizeDimen = R.dimen.map_button_icon_size;
|
||||
if (mode == LocationState.FOLLOW || mode == LocationState.FOLLOW_AND_ROTATE
|
||||
|| mode == LocationState.PENDING_POSITION)
|
||||
if (mode == LocationState.FOLLOW || mode == LocationState.FOLLOW_AND_ROTATE_ROUTE
|
||||
|| mode == LocationState.FOLLOW_AND_ROTATE_COMPASS || mode == LocationState.PENDING_POSITION)
|
||||
{
|
||||
colorAttr = com.google.android.material.R.attr.colorSecondary;
|
||||
if (mode == LocationState.PENDING_POSITION)
|
||||
@@ -69,7 +69,8 @@ public class MyPositionButton
|
||||
case LocationState.NOT_FOLLOW_NO_POSITION -> R.drawable.ic_location_off;
|
||||
case LocationState.NOT_FOLLOW -> R.drawable.ic_location_crosshair;
|
||||
case LocationState.FOLLOW -> R.drawable.ic_follow;
|
||||
case LocationState.FOLLOW_AND_ROTATE -> R.drawable.ic_follow_and_rotate;
|
||||
case LocationState.FOLLOW_AND_ROTATE_COMPASS -> R.drawable.ic_follow_and_rotate;
|
||||
case LocationState.FOLLOW_AND_ROTATE_ROUTE -> R.drawable.ic_follow_route;
|
||||
default -> throw new IllegalArgumentException("Invalid button mode: " + mode);
|
||||
};
|
||||
image = ResourcesCompat.getDrawable(resources, drawableRes, context.getTheme());
|
||||
|
||||
30
android/app/src/main/res/drawable/ic_follow_route.xml
Normal file
30
android/app/src/main/res/drawable/ic_follow_route.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:strokeColor="@android:color/white"
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M15.15 16.92l-7.04 2.8v0.4l2.63 1.07 0.98 2.67h0.39z"/>
|
||||
<path
|
||||
android:strokeColor="@android:color/white"
|
||||
android:strokeWidth="2"
|
||||
android:strokeLineJoin="miter"
|
||||
android:pathData="m 16.25,15.95 c 2.29,-2.29 1.91,-3.87 -4.06,-3.87"/>
|
||||
<path
|
||||
android:strokeColor="@android:color/white"
|
||||
android:strokeWidth="2"
|
||||
android:strokeLineJoin="miter"
|
||||
android:pathData="M8.98 7.43c-2.97 2.96-2.72 4.66 3.24 4.66"/>
|
||||
<path
|
||||
android:strokeColor="@android:color/white"
|
||||
android:strokeWidth="2"
|
||||
android:strokeLineJoin="miter"
|
||||
android:pathData="M8.89 7.51l8.83-8.79"/>
|
||||
<path
|
||||
android:strokeColor="@android:color/white"
|
||||
android:strokeWidth="2"
|
||||
android:strokeLineJoin="miter"
|
||||
android:pathData="M9.2 22.83c-0.64 0.57-2.9 2.5-3.56 3.13"/>
|
||||
</vector>
|
||||
@@ -27,6 +27,8 @@ import app.organicmaps.sdk.util.NetworkPolicy;
|
||||
import app.organicmaps.sdk.util.log.Logger;
|
||||
import org.chromium.base.ObserverList;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class LocationHelper implements BaseLocationProvider.Listener
|
||||
{
|
||||
private static final long INTERVAL_MS = 500;
|
||||
@@ -56,6 +58,12 @@ public class LocationHelper implements BaseLocationProvider.Listener
|
||||
private Handler mHandler;
|
||||
private Runnable mLocationTimeoutRunnable = this::notifyLocationUpdateTimeout;
|
||||
|
||||
private static final double INTERVAL_PROVIDER_DECISION = 3.0; // seconds
|
||||
private final HashMap<String, Integer> mProviderLocationCounts = new HashMap<>();
|
||||
private final HashMap<String, Float> mProviderAccuracyMeans = new HashMap<>();
|
||||
private double mTimeAtLastProviderChange = Double.NaN;
|
||||
private String mCurrentProvider = null;
|
||||
|
||||
@NonNull
|
||||
private final GnssStatusCompat.Callback mGnssStatusCallback = new GnssStatusCompat.Callback() {
|
||||
@Override
|
||||
@@ -187,7 +195,6 @@ public class LocationHelper implements BaseLocationProvider.Listener
|
||||
@Override
|
||||
public void onLocationChanged(@NonNull Location location)
|
||||
{
|
||||
Logger.d(TAG, "provider = " + mLocationProvider.getClass().getSimpleName() + " location = " + location);
|
||||
|
||||
if (!isActive())
|
||||
{
|
||||
@@ -201,21 +208,59 @@ public class LocationHelper implements BaseLocationProvider.Listener
|
||||
return;
|
||||
}
|
||||
|
||||
if (mSavedLocation != null)
|
||||
{
|
||||
if (!LocationUtils.isLocationBetterThanLast(location, mSavedLocation))
|
||||
{
|
||||
Logger.d(TAG, "The new " + location + " is worse than the last " + mSavedLocation);
|
||||
updateProviderDecision(location);
|
||||
if(mCurrentProvider != null && !mCurrentProvider.equals(location.getProvider())) {
|
||||
Logger.d(TAG, "REJECTED: provider = " + mLocationProvider.getClass().getSimpleName() + " location = " + location);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.d(TAG, "provider = " + mLocationProvider.getClass().getSimpleName() + " location = " + location);
|
||||
|
||||
mSavedLocation = location;
|
||||
mMyPosition = null;
|
||||
notifyLocationUpdated();
|
||||
}
|
||||
|
||||
// Used by GoogleFusedLocationProvider.
|
||||
private void updateProviderDecision(Location location) {
|
||||
if(Double.isNaN(mTimeAtLastProviderChange))
|
||||
mTimeAtLastProviderChange = location.getElapsedRealtimeNanos() * 1.0E-9;
|
||||
|
||||
String provider = location.getProvider();
|
||||
int count = mProviderLocationCounts.getOrDefault(provider, 0);
|
||||
float average = mProviderAccuracyMeans.getOrDefault(provider, 0.0f);
|
||||
|
||||
float accuracy = location.getAccuracy();
|
||||
float newAverage = (count * average + accuracy) / (count + 1);
|
||||
|
||||
mProviderLocationCounts.put(provider, count + 1);
|
||||
mProviderAccuracyMeans.put(provider, newAverage);
|
||||
|
||||
double currentTime = location.getElapsedRealtimeNanos();
|
||||
double timeDiff = (currentTime - mTimeAtLastProviderChange) * 1.0E-9;
|
||||
|
||||
if(timeDiff > INTERVAL_PROVIDER_DECISION) {
|
||||
mCurrentProvider = getMinAccuracyProvider();
|
||||
Logger.d(TAG, "Selected: " + mCurrentProvider + ", with acc. " + mProviderAccuracyMeans.get(mCurrentProvider));
|
||||
mTimeAtLastProviderChange = currentTime;
|
||||
mProviderLocationCounts.clear();
|
||||
mProviderAccuracyMeans.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private String getMinAccuracyProvider() {
|
||||
String minAccuracyProvider = null;
|
||||
float minAccuracy = Float.MAX_VALUE;
|
||||
for(String p : mProviderAccuracyMeans.keySet()) {
|
||||
float pAccuracy = mProviderAccuracyMeans.get(p);
|
||||
if(pAccuracy < minAccuracy) {
|
||||
minAccuracy = pAccuracy;
|
||||
minAccuracyProvider = p;
|
||||
}
|
||||
}
|
||||
return minAccuracyProvider;
|
||||
}
|
||||
|
||||
// Used by GoogleFusedLocationProvider.
|
||||
@SuppressWarnings("unused")
|
||||
@Override
|
||||
@UiThread
|
||||
|
||||
@@ -20,7 +20,8 @@ public final class LocationState
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({PENDING_POSITION, NOT_FOLLOW_NO_POSITION, NOT_FOLLOW, FOLLOW, FOLLOW_AND_ROTATE})
|
||||
@IntDef({PENDING_POSITION, NOT_FOLLOW_NO_POSITION, NOT_FOLLOW, FOLLOW, FOLLOW_AND_ROTATE_COMPASS,
|
||||
FOLLOW_AND_ROTATE_ROUTE})
|
||||
@interface Value
|
||||
{}
|
||||
|
||||
@@ -29,7 +30,8 @@ public final class LocationState
|
||||
public static final int NOT_FOLLOW_NO_POSITION = 1;
|
||||
public static final int NOT_FOLLOW = 2;
|
||||
public static final int FOLLOW = 3;
|
||||
public static final int FOLLOW_AND_ROTATE = 4;
|
||||
public static final int FOLLOW_AND_ROTATE_COMPASS = 4;
|
||||
public static final int FOLLOW_AND_ROTATE_ROUTE = 5;
|
||||
|
||||
// These constants should correspond to values defined in platform/location.hpp
|
||||
// Leave 0-value as no any error.
|
||||
@@ -69,7 +71,8 @@ public final class LocationState
|
||||
case NOT_FOLLOW_NO_POSITION -> "NOT_FOLLOW_NO_POSITION";
|
||||
case NOT_FOLLOW -> "NOT_FOLLOW";
|
||||
case FOLLOW -> "FOLLOW";
|
||||
case FOLLOW_AND_ROTATE -> "FOLLOW_AND_ROTATE";
|
||||
case FOLLOW_AND_ROTATE_COMPASS -> "FOLLOW_AND_ROTATE_COMPASS";
|
||||
case FOLLOW_AND_ROTATE_ROUTE -> "FOLLOW_AND_ROTATE_ROUTE";
|
||||
default -> "Unknown: " + mode;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -81,20 +81,6 @@ public class LocationUtils
|
||||
return location.getAccuracy() > 0.0f;
|
||||
}
|
||||
|
||||
public static boolean isLocationBetterThanLast(@NonNull Location newLocation, @NonNull Location lastLocation)
|
||||
{
|
||||
if (newLocation.getElapsedRealtimeNanos() < lastLocation.getElapsedRealtimeNanos())
|
||||
return false;
|
||||
|
||||
// As described in isAccuracySatisfied, GPS may have zero accuracy "for some reasons".
|
||||
if (isFromGpsProvider(lastLocation) && lastLocation.getAccuracy() == 0.0f)
|
||||
return true;
|
||||
|
||||
double speed = Math.max(DEFAULT_SPEED_MPS, (newLocation.getSpeed() + lastLocation.getSpeed()) / 2.0);
|
||||
double lastAccuracy = lastLocation.getAccuracy() + speed * LocationUtils.getTimeDiff(lastLocation, newLocation);
|
||||
return newLocation.getAccuracy() < lastAccuracy;
|
||||
}
|
||||
|
||||
public static boolean areLocationServicesTurnedOn(@NonNull Context context)
|
||||
{
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
|
||||
|
||||
4614
data/countries.txt
4614
data/countries.txt
File diff suppressed because it is too large
Load Diff
@@ -1674,57 +1674,57 @@ CoMaps 的地理位置数据共享应该是启用的。</p>
|
||||
<dd lang="en">
|
||||
<p>Default battery optimization settings on Samsung, Huawei, Google, Xiaomi, OnePlus, Meizu, Asus, Wiko, Lenovo, Oppo, Vivo, Realme, Sony, Motorola, HTC and other devices may stop or kill CoMaps app in the background.</p>
|
||||
<p>This is especially true for versions Android 11 and higher</p>
|
||||
<p>The exact steps on how to make CoMaps (and other apps) work in the background are listed here: https://dontkillmyapp.com/</p>
|
||||
<p>The exact steps on how to make CoMaps (and other apps) work in the background are listed here: <a href="https://dontkillmyapp.com/">https://dontkillmyapp.com/</a></p>
|
||||
</dd>
|
||||
<dd lang="ru">
|
||||
<p>Настройки оптимизации батареи по умолчанию на устройствах Samsung, Huawei, Google, Xiaomi, OnePlus, Meizu, Asus, Wiko, Lenovo, Oppo, Vivo, Realme, Sony, Motorola, HTC и других могут остановить или закрыть приложение CoMaps в фоновом режиме.</p>
|
||||
<p>Это особенно актуально для версий Android 11 и выше.</p>
|
||||
<p>На этом сайте описано, как настроить работу в фоновом режиме для CoMaps и других приложений: https://dontkillmyapp.com/</p>
|
||||
<p>На этом сайте описано, как настроить работу в фоновом режиме для CoMaps и других приложений: <a href="https://dontkillmyapp.com/">https://dontkillmyapp.com/</a></p>
|
||||
|
||||
</dd><dd lang="de">
|
||||
<p>Die Standardeinstellungen zur Akkuoptimierung auf Samsung, Huawei, Google, Xiaomi, OnePlus, Meizu, Asus, Wiko, Lenovo, Oppo, Vivo, Realme, Sony, Motorola, HTC und anderen Geräten können die CoMaps-App im Hintergrund stoppen oder beenden.</p>
|
||||
<p>Dies gilt insbesondere für Versionen Android 11 und höher</p>
|
||||
<p>Die genauen Schritte, wie man CoMaps (und andere Apps) im Hintergrund zum Laufen bringt, sind hier aufgeführt: https://dontkillmyapp.com/</p>
|
||||
<p>Die genauen Schritte, wie man CoMaps (und andere Apps) im Hintergrund zum Laufen bringt, sind hier aufgeführt: <a href="https://dontkillmyapp.com/">https://dontkillmyapp.com/</a></p>
|
||||
|
||||
</dd><dd lang="es">
|
||||
<p>La configuración predeterminada de optimización de la batería en Samsung, Huawei, Google, Xiaomi, OnePlus, Meizu, Asus, Wiko, Lenovo, Oppo, Vivo, Realme, Sony, Motorola, HTC y otros dispositivos puede detener o cerrar la aplicación CoMaps en segundo plano.</p>
|
||||
<p>Esto es especialmente cierto para las versiones de Android 11 y superior</p>
|
||||
<p>Los pasos exactos sobre cómo hacer que CoMaps (y otras aplicaciones) funcionen en segundo plano se enumeran aquí (en inglés): https://dontkillmyapp.com/</p>
|
||||
<p>Los pasos exactos sobre cómo hacer que CoMaps (y otras aplicaciones) funcionen en segundo plano se enumeran aquí (en inglés): <a href="https://dontkillmyapp.com/">https://dontkillmyapp.com/</a></p>
|
||||
|
||||
</dd><dd lang="fr">
|
||||
<p>Les paramètres d'optimisation de la batterie par défaut sur Samsung, Huawei, Google, Xiaomi, OnePlus, Meizu, Asus, Wiko, Lenovo, Oppo, Vivo, Realme, Sony, Motorola, HTC et d'autres appareils peuvent arrêter ou tuer l'application CoMaps en arrière-plan.</p>
|
||||
<p>Cela est particulièrement vrai pour les versions Android 11 et supérieures</p>
|
||||
<p>Les étapes exactes pour faire fonctionner CoMaps (et d'autres applications) en arrière-plan sont listées ici : https://dontkillmyapp.com/</p>
|
||||
<p>Les étapes exactes pour faire fonctionner CoMaps (et d'autres applications) en arrière-plan sont listées ici : <a href="https://dontkillmyapp.com/">https://dontkillmyapp.com/</a></p>
|
||||
|
||||
</dd><dd lang="pl">
|
||||
<p>Domyślne ustawienia optymalizacji baterii na urządzeniach Samsung, Huawei, Google, Xiaomi, OnePlus, Meizu, Asus, Wiko, Lenovo, Oppo, Vivo, Realme, Sony, Motorola, HTC i innych, mogą zatrzymać aplikację CoMaps działającą w tle.</p>
|
||||
<p>Dotyczy to szczególnie wersji Android 11 i wyższych</p>
|
||||
<p>Dokładne kroki, jak sprawić, by CoMaps (i inne aplikacje) działały w tle, są wymienione tutaj: https://dontkillmyapp.com/</p>
|
||||
<p>Dokładne kroki, jak sprawić, by CoMaps (i inne aplikacje) działały w tle, są wymienione tutaj: <a href="https://dontkillmyapp.com/">https://dontkillmyapp.com/</a></p>
|
||||
|
||||
</dd><dd lang="pt">
|
||||
<p>As definições predefinidas de otimização da bateria na Samsung, Huawei, Google, Xiaomi, OnePlus, Meizu, Asus, Wiko, Lenovo, Oppo, Vivo, Realme, Sony, Motorola, HTC e outros dispositivos podem parar ou encerrar a aplicação CoMaps em segundo plano.</p>
|
||||
<p>Isso é especialmente verdadeiro para as versões Android 11 e superior</p>
|
||||
<p>Os passos exatos sobre como fazer com que o CoMaps (e outras aplicações) funcionem em segundo plano estão listados aqui: https://dontkillmyapp.com/</p>
|
||||
<p>Os passos exatos sobre como fazer com que o CoMaps (e outras aplicações) funcionem em segundo plano estão listados aqui: <a href="https://dontkillmyapp.com/">https://dontkillmyapp.com/</a></p>
|
||||
|
||||
</dd><dd lang="pt-BR">
|
||||
<p>As configurações padrão de otimização da bateria na Samsung, Huawei, Google, Xiaomi, OnePlus, Meizu, Asus, Wiko, Lenovo, Oppo, Vivo, Realme, Sony, Motorola, HTC e outros dispositivos podem interromper ou fechar o aplicativo CoMaps em segundo plano.</p>
|
||||
<p>Isso é especialmente verdadeiro para as versões Android 11 e superiores</p>
|
||||
<p>O passo-a-passo sobre como fazer o CoMaps (e outros aplicativos) funcionar em segundo plano pode ser encontrado aqui: https://dontkillmyapp.com/</p>
|
||||
<p>O passo-a-passo sobre como fazer o CoMaps (e outros aplicativos) funcionar em segundo plano pode ser encontrado aqui: <a href="https://dontkillmyapp.com/">https://dontkillmyapp.com/</a></p>
|
||||
|
||||
</dd><dd lang="tr">
|
||||
<p>Samsung, Huawei, Google, Xiaomi, OnePlus, Meizu, Asus, Wiko, Lenovo, Oppo, Vivo, Realme, Sony, Motorola, HTC ve diğer cihazlardaki varsayılan pil optimizasyon ayarları arka planda CoMaps uygulamasını durdurabilir veya öldürebilir.</p>
|
||||
<p>Bu özellikle Android 11 ve üstü sürümler için geçerlidir</p>
|
||||
<p>CoMaps'ın (ve diğer uygulamaların) arka planda nasıl çalışacağına ilişkin tam adımlar burada listelenmiştir: https://dontkillmyapp.com/</p>
|
||||
<p>CoMaps'ın (ve diğer uygulamaların) arka planda nasıl çalışacağına ilişkin tam adımlar burada listelenmiştir: <a href="https://dontkillmyapp.com/">https://dontkillmyapp.com/</a></p>
|
||||
|
||||
</dd><dd lang="uk">
|
||||
<p>Стандартні налаштування оптимізації роботи акумулятора на пристроях Samsung, Huawei, Google, Xiaomi, OnePlus, Meizu, Asus, Wiko, Lenovo, Oppo, Vivo, Realme, Sony, Motorola, HTC та інших пристроях можуть зупиняти або закривати додаток CoMaps у фоновому режимі.</p>
|
||||
<p>Особливо це стосується версій Android 11 і вище</p>
|
||||
<p>На цьому сайті описано, як налаштувати роботу у фоновому режимі для CoMaps та інших додатків: https://dontkillmyapp.com/</p>
|
||||
<p>На цьому сайті описано, як налаштувати роботу у фоновому режимі для CoMaps та інших додатків: <a href="https://dontkillmyapp.com/">https://dontkillmyapp.com/</a></p>
|
||||
|
||||
</dd><dd lang="zh">
|
||||
<p>三星、华为、谷歌、小米、OnePlus、美图、华硕、Wiko、联想、Oppo、Vivo、Realme、索尼、摩托罗拉、HTC 和其他设备上的默认电池优化设置可能会在后台停止或杀死CoMaps应用程序。</p>
|
||||
<p>对于 Android 11 及更高版本来说尤其如此</p>
|
||||
<p>如何让CoMaps(和其他应用程序)在后台工作的具体步骤在此列出:https://dontkillmyapp.com/</p>
|
||||
<p>如何让CoMaps(和其他应用程序)在后台工作的具体步骤在此列出:<a href="https://dontkillmyapp.com/">https://dontkillmyapp.com/</a></p>
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
108
data/sound-strings/ta.json/localize.json
Normal file
108
data/sound-strings/ta.json/localize.json
Normal file
@@ -0,0 +1,108 @@
|
||||
{
|
||||
"make_a_slight_right_turn":"லேசாக வலதுபுறம் திரும்புங்கள்.",
|
||||
"make_a_slight_right_turn_street":"NULL",
|
||||
"make_a_slight_right_turn_street_verb":"NULL",
|
||||
"make_a_right_turn":"வலதுபுறமாக திரும்புங்கள்.",
|
||||
"make_a_right_turn_street":"NULL",
|
||||
"make_a_sharp_right_turn":"கூர்மையாக வலதுபுறம் திரும்புங்கள்.",
|
||||
"make_a_sharp_right_turn_street":"NULL",
|
||||
"enter_the_roundabout":"ரவுண்டபௌட்டை நுழையுங்கள்.",
|
||||
"enter_the_roundabout_street":"NULL",
|
||||
"enter_the_roundabout_street_verb":"NULL",
|
||||
"leave_the_roundabout":"ரவுண்டபௌட்டை வெளியேறுங்கள்.",
|
||||
"leave_the_roundabout_street":"NULL",
|
||||
"leave_the_roundabout_street_verb":"NULL",
|
||||
"make_a_slight_left_turn":"லேசாக இடதுபுறம் திரும்புங்கள்.",
|
||||
"make_a_slight_left_turn_street":"NULL",
|
||||
"make_a_slight_left_turn_street_verb":"NULL",
|
||||
"make_a_left_turn":"இடதுபுறமாக திரும்புங்கள்.",
|
||||
"make_a_left_turn_street":"NULL",
|
||||
"make_a_sharp_left_turn":"கூர்மையாக இடதுபுறம் திரும்புங்கள்.",
|
||||
"make_a_sharp_left_turn_street":"NULL",
|
||||
"make_a_u_turn":"U-வழியில் திரும்புங்கள்.",
|
||||
"make_a_u_turn_street":"NULL",
|
||||
"make_a_u_turn_street_verb":"NULL",
|
||||
"go_straight":"நேரா போங்க.",
|
||||
"exit":"வெளியேறுங்கள்.",
|
||||
"onto":"நோக்கி",
|
||||
"take_exit_number":"வெளியேறும் வழி எடுங்கள்",
|
||||
"take_exit_number_street_verb":"NULL",
|
||||
"route_recalculating":"வழி மறுபடி கணிக்கிறது",
|
||||
"destination":"நீங்கள் வருவீர்கள்.",
|
||||
"you_have_reached_the_destination":"நீங்கள் வந்துவிட்டீர்கள்..",
|
||||
"in_50_meters":"ஐம்பது மீட்டரில்",
|
||||
"in_100_meters":"நூறு மீட்டரில்",
|
||||
"in_200_meters":"இருநூறு மீட்டரில்",
|
||||
"in_250_meters":"இருநூற்று ஐம்பது மீட்டரில்",
|
||||
"in_300_meters":"முன்னூறு மீட்டரில்",
|
||||
"in_400_meters":"நானூறு மீட்டரில்",
|
||||
"in_500_meters":"ஐநூறு மீட்டரில்",
|
||||
"in_600_meters":"அறுநூறு மீட்டரில்",
|
||||
"in_700_meters":"எழுநூறு மீட்டரில்",
|
||||
"in_750_meters":"எழுநூறு ஐம்பது மீட்டரில்",
|
||||
"in_800_meters":"எண்ணூறு மீட்டரில்",
|
||||
"in_900_meters":"தொள்ளாயிரம் மீட்டரில்",
|
||||
"in_1_kilometer":"ஒரு கிலோமீட்டரில்",
|
||||
"in_1_5_kilometers":"ஒன்றரை கிலோமீட்டரில்",
|
||||
"in_2_kilometers":"இரண்டு கிலோமீட்டரில்",
|
||||
"in_2_5_kilometers":"இரண்டரை கிலோமீட்டரில்",
|
||||
"in_3_kilometers":"மூன்று கிலோமீட்டரில்",
|
||||
"then":"அடுத்தது",
|
||||
"dist_direction_onto_street":"%1$s %4$s %3$s %5$s %2$s",
|
||||
"take_the_1_exit":"ஒன்றாம் வெளியேறும் வழி எடுங்கள்.",
|
||||
"take_the_1_exit_street":"NULL",
|
||||
"take_the_1_exit_street_verb":"NULL",
|
||||
"take_the_2_exit":"இரண்டாம் வெளியேறும் வழி எடுங்கள்.",
|
||||
"take_the_2_exit_street":"NULL",
|
||||
"take_the_2_exit_street_verb":"NULL",
|
||||
"take_the_3_exit":"மூன்றாம் வெளியேறும் வழி எடுங்கள்.",
|
||||
"take_the_3_exit_street":"NULL",
|
||||
"take_the_3_exit_street_verb":"NULL",
|
||||
"take_the_4_exit":"நான்காவது வெளியேறும் வழி எடுங்கள்.",
|
||||
"take_the_4_exit_street":"NULL",
|
||||
"take_the_4_exit_street_verb":"NULL",
|
||||
"take_the_5_exit":"ஐந்தாவது வெளியேறும் வழி எடுங்கள்.",
|
||||
"take_the_5_exit_street":"NULL",
|
||||
"take_the_5_exit_street_verb":"NULL",
|
||||
"take_the_6_exit":"ஆறாவது வெளியேறும் வழி எடுங்கள்.",
|
||||
"take_the_6_exit_street":"NULL",
|
||||
"take_the_6_exit_street_verb":"NULL",
|
||||
"take_the_7_exit":"ஏழாவது வெளியேறும் வழி எடுங்கள்.",
|
||||
"take_the_7_exit_street":"NULL",
|
||||
"take_the_7_exit_street_verb":"NULL",
|
||||
"take_the_8_exit":"எட்டாவது வெளியேறும் வழி எடுங்கள்.",
|
||||
"take_the_8_exit_street":"NULL",
|
||||
"take_the_8_exit_street_verb":"NULL",
|
||||
"take_the_9_exit":"ஒன்பதாவது வெளியேறும் வழி எடுங்கள்.",
|
||||
"take_the_9_exit_street":"NULL",
|
||||
"take_the_9_exit_street_verb":"NULL",
|
||||
"take_the_10_exit":"பத்தாவது வெளியேறும் வழி எடுங்கள்.",
|
||||
"take_the_10_exit_street":"NULL",
|
||||
"take_the_10_exit_street_verb":"NULL",
|
||||
"take_the_11_exit":"பதினொன்றாவது வெளியேறும் வழி எடுங்கள்.",
|
||||
"take_the_11_exit_street":"NULL",
|
||||
"take_the_11_exit_street_verb":"NULL",
|
||||
"in_50_feet":"ஐம்பது அடியில்",
|
||||
"in_100_feet":"நூறு அடியில்",
|
||||
"in_200_feet":"இருநூறு அடியில்",
|
||||
"in_300_feet":"முன்னூறு அடியில்",
|
||||
"in_400_feet":"நானூறு அடியில்",
|
||||
"in_500_feet":"ஐநூறு அடியில்",
|
||||
"in_600_feet":"அறுநூறு அடியில்",
|
||||
"in_700_feet":"எழுநூறு அடியில்",
|
||||
"in_800_feet":"எண்ணூறு அடியில்",
|
||||
"in_900_feet":"தொள்ளாயிரம் அடியில்",
|
||||
"in_1000_feet":"ஆயிரம் அடியில்",
|
||||
"in_1500_feet":"ஆயிரம் ஐநூறு அடியில்",
|
||||
"in_2000_feet":"இரண்டு ஆயிரம் அடியில்",
|
||||
"in_2500_feet":"இரண்டு ஆயிரம் ஐநூறு அடியில்",
|
||||
"in_3000_feet":"மூன்று ஆயிரம் அடியில்",
|
||||
"in_3500_feet":"மூன்று ஆயிரம் ஐநூறு அடியில்",
|
||||
"in_4000_feet":"நான்கு ஆயிரம் அடியில்",
|
||||
"in_4500_feet":"நான்கு ஆயிரம் ஐநூறு அடியில்",
|
||||
"in_5000_feet":"ஐந்து ஆயிரம் அடியில்",
|
||||
"in_1_mile":"ஒன்று மைலில்",
|
||||
"in_1_5_miles":"ஒன்றரை மைலில்",
|
||||
"in_2_miles":"இரண்டு மைலில்",
|
||||
"unknown_camera":"கேமரா முன்னால் இருக்கிறது"
|
||||
}
|
||||
@@ -239,7 +239,7 @@ final class CarPlayService: NSObject {
|
||||
MapTemplateBuilder.configureBaseUI(mapTemplate: mapTemplate)
|
||||
if currentPositionMode == .pendingPosition {
|
||||
mapTemplate.leadingNavigationBarButtons = []
|
||||
} else if currentPositionMode == .follow || currentPositionMode == .followAndRotate {
|
||||
} else if currentPositionMode == .follow || currentPositionMode == .followAndRotateCompass || currentPositionMode == .followAndRotateRoute {
|
||||
MapTemplateBuilder.setupDestinationButton(mapTemplate: mapTemplate)
|
||||
} else {
|
||||
MapTemplateBuilder.setupRecenterButton(mapTemplate: mapTemplate)
|
||||
@@ -623,22 +623,32 @@ extension CarPlayService: CarPlayRouterListener {
|
||||
extension CarPlayService: LocationModeListener {
|
||||
func processMyPositionStateModeEvent(_ mode: MWMMyPositionMode) {
|
||||
currentPositionMode = mode
|
||||
guard let rootMapTemplate = rootMapTemplate,
|
||||
let info = rootMapTemplate.userInfo as? MapInfo,
|
||||
info.type == CPConstants.TemplateType.main else {
|
||||
|
||||
// make sure we have a rootMapTemplate
|
||||
guard let rootMapTemplate = rootMapTemplate else {
|
||||
return
|
||||
}
|
||||
|
||||
// exit if we're navigating
|
||||
guard let info = rootMapTemplate.userInfo as? MapInfo,
|
||||
info.type == CPConstants.TemplateType.main else {
|
||||
MapTemplateBuilder.updateMyPositionModeButton(mapTemplate: rootMapTemplate, newMode: mode)
|
||||
return
|
||||
}
|
||||
switch mode {
|
||||
case .follow, .followAndRotate:
|
||||
case .follow, .followAndRotateCompass, .followAndRotateRoute:
|
||||
if !rootMapTemplate.isPanningInterfaceVisible {
|
||||
MapTemplateBuilder.setupDestinationButton(mapTemplate: rootMapTemplate)
|
||||
MapTemplateBuilder.updateMyPositionModeButton(mapTemplate: rootMapTemplate, newMode: mode)
|
||||
}
|
||||
case .notFollow:
|
||||
if !rootMapTemplate.isPanningInterfaceVisible {
|
||||
MapTemplateBuilder.setupRecenterButton(mapTemplate: rootMapTemplate)
|
||||
MapTemplateBuilder.updateMyPositionModeButton(mapTemplate: rootMapTemplate, newMode: mode)
|
||||
}
|
||||
case .pendingPosition, .notFollowNoPosition:
|
||||
rootMapTemplate.leadingNavigationBarButtons = []
|
||||
MapTemplateBuilder.updateMyPositionModeButton(mapTemplate: rootMapTemplate, newMode: mode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ final class MapTemplateBuilder {
|
||||
case startPanning
|
||||
case zoomIn
|
||||
case zoomOut
|
||||
case myPositionMode
|
||||
}
|
||||
enum BarButtonType {
|
||||
case dismissPaning
|
||||
@@ -28,7 +29,7 @@ final class MapTemplateBuilder {
|
||||
configureBaseUI(mapTemplate: mapTemplate)
|
||||
if positionMode == .pendingPosition {
|
||||
mapTemplate.leadingNavigationBarButtons = []
|
||||
} else if positionMode == .follow || positionMode == .followAndRotate {
|
||||
} else if positionMode == .follow || positionMode == .followAndRotateCompass || positionMode == .followAndRotateRoute {
|
||||
setupDestinationButton(mapTemplate: mapTemplate)
|
||||
} else {
|
||||
setupRecenterButton(mapTemplate: mapTemplate)
|
||||
@@ -69,7 +70,10 @@ final class MapTemplateBuilder {
|
||||
let zoomOutButton = buildMapButton(type: .zoomOut) { _ in
|
||||
FrameworkHelper.zoomMap(.out)
|
||||
}
|
||||
mapTemplate.mapButtons = [panningButton, zoomInButton, zoomOutButton]
|
||||
let myPositionModeButton = buildMapButton(type: .myPositionMode) { _ in
|
||||
FrameworkHelper.switchMyPositionMode()
|
||||
}
|
||||
mapTemplate.mapButtons = [myPositionModeButton, panningButton, zoomInButton, zoomOutButton]
|
||||
|
||||
let settingsButton = buildBarButton(type: .settings) { _ in
|
||||
let gridTemplate = SettingsTemplateBuilder.buildGridTemplate()
|
||||
@@ -99,7 +103,10 @@ final class MapTemplateBuilder {
|
||||
let panningButton = buildMapButton(type: .startPanning) { _ in
|
||||
mapTemplate.showPanningInterface(animated: true)
|
||||
}
|
||||
mapTemplate.mapButtons = [panningButton]
|
||||
let myPositionModeButton = buildMapButton(type: .myPositionMode) { _ in
|
||||
FrameworkHelper.switchMyPositionMode()
|
||||
}
|
||||
mapTemplate.mapButtons = [myPositionModeButton, panningButton]
|
||||
setupMuteAndRedirectButtons(template: mapTemplate)
|
||||
let endButton = buildBarButton(type: .endRoute) { _ in
|
||||
CarPlayService.shared.cancelCurrentTrip()
|
||||
@@ -117,6 +124,28 @@ final class MapTemplateBuilder {
|
||||
mapTemplate.leadingNavigationBarButtons = [destinationButton]
|
||||
}
|
||||
|
||||
class func updateMyPositionModeButton(mapTemplate: CPMapTemplate, newMode: MWMMyPositionMode) {
|
||||
let button = CPMapButton(handler: { _ in
|
||||
FrameworkHelper.switchMyPositionMode()
|
||||
})
|
||||
|
||||
switch newMode {
|
||||
case .pendingPosition:
|
||||
button.image = UIImage(systemName: "location.fill")
|
||||
case .notFollowNoPosition:
|
||||
button.image = UIImage(systemName: "location")
|
||||
case .notFollow:
|
||||
button.image = UIImage(systemName: "location")
|
||||
case .follow:
|
||||
button.image = UIImage(systemName: "location.fill")
|
||||
case .followAndRotate:
|
||||
button.image = UIImage(systemName: "location.north.line.fill")
|
||||
}
|
||||
if mapTemplate.mapButtons.count > 0 {
|
||||
mapTemplate.mapButtons[0] = button
|
||||
}
|
||||
}
|
||||
|
||||
class func setupRecenterButton(mapTemplate: CPMapTemplate) {
|
||||
let recenterButton = buildBarButton(type: .recenter) { _ in
|
||||
FrameworkHelper.switchMyPositionMode()
|
||||
@@ -166,6 +195,8 @@ final class MapTemplateBuilder {
|
||||
button.image = UIImage(systemName: "plus")
|
||||
case .zoomOut:
|
||||
button.image = UIImage(systemName: "minus")
|
||||
case .myPositionMode:
|
||||
button.image = UIImage(systemName: "location")
|
||||
}
|
||||
// Remove code below once Apple has fixed its issue with the button background
|
||||
if #unavailable(iOS 26) {
|
||||
@@ -176,6 +207,8 @@ final class MapTemplateBuilder {
|
||||
button.focusedImage = UIImage(systemName: "plus.circle.fill")
|
||||
case .zoomOut:
|
||||
button.focusedImage = UIImage(systemName: "minus.circle.fill")
|
||||
case .myPositionMode:
|
||||
button.image = UIImage(systemName: "location.fill")
|
||||
}
|
||||
}
|
||||
return button
|
||||
|
||||
@@ -91,7 +91,8 @@ NSString * const kUDDidShowLongTapToShowSideButtonsToast = @"kUDDidShowLongTapTo
|
||||
case MWMMyPositionModeNotFollow:
|
||||
case MWMMyPositionModeNotFollowNoPosition: [locBtn setStyleNameAndApply: @"ButtonGetPosition"]; break;
|
||||
case MWMMyPositionModeFollow: [locBtn setStyleNameAndApply: @"ButtonFollow"]; break;
|
||||
case MWMMyPositionModeFollowAndRotate: [locBtn setStyleNameAndApply: @"ButtonFollowAndRotate"]; break;
|
||||
case MWMMyPositionModeFollowAndRotateCompass: [locBtn setStyleNameAndApply: @"ButtonFollowAndRotateCompass"]; break;
|
||||
case MWMMyPositionModeFollowAndRotateRoute: [locBtn setStyleNameAndApply: @"ButtonFollowAndRotateRoute"]; break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -632,7 +632,8 @@ NSString *const kAboutSegue = @"Map2About";
|
||||
case MWMMyPositionModeNotFollow:
|
||||
break;
|
||||
case MWMMyPositionModeFollow:
|
||||
case MWMMyPositionModeFollowAndRotate:
|
||||
case MWMMyPositionModeFollowAndRotateCompass:
|
||||
case MWMMyPositionModeFollowAndRotateRoute:
|
||||
self.disableStandbyOnLocationStateMode = YES;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,8 @@ static inline MWMMyPositionMode mwmMyPositionMode(location::EMyPositionMode mode
|
||||
case location::EMyPositionMode::NotFollowNoPosition: return MWMMyPositionModeNotFollowNoPosition;
|
||||
case location::EMyPositionMode::NotFollow: return MWMMyPositionModeNotFollow;
|
||||
case location::EMyPositionMode::Follow: return MWMMyPositionModeFollow;
|
||||
case location::EMyPositionMode::FollowAndRotate: return MWMMyPositionModeFollowAndRotate;
|
||||
case location::EMyPositionMode::FollowAndRotateCompass: return MWMMyPositionModeFollowAndRotateCompass;
|
||||
case location::EMyPositionMode::FollowAndRotateRoute: return MWMMyPositionModeFollowAndRotateRoute;
|
||||
}
|
||||
}
|
||||
} // namespace location_helpers
|
||||
|
||||
@@ -50,7 +50,8 @@ std::string DebugPrint(MWMMyPositionMode mode) {
|
||||
case MWMMyPositionModeNotFollowNoPosition: return "MWMMyPositionModeNotFollowNoPosition";
|
||||
case MWMMyPositionModeNotFollow: return "MWMMyPositionModeNotFollow";
|
||||
case MWMMyPositionModeFollow: return "MWMMyPositionModeFollow";
|
||||
case MWMMyPositionModeFollowAndRotate: return "MWMMyPositionModeFollowAndRotate";
|
||||
case MWMMyPositionModeFollowAndRotateCompass: return "MWMMyPositionModeFollowAndRotateCompass";
|
||||
case MWMMyPositionModeFollowAndRotateRoute: return "MWMMyPositionModeFollowAndRotateRoute";
|
||||
}
|
||||
CHECK(false, ("Unsupported value", static_cast<int>(mode)));
|
||||
}
|
||||
@@ -367,7 +368,8 @@ void setShowLocationAlert(BOOL needShow) {
|
||||
case MWMMyPositionModeNotFollowNoPosition:
|
||||
case MWMMyPositionModeNotFollow: manager.geoMode = GeoMode::NotInPosition; break;
|
||||
case MWMMyPositionModeFollow: manager.geoMode = GeoMode::InPosition; break;
|
||||
case MWMMyPositionModeFollowAndRotate: manager.geoMode = GeoMode::FollowAndRotate; break;
|
||||
case MWMMyPositionModeFollowAndRotateCompass:
|
||||
case MWMMyPositionModeFollowAndRotateRoute: manager.geoMode = GeoMode::FollowAndRotate; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ NSUInteger constexpr kMaxPredictionCount = 20;
|
||||
|
||||
- (void)setMyPositionMode:(MWMMyPositionMode)mode
|
||||
{
|
||||
self.isLastPositionModeValid = (mode == MWMMyPositionModeFollowAndRotate);
|
||||
self.isLastPositionModeValid = (mode == MWMMyPositionModeFollowAndRotateCompass);
|
||||
[self restart];
|
||||
}
|
||||
|
||||
|
||||
@@ -3,5 +3,6 @@ typedef NS_CLOSED_ENUM(NSUInteger, MWMMyPositionMode) {
|
||||
MWMMyPositionModeNotFollowNoPosition,
|
||||
MWMMyPositionModeNotFollow,
|
||||
MWMMyPositionModeFollow,
|
||||
MWMMyPositionModeFollowAndRotate
|
||||
MWMMyPositionModeFollowAndRotateCompass,
|
||||
MWMMyPositionModeFollowAndRotateRoute
|
||||
};
|
||||
|
||||
@@ -41,13 +41,14 @@ final class ThemeManager: NSObject {
|
||||
}
|
||||
}(actualTheme)
|
||||
|
||||
if Settings.mapAppearance == .light {
|
||||
let isCarPlayActive = CarPlayService.shared.isCarplayActivated
|
||||
if !isCarPlayActive, Settings.mapAppearance == .light {
|
||||
if actualTheme == .vehicleDay || actualTheme == .vehicleNight {
|
||||
FrameworkHelper.setTheme(.vehicleDay)
|
||||
} else {
|
||||
FrameworkHelper.setTheme(.day)
|
||||
}
|
||||
} else if Settings.mapAppearance == .dark {
|
||||
} else if !isCarPlayActive, Settings.mapAppearance == .dark {
|
||||
if actualTheme == .vehicleDay || actualTheme == .vehicleNight {
|
||||
FrameworkHelper.setTheme(.vehicleNight)
|
||||
} else {
|
||||
|
||||
@@ -7,7 +7,8 @@ enum MapStyleSheet: String, CaseIterable {
|
||||
case mapButtonPending = "ButtonPending"
|
||||
case mapButtonGetPosition = "ButtonGetPosition"
|
||||
case mapButtonFollow = "ButtonFollow"
|
||||
case mapButtonFollowAndRotate = "ButtonFollowAndRotate"
|
||||
case mapButtonFollowAndRotateCompass = "ButtonFollowAndRotateCompass"
|
||||
case mapButtonFollowAndRotateRoute = "ButtonFollowAndRotateRoute"
|
||||
case mapButtonMapBookmarks = "ButtonMapBookmarks"
|
||||
case mapPromoDiscoveryButton = "PromoDiscroveryButton"
|
||||
case mapButtonBookmarksBack = "ButtonBookmarksBack"
|
||||
@@ -67,10 +68,14 @@ extension MapStyleSheet: IStyleSheet {
|
||||
return .add { s in
|
||||
s.mwmImage = "btn_follow"
|
||||
}
|
||||
case .mapButtonFollowAndRotate:
|
||||
case .mapButtonFollowAndRotateCompass:
|
||||
return .add { s in
|
||||
s.mwmImage = "btn_follow_and_rotate"
|
||||
}
|
||||
case .mapButtonFollowAndRotateRoute:
|
||||
return .add { s in
|
||||
s.mwmImage = "btn_follow_route"
|
||||
}
|
||||
case .mapButtonMapBookmarks:
|
||||
return .add { s in
|
||||
s.mwmImage = "ic_routing_bookmark"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
21
iphone/Maps/Images.xcassets/Location/btn_follow_route_dark.imageset/Contents.json
vendored
Normal file
21
iphone/Maps/Images.xcassets/Location/btn_follow_route_dark.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "btn_follow_route_dark.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
iphone/Maps/Images.xcassets/Location/btn_follow_route_dark.imageset/btn_follow_route_dark.png
vendored
Normal file
BIN
iphone/Maps/Images.xcassets/Location/btn_follow_route_dark.imageset/btn_follow_route_dark.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
21
iphone/Maps/Images.xcassets/Location/btn_follow_route_highlighted_dark.imageset/Contents.json
vendored
Normal file
21
iphone/Maps/Images.xcassets/Location/btn_follow_route_highlighted_dark.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "btn_follow_route_highlighted_dark.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
21
iphone/Maps/Images.xcassets/Location/btn_follow_route_highlighted_light.imageset/Contents.json
vendored
Normal file
21
iphone/Maps/Images.xcassets/Location/btn_follow_route_highlighted_light.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "btn_follow_route_highlighted_light.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
21
iphone/Maps/Images.xcassets/Location/btn_follow_route_light.imageset/Contents.json
vendored
Normal file
21
iphone/Maps/Images.xcassets/Location/btn_follow_route_light.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "btn_follow_route_light.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
iphone/Maps/Images.xcassets/Location/btn_follow_route_light.imageset/btn_follow_route_light.png
vendored
Normal file
BIN
iphone/Maps/Images.xcassets/Location/btn_follow_route_light.imageset/btn_follow_route_light.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
@@ -186,7 +186,7 @@ import AVFoundation
|
||||
return mapAppearance
|
||||
}
|
||||
|
||||
return .auto
|
||||
return .light
|
||||
}
|
||||
set {
|
||||
UserDefaults.standard.set(newValue.rawValue, forKey: userDefaultsKeyMapAppearance)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "drape/support_manager.hpp"
|
||||
|
||||
#include "platform/settings.hpp"
|
||||
#include "routing/base/followed_polyline.hpp"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
@@ -40,7 +41,7 @@ DrapeEngine::DrapeEngine(Params && params)
|
||||
|
||||
using namespace location;
|
||||
EMyPositionMode mode = PendingPosition;
|
||||
if (settings::Get(kLocationStateMode, mode) && mode == FollowAndRotate)
|
||||
if (settings::Get(kLocationStateMode, mode) && mode == FollowAndRotateCompass)
|
||||
{
|
||||
// If the screen rect setting in follow and rotate mode is missing or invalid, it could cause
|
||||
// invalid animations, so the follow and rotate mode should be discarded.
|
||||
@@ -441,13 +442,12 @@ void DrapeEngine::SetCompassInfo(location::CompassInfo const & info)
|
||||
MessagePriority::Normal);
|
||||
}
|
||||
|
||||
void DrapeEngine::SetGpsInfo(location::GpsInfo const & info, bool isNavigable, double distToNextTurn, double speedLimit,
|
||||
void DrapeEngine::SetGpsInfo(location::GpsInfo const & info, df::NavigationContext const & navigationContext,
|
||||
location::RouteMatchingInfo const & routeInfo)
|
||||
{
|
||||
m_threadCommutator->PostMessage(
|
||||
ThreadsCommutator::RenderThread,
|
||||
make_unique_dp<GpsInfoMessage>(info, isNavigable, distToNextTurn, speedLimit, routeInfo),
|
||||
MessagePriority::Normal);
|
||||
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread,
|
||||
make_unique_dp<GpsInfoMessage>(info, navigationContext, routeInfo),
|
||||
MessagePriority::Normal);
|
||||
}
|
||||
|
||||
void DrapeEngine::SwitchMyPositionNextMode()
|
||||
@@ -474,12 +474,13 @@ void DrapeEngine::StopLocationFollow()
|
||||
MessagePriority::Normal);
|
||||
}
|
||||
|
||||
void DrapeEngine::FollowRoute(int preferredZoomLevel, int preferredZoomLevel3d, bool enableAutoZoom, bool isArrowGlued)
|
||||
void DrapeEngine::FollowRoute(int preferredZoomLevel, int preferredZoomLevel3d, bool enableAutoZoom, bool isArrowGlued,
|
||||
bool allowRouteRotation)
|
||||
{
|
||||
m_threadCommutator->PostMessage(
|
||||
ThreadsCommutator::RenderThread,
|
||||
make_unique_dp<FollowRouteMessage>(preferredZoomLevel, preferredZoomLevel3d, enableAutoZoom, isArrowGlued),
|
||||
MessagePriority::Normal);
|
||||
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread,
|
||||
make_unique_dp<FollowRouteMessage>(preferredZoomLevel, preferredZoomLevel3d,
|
||||
enableAutoZoom, isArrowGlued, allowRouteRotation),
|
||||
MessagePriority::Normal);
|
||||
}
|
||||
|
||||
void DrapeEngine::SetModelViewListener(ModelViewChangedHandler && fn)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "drape_frontend/my_position_controller.hpp"
|
||||
#include "routing/base/followed_polyline.hpp"
|
||||
#include "traffic/traffic_info.hpp"
|
||||
|
||||
#include "drape_frontend/backend_renderer.hpp"
|
||||
@@ -154,7 +156,7 @@ public:
|
||||
void UpdateMapStyle();
|
||||
|
||||
void SetCompassInfo(location::CompassInfo const & info);
|
||||
void SetGpsInfo(location::GpsInfo const & info, bool isNavigable, double distToNextTurn, double speedLimit,
|
||||
void SetGpsInfo(location::GpsInfo const & info, df::NavigationContext const & navigationContext,
|
||||
location::RouteMatchingInfo const & routeInfo);
|
||||
void SwitchMyPositionNextMode();
|
||||
void LoseLocation();
|
||||
@@ -171,7 +173,8 @@ public:
|
||||
|
||||
dp::DrapeID AddSubroute(SubrouteConstPtr subroute);
|
||||
void RemoveSubroute(dp::DrapeID subrouteId, bool deactivateFollowing);
|
||||
void FollowRoute(int preferredZoomLevel, int preferredZoomLevel3d, bool enableAutoZoom, bool isArrowGlued);
|
||||
void FollowRoute(int preferredZoomLevel, int preferredZoomLevel3d, bool enableAutoZoom, bool isArrowGlued,
|
||||
bool allowRouteRotation);
|
||||
void DeactivateRouteFollowing();
|
||||
void SetSubrouteVisibility(dp::DrapeID subrouteId, bool isVisible);
|
||||
dp::DrapeID AddRoutePreviewSegment(m2::PointD const & startPt, m2::PointD const & finishPt);
|
||||
|
||||
@@ -438,8 +438,8 @@ void FrontendRenderer::AcceptMessage(ref_ptr<Message> message)
|
||||
break;
|
||||
#endif
|
||||
ref_ptr<GpsInfoMessage> msg = message;
|
||||
m_myPositionController->OnLocationUpdate(msg->GetInfo(), msg->IsNavigable(), msg->GetDistanceToNextTurn(),
|
||||
msg->GetSpeedLimit(), m_userEventStream.GetCurrentScreen());
|
||||
m_myPositionController->OnLocationUpdate(msg->GetInfo(), msg->GetNavigationContext(),
|
||||
m_userEventStream.GetCurrentScreen());
|
||||
|
||||
location::RouteMatchingInfo const & info = msg->GetRouteInfo();
|
||||
if (info.HasDistanceFromBegin())
|
||||
@@ -512,7 +512,8 @@ void FrontendRenderer::AcceptMessage(ref_ptr<Message> message)
|
||||
if (m_pendingFollowRoute != nullptr)
|
||||
{
|
||||
FollowRoute(m_pendingFollowRoute->m_preferredZoomLevel, m_pendingFollowRoute->m_preferredZoomLevelIn3d,
|
||||
m_pendingFollowRoute->m_enableAutoZoom, m_pendingFollowRoute->m_isArrowGlued);
|
||||
m_pendingFollowRoute->m_enableAutoZoom, m_pendingFollowRoute->m_isArrowGlued,
|
||||
m_pendingFollowRoute->m_allowRouteRotation);
|
||||
m_pendingFollowRoute.reset();
|
||||
}
|
||||
break;
|
||||
@@ -584,13 +585,14 @@ void FrontendRenderer::AcceptMessage(ref_ptr<Message> message)
|
||||
// receive FollowRoute message before FlushSubroute message, so we need to postpone its processing.
|
||||
if (m_routeRenderer->GetSubroutes().empty())
|
||||
{
|
||||
m_pendingFollowRoute = std::make_unique<FollowRouteData>(
|
||||
msg->GetPreferredZoomLevel(), msg->GetPreferredZoomLevelIn3d(), msg->EnableAutoZoom(), msg->IsArrowGlued());
|
||||
m_pendingFollowRoute =
|
||||
std::make_unique<FollowRouteData>(msg->GetPreferredZoomLevel(), msg->GetPreferredZoomLevelIn3d(),
|
||||
msg->EnableAutoZoom(), msg->IsArrowGlued(), msg->AllowRouteRotation());
|
||||
}
|
||||
else
|
||||
{
|
||||
FollowRoute(msg->GetPreferredZoomLevel(), msg->GetPreferredZoomLevelIn3d(), msg->EnableAutoZoom(),
|
||||
msg->IsArrowGlued());
|
||||
msg->IsArrowGlued(), msg->AllowRouteRotation());
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1057,10 +1059,11 @@ void FrontendRenderer::UpdateContextDependentResources()
|
||||
}
|
||||
|
||||
void FrontendRenderer::FollowRoute(int preferredZoomLevel, int preferredZoomLevelIn3d, bool enableAutoZoom,
|
||||
bool isArrowGlued)
|
||||
bool isArrowGlued, bool allowRouteRotation)
|
||||
{
|
||||
m_myPositionController->ActivateRouting(
|
||||
!m_enablePerspectiveInNavigation ? preferredZoomLevel : preferredZoomLevelIn3d, enableAutoZoom, isArrowGlued);
|
||||
!m_enablePerspectiveInNavigation ? preferredZoomLevel : preferredZoomLevelIn3d, enableAutoZoom, isArrowGlued,
|
||||
allowRouteRotation);
|
||||
|
||||
if (m_enablePerspectiveInNavigation)
|
||||
AddUserEvent(make_unique_dp<SetAutoPerspectiveEvent>(true /* isAutoPerspective */));
|
||||
|
||||
@@ -261,7 +261,8 @@ private:
|
||||
using TRenderGroupRemovePredicate = std::function<bool(drape_ptr<RenderGroup> const &)>;
|
||||
void RemoveRenderGroupsLater(TRenderGroupRemovePredicate const & predicate);
|
||||
|
||||
void FollowRoute(int preferredZoomLevel, int preferredZoomLevelIn3d, bool enableAutoZoom, bool isArrowGlued);
|
||||
void FollowRoute(int preferredZoomLevel, int preferredZoomLevelIn3d, bool enableAutoZoom, bool isArrowGlued,
|
||||
bool allowRouteRotation);
|
||||
|
||||
bool CheckRouteRecaching(ref_ptr<BaseSubrouteData> subrouteData);
|
||||
|
||||
@@ -372,17 +373,20 @@ private:
|
||||
|
||||
struct FollowRouteData
|
||||
{
|
||||
FollowRouteData(int preferredZoomLevel, int preferredZoomLevelIn3d, bool enableAutoZoom, bool isArrowGlued)
|
||||
FollowRouteData(int preferredZoomLevel, int preferredZoomLevelIn3d, bool enableAutoZoom, bool isArrowGlued,
|
||||
bool allowRouteRotation)
|
||||
: m_preferredZoomLevel(preferredZoomLevel)
|
||||
, m_preferredZoomLevelIn3d(preferredZoomLevelIn3d)
|
||||
, m_enableAutoZoom(enableAutoZoom)
|
||||
, m_isArrowGlued(isArrowGlued)
|
||||
, m_allowRouteRotation(allowRouteRotation)
|
||||
{}
|
||||
|
||||
int m_preferredZoomLevel;
|
||||
int m_preferredZoomLevelIn3d;
|
||||
bool m_enableAutoZoom;
|
||||
bool m_isArrowGlued;
|
||||
bool m_allowRouteRotation;
|
||||
};
|
||||
|
||||
std::unique_ptr<FollowRouteData> m_pendingFollowRoute;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "drape_frontend/gui/skin.hpp"
|
||||
#include "drape_frontend/message.hpp"
|
||||
#include "drape_frontend/my_position.hpp"
|
||||
#include "drape_frontend/my_position_controller.hpp"
|
||||
#include "drape_frontend/overlay_batcher.hpp"
|
||||
#include "drape_frontend/postprocess_renderer.hpp"
|
||||
#include "drape_frontend/render_node.hpp"
|
||||
@@ -29,6 +30,7 @@
|
||||
|
||||
#include "geometry/rect2d.hpp"
|
||||
#include "geometry/triangle2d.hpp"
|
||||
#include "routing/base/followed_polyline.hpp"
|
||||
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
@@ -475,28 +477,22 @@ private:
|
||||
class GpsInfoMessage : public Message
|
||||
{
|
||||
public:
|
||||
GpsInfoMessage(location::GpsInfo const & info, bool isNavigable, double distToNextTurn, double speedLimit,
|
||||
GpsInfoMessage(location::GpsInfo const & info, df::NavigationContext const & navigationContext,
|
||||
location::RouteMatchingInfo const & routeInfo)
|
||||
: m_info(info)
|
||||
, m_isNavigable(isNavigable)
|
||||
, m_distToNextTurn(distToNextTurn)
|
||||
, m_speedLimit(speedLimit)
|
||||
, m_navigationContext(navigationContext)
|
||||
, m_routeInfo(routeInfo)
|
||||
{}
|
||||
|
||||
Type GetType() const override { return Type::GpsInfo; }
|
||||
|
||||
location::GpsInfo const & GetInfo() const { return m_info; }
|
||||
bool IsNavigable() const { return m_isNavigable; }
|
||||
double const & GetSpeedLimit() const { return m_speedLimit; }
|
||||
double const & GetDistanceToNextTurn() const { return m_distToNextTurn; }
|
||||
df::NavigationContext const & GetNavigationContext() const { return m_navigationContext; }
|
||||
location::RouteMatchingInfo const & GetRouteInfo() const { return m_routeInfo; }
|
||||
|
||||
private:
|
||||
location::GpsInfo const m_info;
|
||||
bool const m_isNavigable;
|
||||
double const m_distToNextTurn;
|
||||
double const m_speedLimit;
|
||||
df::NavigationContext const m_navigationContext;
|
||||
location::RouteMatchingInfo const m_routeInfo;
|
||||
};
|
||||
|
||||
@@ -740,11 +736,13 @@ public:
|
||||
class FollowRouteMessage : public Message
|
||||
{
|
||||
public:
|
||||
FollowRouteMessage(int preferredZoomLevel, int preferredZoomLevelIn3d, bool enableAutoZoom, bool isArrowGlued)
|
||||
FollowRouteMessage(int preferredZoomLevel, int preferredZoomLevelIn3d, bool enableAutoZoom, bool isArrowGlued,
|
||||
bool allowRouteRotation)
|
||||
: m_preferredZoomLevel(preferredZoomLevel)
|
||||
, m_preferredZoomLevelIn3d(preferredZoomLevelIn3d)
|
||||
, m_enableAutoZoom(enableAutoZoom)
|
||||
, m_isArrowGlued(isArrowGlued)
|
||||
, m_allowRouteRotation(allowRouteRotation)
|
||||
{}
|
||||
|
||||
Type GetType() const override { return Type::FollowRoute; }
|
||||
@@ -753,12 +751,14 @@ public:
|
||||
int GetPreferredZoomLevelIn3d() const { return m_preferredZoomLevelIn3d; }
|
||||
bool EnableAutoZoom() const { return m_enableAutoZoom; }
|
||||
bool IsArrowGlued() const { return m_isArrowGlued; }
|
||||
bool AllowRouteRotation() const { return m_allowRouteRotation; }
|
||||
|
||||
private:
|
||||
int const m_preferredZoomLevel;
|
||||
int const m_preferredZoomLevelIn3d;
|
||||
bool const m_enableAutoZoom;
|
||||
bool const m_isArrowGlued;
|
||||
bool const m_allowRouteRotation;
|
||||
};
|
||||
|
||||
class SwitchMapStyleMessage : public BaseBlockingMessage
|
||||
|
||||
@@ -9,9 +9,11 @@
|
||||
|
||||
#include "geometry/mercator.hpp"
|
||||
|
||||
#include "platform/location.hpp"
|
||||
#include "platform/measurement_utils.hpp"
|
||||
|
||||
#include "base/math.hpp"
|
||||
#include "routing/base/followed_polyline.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
@@ -31,6 +33,9 @@ double constexpr kMaxTimeInBackgroundSec = 60.0 * 60 * 30; // 30 hours before s
|
||||
double constexpr kMaxNotFollowRoutingTimeSec = 20.0;
|
||||
double constexpr kMaxUpdateLocationInvervalSec = 30.0;
|
||||
double constexpr kMaxBlockAutoZoomTimeSec = 10.0;
|
||||
double constexpr kDefaultSpeedLimitKmpH = 50.0;
|
||||
double constexpr kLookaheadTimeSpeedRatio = 0.3;
|
||||
double constexpr kMaxLookaheadTimeSec = 25.0;
|
||||
|
||||
int constexpr kZoomThreshold = 10;
|
||||
int constexpr kMaxScaleZoomLevel = 16;
|
||||
@@ -66,7 +71,6 @@ double CalculateZoomByMaxSpeed(double speedMpS, bool isPerspectiveAllowed)
|
||||
|
||||
std::array<TSpeedScale, 2> const & scales = isPerspectiveAllowed ? scales3d : scales2d;
|
||||
|
||||
double constexpr kDefaultSpeedLimitKmpH = 50.0;
|
||||
double const speedKmpH = speedMpS > 0 ? measurement_utils::MpsToKmph(speedMpS) : kDefaultSpeedLimitKmpH;
|
||||
|
||||
size_t i = 0;
|
||||
@@ -143,7 +147,7 @@ void ResetNotification(uint64_t & notifyId)
|
||||
|
||||
bool IsModeChangeViewport(location::EMyPositionMode mode)
|
||||
{
|
||||
return mode == location::Follow || mode == location::FollowAndRotate;
|
||||
return mode == location::Follow || mode == location::FollowAndRotateCompass || mode == location::FollowAndRotateRoute;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -157,9 +161,11 @@ MyPositionController::MyPositionController(Params && params, ref_ptr<DrapeNotifi
|
||||
, m_errorRadius(0.0)
|
||||
, m_horizontalAccuracy(0.0)
|
||||
, m_position(m2::PointD::Zero())
|
||||
, m_drawDirection(0.0)
|
||||
, m_direction(0.0)
|
||||
, m_routeDirection(0.0)
|
||||
, m_arrowDirection(0.0)
|
||||
, m_oldPosition(m2::PointD::Zero())
|
||||
, m_oldDrawDirection(0.0)
|
||||
, m_oldArrowDirection(0.0)
|
||||
, m_enablePerspectiveInRouting(false)
|
||||
, m_enableAutoZoomInRouting(params.m_isAutozoomEnabled)
|
||||
, m_autoScale2d(GetScreenScale(kDefaultAutoZoom))
|
||||
@@ -171,7 +177,8 @@ MyPositionController::MyPositionController(Params && params, ref_ptr<DrapeNotifi
|
||||
, m_isDirtyAutoZoom(false)
|
||||
, m_isPendingAnimation(false)
|
||||
, m_isPositionAssigned(false)
|
||||
, m_isDirectionAssigned(false)
|
||||
, m_isArrowDirectionAssigned(false)
|
||||
, m_isRouteDirectionAssigned(false)
|
||||
, m_isCompassAvailable(false)
|
||||
, m_positionIsObsolete(false)
|
||||
, m_needBlockAutoZoom(false)
|
||||
@@ -290,7 +297,7 @@ void MyPositionController::ScaleEnded()
|
||||
|
||||
void MyPositionController::Rotated()
|
||||
{
|
||||
if (m_mode == location::FollowAndRotate)
|
||||
if (m_mode == location::FollowAndRotateCompass)
|
||||
m_wasRotationInScaling = true;
|
||||
}
|
||||
|
||||
@@ -398,25 +405,32 @@ void MyPositionController::NextMode(ScreenBase const & screen)
|
||||
// In routing not-follow -> follow-and-rotate, otherwise not-follow -> follow.
|
||||
if (m_mode == location::NotFollow)
|
||||
{
|
||||
ChangeMode(m_isInRouting ? location::FollowAndRotate : location::Follow);
|
||||
ChangeMode(m_isInRouting ? location::FollowAndRotateCompass : location::Follow);
|
||||
UpdateViewport(preferredZoomLevel);
|
||||
return;
|
||||
}
|
||||
|
||||
// From follow mode we transit to follow-and-rotate if compass is available or
|
||||
// routing is enabled.
|
||||
// From follow mode we transit to follow-and-rotate-compass, if in routing
|
||||
if (m_mode == location::Follow)
|
||||
{
|
||||
if (IsRotationAvailable() || m_isInRouting)
|
||||
if (IsArrowRotationAvailable() || m_isInRouting)
|
||||
{
|
||||
ChangeMode(location::FollowAndRotate);
|
||||
ChangeMode(location::FollowAndRotateCompass);
|
||||
UpdateViewport(preferredZoomLevel);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// From follow-and-rotate mode we can transit to follow mode.
|
||||
if (m_mode == location::FollowAndRotate)
|
||||
// From -rotate-compass mode we transit to -rotate-route mode, if allowed
|
||||
if (m_mode == location::FollowAndRotateCompass && IsRouteRotationAvailable() && m_allowRouteRotationInRouting)
|
||||
{
|
||||
ChangeMode(location::FollowAndRotateRoute);
|
||||
UpdateViewport(preferredZoomLevel);
|
||||
return;
|
||||
}
|
||||
|
||||
// From -rotate-route mode, or from -rotate-compass if -rotate-route is not allowed, we return to follow mode
|
||||
if (m_mode == location::FollowAndRotateRoute || m_mode == location::FollowAndRotateCompass)
|
||||
{
|
||||
if (m_isInRouting && screen.isPerspective())
|
||||
preferredZoomLevel = static_cast<int>(GetZoomLevel(ScreenBase::GetStartPerspectiveScale() * 1.1));
|
||||
@@ -425,8 +439,8 @@ void MyPositionController::NextMode(ScreenBase const & screen)
|
||||
}
|
||||
}
|
||||
|
||||
void MyPositionController::OnLocationUpdate(location::GpsInfo const & info, bool isNavigable, double distanceToNextTurn,
|
||||
double speedLimit, ScreenBase const & screen)
|
||||
void MyPositionController::OnLocationUpdate(location::GpsInfo const & info,
|
||||
df::NavigationContext const & navigationContext, ScreenBase const & screen)
|
||||
{
|
||||
m2::PointD const oldPos = GetDrawablePosition();
|
||||
double const oldAzimut = GetDrawableAzimut();
|
||||
@@ -438,33 +452,62 @@ void MyPositionController::OnLocationUpdate(location::GpsInfo const & info, bool
|
||||
m_errorRadius = rect.SizeX() * 0.5;
|
||||
m_horizontalAccuracy = info.m_horizontalAccuracy;
|
||||
|
||||
if (distanceToNextTurn >= 0.0 || speedLimit >= 0.0)
|
||||
if (navigationContext.m_distanceToNextTurn >= 0.0 || navigationContext.m_speedLimit >= 0.0)
|
||||
{
|
||||
double const mercatorPerMeter = m_errorRadius / info.m_horizontalAccuracy;
|
||||
m_autoScale2d =
|
||||
mercatorPerMeter * CalculateAutoZoom(speedLimit, distanceToNextTurn, false /* isPerspectiveAllowed */);
|
||||
mercatorPerMeter * CalculateAutoZoom(navigationContext.m_speedLimit, navigationContext.m_distanceToNextTurn,
|
||||
false /* isPerspectiveAllowed */);
|
||||
m_autoScale3d =
|
||||
mercatorPerMeter * CalculateAutoZoom(speedLimit, distanceToNextTurn, true /* isPerspectiveAllowed */);
|
||||
mercatorPerMeter * CalculateAutoZoom(navigationContext.m_speedLimit, navigationContext.m_distanceToNextTurn,
|
||||
true /* isPerspectiveAllowed */);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_autoScale2d = m_autoScale3d = kUnknownAutoZoom;
|
||||
}
|
||||
|
||||
// Sets direction based on GPS if:
|
||||
// Sets arrow direction based on GPS if:
|
||||
// 1. Compass is not available.
|
||||
// 2. Direction must be glued to the route during routing (route-corrected angle is set only in
|
||||
// OnLocationUpdate(): in OnCompassUpdate() the angle always has the original value.
|
||||
// 3. Device is moving faster then pedestrian.
|
||||
bool const isMovingFast = info.HasSpeed() && info.m_speed > kMinSpeedThresholdMps;
|
||||
bool const glueArrowInRouting = isNavigable && m_isArrowGluedInRouting;
|
||||
bool const glueArrowInRouting = navigationContext.m_isNavigable && m_isArrowGluedInRouting;
|
||||
|
||||
// Calculate the route direction by looking ahead a distance in the route
|
||||
// depending on the current speed
|
||||
if (glueArrowInRouting && navigationContext.m_followedPolyline != nullptr)
|
||||
{
|
||||
double const speed =
|
||||
(navigationContext.m_speedLimit > 0 ? measurement_utils::MpsToKmph(navigationContext.m_speedLimit)
|
||||
: kDefaultSpeedLimitKmpH);
|
||||
double const lookaheadTimeSec = std::min(speed * kLookaheadTimeSpeedRatio, kMaxLookaheadTimeSec);
|
||||
double const lookaheadDistance = std::min(speed * lookaheadTimeSec, navigationContext.m_distanceToNextTurn);
|
||||
|
||||
auto const & iter = navigationContext.m_followedPolyline->GetCurrentIter();
|
||||
m2::PointD const point = navigationContext.m_followedPolyline->GetLookaheadPoint(lookaheadDistance);
|
||||
|
||||
auto angle = math::RadToDeg(ang::AngleTo(iter.m_pt, point));
|
||||
if (std::isnan(angle) || std::isinf(angle))
|
||||
{
|
||||
// fallback in case the current route becomes invalid for any reason
|
||||
SetRouteDirection(info.m_bearing);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetRouteDirection(math::DegToRad(location::AngleToBearing(angle)));
|
||||
}
|
||||
}
|
||||
|
||||
if ((!m_isCompassAvailable || glueArrowInRouting || isMovingFast) && info.HasBearing())
|
||||
{
|
||||
SetDirection(math::DegToRad(info.m_bearing));
|
||||
SetArrowDirection(math::DegToRad(info.m_bearing));
|
||||
m_lastGPSBearingTimer.Reset();
|
||||
}
|
||||
|
||||
m_direction = m_mode == location::FollowAndRotateRoute ? m_routeDirection : m_arrowDirection;
|
||||
|
||||
if (m_isPositionAssigned && (!AlmostCurrentPosition(oldPos) || !AlmostCurrentAzimut(oldAzimut)))
|
||||
{
|
||||
CreateAnim(oldPos, oldAzimut, screen);
|
||||
@@ -487,9 +530,9 @@ void MyPositionController::OnLocationUpdate(location::GpsInfo const & info, bool
|
||||
{
|
||||
ChangeModelView(m_position, kDoNotChangeZoom);
|
||||
}
|
||||
else if (m_mode == location::FollowAndRotate)
|
||||
else if (m_mode == location::FollowAndRotateCompass || m_mode == location::FollowAndRotateRoute)
|
||||
{
|
||||
ChangeModelView(m_position, m_drawDirection,
|
||||
ChangeModelView(m_position, m_direction,
|
||||
m_isInRouting ? GetRoutingRotationPixelCenter() : m_visiblePixelRect.Center(),
|
||||
kDoNotChangeZoom);
|
||||
}
|
||||
@@ -499,7 +542,7 @@ void MyPositionController::OnLocationUpdate(location::GpsInfo const & info, bool
|
||||
{
|
||||
if (m_isInRouting)
|
||||
{
|
||||
ChangeMode(location::FollowAndRotate);
|
||||
ChangeMode(location::FollowAndRotateCompass);
|
||||
UpdateViewport(kMaxScaleZoomLevel);
|
||||
}
|
||||
else
|
||||
@@ -525,7 +568,7 @@ void MyPositionController::OnLocationUpdate(location::GpsInfo const & info, bool
|
||||
{
|
||||
if (m_isInRouting)
|
||||
{
|
||||
ChangeMode(location::FollowAndRotate);
|
||||
ChangeMode(location::FollowAndRotateCompass);
|
||||
UpdateViewport(kMaxScaleZoomLevel);
|
||||
}
|
||||
else
|
||||
@@ -552,7 +595,8 @@ void MyPositionController::LoseLocation()
|
||||
{
|
||||
if (m_mode == location::NotFollowNoPosition)
|
||||
return;
|
||||
else if (m_mode == location::Follow || m_mode == location::FollowAndRotate)
|
||||
else if (m_mode == location::Follow || m_mode == location::FollowAndRotateCompass ||
|
||||
m_mode == location::FollowAndRotateRoute)
|
||||
ChangeMode(location::PendingPosition);
|
||||
else
|
||||
ChangeMode(location::NotFollowNoPosition);
|
||||
@@ -570,10 +614,11 @@ void MyPositionController::OnCompassUpdate(location::CompassInfo const & info, S
|
||||
if ((IsInRouting() && m_isArrowGluedInRouting) || existsFreshGpsBearing)
|
||||
return;
|
||||
|
||||
SetDirection(info.m_bearing);
|
||||
SetArrowDirection(info.m_bearing);
|
||||
|
||||
if (m_isPositionAssigned && !AlmostCurrentAzimut(oldAzimut) && m_mode == location::FollowAndRotate)
|
||||
if (m_isPositionAssigned && !AlmostCurrentAzimut(oldAzimut) && m_mode == location::FollowAndRotateCompass)
|
||||
{
|
||||
m_direction = info.m_bearing;
|
||||
CreateAnim(GetDrawablePosition(), oldAzimut, screen);
|
||||
m_isDirtyViewport = true;
|
||||
}
|
||||
@@ -582,10 +627,10 @@ void MyPositionController::OnCompassUpdate(location::CompassInfo const & info, S
|
||||
bool MyPositionController::UpdateViewportWithAutoZoom()
|
||||
{
|
||||
double const autoScale = m_enablePerspectiveInRouting ? m_autoScale3d : m_autoScale2d;
|
||||
if (autoScale > 0.0 && m_mode == location::FollowAndRotate && m_isInRouting && m_enableAutoZoomInRouting &&
|
||||
!m_needBlockAutoZoom)
|
||||
if (autoScale > 0.0 && (m_mode == location::FollowAndRotateCompass || m_mode == location::FollowAndRotateRoute) &&
|
||||
m_isInRouting && m_enableAutoZoomInRouting && !m_needBlockAutoZoom)
|
||||
{
|
||||
ChangeModelView(autoScale, m_position, m_drawDirection, GetRoutingRotationPixelCenter());
|
||||
ChangeModelView(autoScale, m_position, m_direction, GetRoutingRotationPixelCenter());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -617,7 +662,7 @@ void MyPositionController::Render(ref_ptr<dp::GraphicsContext> context, ref_ptr<
|
||||
m_shape->SetPositionObsolete(m_positionIsObsolete);
|
||||
m_shape->SetPosition(m2::PointF(GetDrawablePosition()));
|
||||
m_shape->SetAzimuth(static_cast<float>(GetDrawableAzimut()));
|
||||
m_shape->SetIsValidAzimuth(IsRotationAvailable());
|
||||
m_shape->SetIsValidAzimuth(IsArrowRotationAvailable());
|
||||
m_shape->SetAccuracy(static_cast<float>(m_errorRadius));
|
||||
m_shape->SetRoutingMode(IsInRouting());
|
||||
|
||||
@@ -631,7 +676,7 @@ void MyPositionController::Render(ref_ptr<dp::GraphicsContext> context, ref_ptr<
|
||||
|
||||
bool MyPositionController::IsRouteFollowingActive() const
|
||||
{
|
||||
return IsInRouting() && m_mode == location::FollowAndRotate;
|
||||
return IsInRouting() && m_mode == location::FollowAndRotateCompass;
|
||||
}
|
||||
|
||||
bool MyPositionController::AlmostCurrentPosition(m2::PointD const & pos) const
|
||||
@@ -643,18 +688,25 @@ bool MyPositionController::AlmostCurrentPosition(m2::PointD const & pos) const
|
||||
bool MyPositionController::AlmostCurrentAzimut(double azimut) const
|
||||
{
|
||||
double constexpr kDirectionEqualityDelta = 1e-3;
|
||||
return AlmostEqualAbs(azimut, m_drawDirection, kDirectionEqualityDelta);
|
||||
return AlmostEqualAbs(azimut, m_direction, kDirectionEqualityDelta);
|
||||
}
|
||||
|
||||
void MyPositionController::SetDirection(double bearing)
|
||||
void MyPositionController::SetRouteDirection(double bearing)
|
||||
{
|
||||
m_drawDirection = bearing;
|
||||
m_isDirectionAssigned = true;
|
||||
m_routeDirection = bearing;
|
||||
m_isRouteDirectionAssigned = true;
|
||||
}
|
||||
|
||||
void MyPositionController::SetArrowDirection(double bearing)
|
||||
{
|
||||
m_arrowDirection = bearing;
|
||||
m_isArrowDirectionAssigned = true;
|
||||
}
|
||||
|
||||
void MyPositionController::ChangeMode(location::EMyPositionMode newMode)
|
||||
{
|
||||
if (m_isInRouting && (m_mode != newMode) && (newMode == location::FollowAndRotate))
|
||||
if (m_isInRouting && (m_mode != newMode) &&
|
||||
(newMode == location::FollowAndRotateCompass || newMode == location::FollowAndRotateRoute))
|
||||
ResetBlockAutoZoomTimer();
|
||||
|
||||
m_mode = newMode;
|
||||
@@ -675,7 +727,8 @@ bool MyPositionController::IsWaitingForLocation() const
|
||||
|
||||
void MyPositionController::StopLocationFollow()
|
||||
{
|
||||
if (m_mode == location::Follow || m_mode == location::FollowAndRotate)
|
||||
if (m_mode == location::Follow || m_mode == location::FollowAndRotateCompass ||
|
||||
m_mode == location::FollowAndRotateRoute)
|
||||
ChangeMode(location::NotFollow);
|
||||
m_desiredInitMode = location::NotFollow;
|
||||
|
||||
@@ -690,7 +743,7 @@ void MyPositionController::OnEnterForeground(double backgroundTime)
|
||||
// When location was active during previous session the app will try to follow the user.
|
||||
if (m_mode == location::NotFollow)
|
||||
{
|
||||
ChangeMode(m_isInRouting ? location::FollowAndRotate : location::Follow);
|
||||
ChangeMode(m_isInRouting ? location::FollowAndRotateCompass : location::Follow);
|
||||
UpdateViewport(kDoNotChangeZoom);
|
||||
}
|
||||
|
||||
@@ -706,7 +759,7 @@ void MyPositionController::OnEnterBackground() {}
|
||||
|
||||
void MyPositionController::OnCompassTapped()
|
||||
{
|
||||
if (m_mode == location::FollowAndRotate)
|
||||
if (m_mode == location::FollowAndRotateCompass)
|
||||
{
|
||||
ChangeMode(location::Follow);
|
||||
ChangeModelView(m_position, 0.0, m_visiblePixelRect.Center(), kDoNotChangeZoom);
|
||||
@@ -763,9 +816,9 @@ void MyPositionController::UpdateViewport(int zoomLevel)
|
||||
{
|
||||
ChangeModelView(m_position, zoomLevel);
|
||||
}
|
||||
else if (m_mode == location::FollowAndRotate)
|
||||
else if (m_mode == location::FollowAndRotateCompass || m_mode == location::FollowAndRotateRoute)
|
||||
{
|
||||
ChangeModelView(m_position, m_drawDirection,
|
||||
ChangeModelView(m_position, m_direction,
|
||||
m_isInRouting ? GetRoutingRotationPixelCenter() : m_visiblePixelRect.Center(), zoomLevel);
|
||||
}
|
||||
}
|
||||
@@ -775,7 +828,7 @@ m2::PointD MyPositionController::GetRotationPixelCenter() const
|
||||
if (m_mode == location::Follow)
|
||||
return m_visiblePixelRect.Center();
|
||||
|
||||
if (m_mode == location::FollowAndRotate)
|
||||
if (m_mode == location::FollowAndRotateCompass || m_mode == location::FollowAndRotateRoute)
|
||||
return m_isInRouting ? GetRoutingRotationPixelCenter() : m_visiblePixelRect.Center();
|
||||
|
||||
return m2::PointD::Zero();
|
||||
@@ -817,15 +870,15 @@ double MyPositionController::GetDrawableAzimut()
|
||||
}
|
||||
|
||||
if (m_isPendingAnimation)
|
||||
return m_oldDrawDirection;
|
||||
return m_oldArrowDirection;
|
||||
|
||||
return m_drawDirection;
|
||||
return m_arrowDirection;
|
||||
}
|
||||
|
||||
void MyPositionController::CreateAnim(m2::PointD const & oldPos, double oldAzimut, ScreenBase const & screen)
|
||||
{
|
||||
double const moveDuration = PositionInterpolator::GetMoveDuration(oldPos, m_position, screen);
|
||||
double const rotateDuration = AngleInterpolator::GetRotateDuration(oldAzimut, m_drawDirection);
|
||||
double const rotateDuration = AngleInterpolator::GetRotateDuration(oldAzimut, m_arrowDirection);
|
||||
if (df::IsAnimationAllowed(std::max(moveDuration, rotateDuration), screen))
|
||||
{
|
||||
if (IsModeChangeViewport())
|
||||
@@ -834,7 +887,7 @@ void MyPositionController::CreateAnim(m2::PointD const & oldPos, double oldAzimu
|
||||
{
|
||||
drape_ptr<Animation> anim = make_unique_dp<ArrowAnimation>(
|
||||
GetDrawablePosition(), m_position, syncAnim == nullptr ? moveDuration : syncAnim->GetDuration(),
|
||||
GetDrawableAzimut(), m_drawDirection);
|
||||
GetDrawableAzimut(), m_arrowDirection);
|
||||
if (syncAnim != nullptr)
|
||||
{
|
||||
anim->SetMaxDuration(syncAnim->GetMaxDuration());
|
||||
@@ -843,13 +896,13 @@ void MyPositionController::CreateAnim(m2::PointD const & oldPos, double oldAzimu
|
||||
return anim;
|
||||
};
|
||||
m_oldPosition = oldPos;
|
||||
m_oldDrawDirection = oldAzimut;
|
||||
m_oldArrowDirection = oldAzimut;
|
||||
m_isPendingAnimation = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
AnimationSystem::Instance().CombineAnimation(
|
||||
make_unique_dp<ArrowAnimation>(oldPos, m_position, moveDuration, oldAzimut, m_drawDirection));
|
||||
make_unique_dp<ArrowAnimation>(oldPos, m_position, moveDuration, oldAzimut, m_arrowDirection));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -868,16 +921,18 @@ void MyPositionController::EnableAutoZoomInRouting(bool enableAutoZoom)
|
||||
}
|
||||
}
|
||||
|
||||
void MyPositionController::ActivateRouting(int zoomLevel, bool enableAutoZoom, bool isArrowGlued)
|
||||
void MyPositionController::ActivateRouting(int zoomLevel, bool enableAutoZoom, bool isArrowGlued,
|
||||
bool allowRouteRotation)
|
||||
{
|
||||
if (!m_isInRouting)
|
||||
{
|
||||
m_isInRouting = true;
|
||||
m_isArrowGluedInRouting = isArrowGlued;
|
||||
m_enableAutoZoomInRouting = enableAutoZoom;
|
||||
m_allowRouteRotationInRouting = allowRouteRotation;
|
||||
|
||||
ChangeMode(location::FollowAndRotate);
|
||||
ChangeModelView(m_position, m_isDirectionAssigned ? m_drawDirection : 0.0, GetRoutingRotationPixelCenter(),
|
||||
ChangeMode(location::FollowAndRotateCompass);
|
||||
ChangeModelView(m_position, m_isRouteDirectionAssigned ? m_routeDirection : 0.0, GetRoutingRotationPixelCenter(),
|
||||
zoomLevel, [this](ref_ptr<Animation> anim) { UpdateViewport(kDoNotChangeZoom); });
|
||||
ResetRoutingNotFollowTimer();
|
||||
}
|
||||
@@ -889,8 +944,10 @@ void MyPositionController::DeactivateRouting()
|
||||
{
|
||||
m_isInRouting = false;
|
||||
m_isArrowGluedInRouting = false;
|
||||
m_allowRouteRotationInRouting = false;
|
||||
|
||||
m_isDirectionAssigned = m_isCompassAvailable && m_isDirectionAssigned;
|
||||
m_isArrowDirectionAssigned = m_isCompassAvailable && m_isArrowDirectionAssigned;
|
||||
m_isRouteDirectionAssigned = false;
|
||||
|
||||
ChangeMode(location::Follow);
|
||||
ChangeModelView(m_position, 0.0, m_visiblePixelRect.Center(), kDoNotChangeZoom);
|
||||
@@ -919,7 +976,7 @@ void MyPositionController::CheckNotFollowRouting()
|
||||
CHECK_ON_TIMEOUT(m_routingNotFollowNotifyId, kMaxNotFollowRoutingTimeSec, CheckNotFollowRouting);
|
||||
if (m_routingNotFollowTimer.ElapsedSeconds() >= kMaxNotFollowRoutingTimeSec)
|
||||
{
|
||||
ChangeMode(location::FollowAndRotate);
|
||||
ChangeMode(location::FollowAndRotateCompass);
|
||||
UpdateViewport(kDoNotChangeZoom);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "drape/pointers.hpp"
|
||||
|
||||
#include "routing/base/followed_polyline.hpp"
|
||||
#include "shaders/program_manager.hpp"
|
||||
|
||||
#include "platform/location.hpp"
|
||||
@@ -24,6 +25,24 @@ using TAnimationCreator = std::function<drape_ptr<Animation>(ref_ptr<Animation>)
|
||||
|
||||
class DrapeNotifier;
|
||||
|
||||
struct NavigationContext
|
||||
{
|
||||
bool m_isNavigable = false;
|
||||
double m_distanceToNextTurn = 0.0;
|
||||
double m_speedLimit = 0.0;
|
||||
routing::FollowedPolyline const * m_followedPolyline = nullptr;
|
||||
|
||||
NavigationContext() = default;
|
||||
|
||||
NavigationContext(bool navigable, double distanceToTurn, double speedLimit,
|
||||
routing::FollowedPolyline const & followedPolyline)
|
||||
: m_isNavigable(navigable)
|
||||
, m_distanceToNextTurn(distanceToTurn)
|
||||
, m_speedLimit(speedLimit)
|
||||
, m_followedPolyline(&followedPolyline)
|
||||
{}
|
||||
};
|
||||
|
||||
class MyPositionController
|
||||
{
|
||||
public:
|
||||
@@ -102,7 +121,7 @@ public:
|
||||
drape_ptr<MyPosition> && shape, Arrow3d::PreloadedData && preloadedData);
|
||||
void ResetRenderShape();
|
||||
|
||||
void ActivateRouting(int zoomLevel, bool enableAutoZoom, bool isArrowGlued);
|
||||
void ActivateRouting(int zoomLevel, bool enableAutoZoom, bool isArrowGlued, bool allowRouteRotation);
|
||||
void DeactivateRouting();
|
||||
|
||||
void EnablePerspectiveInRouting(bool enablePerspective);
|
||||
@@ -117,14 +136,15 @@ public:
|
||||
void OnEnterBackground();
|
||||
|
||||
void OnCompassTapped();
|
||||
void OnLocationUpdate(location::GpsInfo const & info, bool isNavigable, double distanceToNextTurn, double speedLimit,
|
||||
void OnLocationUpdate(location::GpsInfo const & info, df::NavigationContext const & navigationContext,
|
||||
ScreenBase const & screen);
|
||||
void OnCompassUpdate(location::CompassInfo const & info, ScreenBase const & screen);
|
||||
|
||||
void Render(ref_ptr<dp::GraphicsContext> context, ref_ptr<gpu::ProgramManager> mng, ScreenBase const & screen,
|
||||
int zoomLevel, FrameValues const & frameValues);
|
||||
|
||||
bool IsRotationAvailable() const { return m_isDirectionAssigned; }
|
||||
bool IsArrowRotationAvailable() const { return m_isArrowDirectionAssigned; }
|
||||
bool IsRouteRotationAvailable() const { return m_isRouteDirectionAssigned; }
|
||||
bool IsInRouting() const { return m_isInRouting; }
|
||||
bool IsRouteFollowingActive() const;
|
||||
bool IsModeChangeViewport() const;
|
||||
@@ -135,7 +155,8 @@ public:
|
||||
|
||||
private:
|
||||
void ChangeMode(location::EMyPositionMode newMode);
|
||||
void SetDirection(double bearing);
|
||||
void SetRouteDirection(double bearing);
|
||||
void SetArrowDirection(double bearing);
|
||||
|
||||
void ChangeModelView(m2::PointD const & center, int zoomLevel);
|
||||
void ChangeModelView(double azimuth);
|
||||
@@ -178,12 +199,15 @@ private:
|
||||
double m_errorRadius; // error radius in mercator.
|
||||
double m_horizontalAccuracy;
|
||||
m2::PointD m_position; // position in mercator.
|
||||
double m_drawDirection;
|
||||
double m_direction;
|
||||
double m_routeDirection;
|
||||
double m_arrowDirection;
|
||||
m2::PointD m_oldPosition; // position in mercator.
|
||||
double m_oldDrawDirection;
|
||||
double m_oldArrowDirection;
|
||||
|
||||
bool m_enablePerspectiveInRouting;
|
||||
bool m_enableAutoZoomInRouting;
|
||||
bool m_allowRouteRotationInRouting;
|
||||
double m_autoScale2d;
|
||||
double m_autoScale3d;
|
||||
|
||||
@@ -205,7 +229,8 @@ private:
|
||||
TAnimationCreator m_animCreator;
|
||||
|
||||
bool m_isPositionAssigned;
|
||||
bool m_isDirectionAssigned;
|
||||
bool m_isArrowDirectionAssigned;
|
||||
bool m_isRouteDirectionAssigned;
|
||||
bool m_isCompassAvailable;
|
||||
|
||||
bool m_positionIsObsolete;
|
||||
|
||||
@@ -31,8 +31,8 @@ namespace df
|
||||
{
|
||||
namespace
|
||||
{
|
||||
uint64_t constexpr kDoubleTapPauseMs = 250;
|
||||
uint64_t constexpr kLongTouchMs = 500;
|
||||
uint64_t constexpr kDoubleTapPauseMs = 200;
|
||||
uint64_t constexpr kLongTouchMs = 700;
|
||||
uint64_t constexpr kKineticDelayMs = 500;
|
||||
|
||||
float constexpr kForceTapThreshold = 0.75;
|
||||
|
||||
@@ -58,15 +58,15 @@ public:
|
||||
// Should match codes in the array below.
|
||||
static int8_t constexpr kEnglishCode = 1;
|
||||
static int8_t constexpr kUnsupportedLocaleCode = -1;
|
||||
static int8_t constexpr kSimplifiedChineseCode = 44;
|
||||
static int8_t constexpr kTraditionalChineseCode = 45;
|
||||
static int8_t constexpr kSimplifiedChineseCode = 45;
|
||||
static int8_t constexpr kTraditionalChineseCode = 46;
|
||||
// *NOTE* These constants should be updated when adding new
|
||||
// translation to categories.txt. When editing, keep in mind to check
|
||||
// CategoriesHolder::MapLocaleToInteger() and
|
||||
// CategoriesHolder::MapIntegerToLocale() as their implementations
|
||||
// strongly depend on the contents of the variable.
|
||||
// TODO: Refactor for more flexibility and to avoid breaking rules in two methods mentioned above.
|
||||
static std::array<CategoriesHolder::Mapping, 45> constexpr kLocaleMapping = {{
|
||||
static std::array<CategoriesHolder::Mapping, 46> constexpr kLocaleMapping = {{
|
||||
{"en", kEnglishCode},
|
||||
{"en-AU", 2},
|
||||
{"en-GB", 3},
|
||||
@@ -93,23 +93,24 @@ public:
|
||||
{"it", 24},
|
||||
{"ja", 25},
|
||||
{"ko", 26},
|
||||
{"lv", 27},
|
||||
{"mr", 28},
|
||||
{"nb", 29},
|
||||
{"nl", 30},
|
||||
{"pl", 31},
|
||||
{"pt", 32},
|
||||
{"pt-BR", 33},
|
||||
{"ro", 34},
|
||||
{"ru", 35},
|
||||
{"sk", 36},
|
||||
{"sr", 37},
|
||||
{"sv", 38},
|
||||
{"sw", 39},
|
||||
{"th", 40},
|
||||
{"tr", 41},
|
||||
{"uk", 42},
|
||||
{"vi", 43},
|
||||
{"lt", 27},
|
||||
{"lv", 28},
|
||||
{"mr", 29},
|
||||
{"nb", 30},
|
||||
{"nl", 31},
|
||||
{"pl", 32},
|
||||
{"pt", 33},
|
||||
{"pt-BR", 34},
|
||||
{"ro", 35},
|
||||
{"ru", 36},
|
||||
{"sk", 37},
|
||||
{"sr", 38},
|
||||
{"sv", 39},
|
||||
{"sw", 40},
|
||||
{"th", 41},
|
||||
{"tr", 42},
|
||||
{"uk", 43},
|
||||
{"vi", 44},
|
||||
{"zh-Hans", kSimplifiedChineseCode},
|
||||
{"zh-Hant", kTraditionalChineseCode},
|
||||
}};
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "ge0/url_generator.hpp"
|
||||
|
||||
#include "platform/location.hpp"
|
||||
#include "routing/route.hpp"
|
||||
#include "routing/speed_camera_prohibition.hpp"
|
||||
|
||||
@@ -1453,7 +1454,7 @@ void Framework::CreateDrapeEngine(ref_ptr<dp::GraphicsContextFactory> contextFac
|
||||
GetPlatform().RunTask(Platform::Thread::Gui, [this, mode, routingActive]()
|
||||
{
|
||||
// Deactivate selection (and hide place page) if we return to routing in F&R mode.
|
||||
if (routingActive && mode == location::FollowAndRotate)
|
||||
if (routingActive && (mode == location::FollowAndRotateCompass || mode == location::FollowAndRotateRoute))
|
||||
DeactivateMapSelection();
|
||||
|
||||
if (m_myPositionListener != nullptr)
|
||||
@@ -3208,6 +3209,7 @@ void Framework::ReadFeatures(function<void(FeatureType &)> const & reader, vecto
|
||||
void Framework::OnRouteFollow(routing::RouterType type)
|
||||
{
|
||||
bool const isPedestrianRoute = type == RouterType::Pedestrian;
|
||||
bool const allowRouteRotation = type == RouterType::Vehicle;
|
||||
bool const enableAutoZoom = isPedestrianRoute ? false : LoadAutoZoom();
|
||||
int const scale = isPedestrianRoute ? scales::GetPedestrianNavigationScale() : scales::GetNavigationScale();
|
||||
int scale3d = isPedestrianRoute ? scales::GetPedestrianNavigation3dScale() : scales::GetNavigation3dScale();
|
||||
@@ -3223,7 +3225,7 @@ void Framework::OnRouteFollow(routing::RouterType type)
|
||||
// TODO. We need to sync two enums VehicleType and RouterType to be able to pass
|
||||
// GetRoutingSettings(type).m_matchRoute to the FollowRoute() instead of |isPedestrianRoute|.
|
||||
// |isArrowGlued| parameter fully corresponds to |m_matchRoute| in RoutingSettings.
|
||||
m_drapeEngine->FollowRoute(scale, scale3d, enableAutoZoom, !isPedestrianRoute /* isArrowGlued */);
|
||||
m_drapeEngine->FollowRoute(scale, scale3d, enableAutoZoom, !isPedestrianRoute /* isArrowGlued */, allowRouteRotation);
|
||||
}
|
||||
|
||||
// RoutingManager::Delegate
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
#include "routing_manager.hpp"
|
||||
|
||||
#include "drape_frontend/my_position_controller.hpp"
|
||||
#include "geometry/angles.hpp"
|
||||
#include "map/chart_generator.hpp"
|
||||
#include "map/routing_mark.hpp"
|
||||
|
||||
#include "routing/absent_regions_finder.hpp"
|
||||
#include "routing/base/followed_polyline.hpp"
|
||||
#include "routing/checkpoint_predictor.hpp"
|
||||
#include "routing/index_router.hpp"
|
||||
#include "routing/route.hpp"
|
||||
@@ -1170,9 +1173,9 @@ void RoutingManager::SetDrapeEngine(ref_ptr<df::DrapeEngine> engine, bool is3dAl
|
||||
if (m_gpsInfoCache != nullptr)
|
||||
{
|
||||
auto routeMatchingInfo = GetRouteMatchingInfo(*m_gpsInfoCache);
|
||||
m_drapeEngine.SafeCall(&df::DrapeEngine::SetGpsInfo, *m_gpsInfoCache, m_routingSession.IsNavigable(),
|
||||
m_routingSession.GetDistanceToNextTurn(), m_routingSession.GetCurrentSpeedLimit(),
|
||||
routeMatchingInfo);
|
||||
df::NavigationContext navigationContext(m_routingSession.IsNavigable(), m_routingSession.GetDistanceToNextTurn(),
|
||||
m_routingSession.GetCurrentSpeedLimit(), GetRoutePolyline());
|
||||
m_drapeEngine.SafeCall(&df::DrapeEngine::SetGpsInfo, *m_gpsInfoCache, navigationContext, routeMatchingInfo);
|
||||
m_gpsInfoCache.reset();
|
||||
}
|
||||
|
||||
@@ -1516,9 +1519,9 @@ void RoutingManager::OnExtrapolatedLocationUpdate(location::GpsInfo const & info
|
||||
m_gpsInfoCache = make_unique<location::GpsInfo>(gpsInfo);
|
||||
|
||||
auto routeMatchingInfo = GetRouteMatchingInfo(gpsInfo);
|
||||
m_drapeEngine.SafeCall(&df::DrapeEngine::SetGpsInfo, gpsInfo, m_routingSession.IsNavigable(),
|
||||
m_routingSession.GetDistanceToNextTurn(), m_routingSession.GetCurrentSpeedLimit(),
|
||||
routeMatchingInfo);
|
||||
df::NavigationContext navigationContext(m_routingSession.IsNavigable(), m_routingSession.GetDistanceToNextTurn(),
|
||||
m_routingSession.GetCurrentSpeedLimit(), GetRoutePolyline());
|
||||
m_drapeEngine.SafeCall(&df::DrapeEngine::SetGpsInfo, gpsInfo, navigationContext, routeMatchingInfo);
|
||||
}
|
||||
|
||||
void RoutingManager::DeleteSavedRoutePoints()
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace routing::turns::sound
|
||||
* - All other variants default to `zh-Hans` (Simplified Chinese).
|
||||
*
|
||||
*/
|
||||
std::array<std::pair<std::string_view, std::string_view>, 42> constexpr kLanguageList = {{
|
||||
std::array<std::pair<std::string_view, std::string_view>, 43> constexpr kLanguageList = {{
|
||||
{"en", "English"},
|
||||
{"id", "Bahasa Indonesia"},
|
||||
{"ca", "Català"},
|
||||
@@ -82,5 +82,6 @@ std::array<std::pair<std::string_view, std::string_view>, 42> constexpr kLanguag
|
||||
#endif
|
||||
{"ja", "日本語"},
|
||||
{"ko", "한국어"},
|
||||
{"ta", "தமிழ்"},
|
||||
}};
|
||||
} // namespace routing::turns::sound
|
||||
|
||||
@@ -139,7 +139,8 @@ enum EMyPositionMode
|
||||
NotFollowNoPosition,
|
||||
NotFollow,
|
||||
Follow,
|
||||
FollowAndRotate
|
||||
FollowAndRotateCompass,
|
||||
FollowAndRotateRoute
|
||||
};
|
||||
|
||||
using TMyPositionModeChanged = std::function<void(location::EMyPositionMode, bool)>;
|
||||
|
||||
@@ -308,7 +308,8 @@ string ToString<location::EMyPositionMode>(location::EMyPositionMode const & v)
|
||||
case location::NotFollow: return "NotFollow";
|
||||
case location::NotFollowNoPosition: return "NotFollowNoPosition";
|
||||
case location::Follow: return "Follow";
|
||||
case location::FollowAndRotate: return "FollowAndRotate";
|
||||
case location::FollowAndRotateCompass: return "FollowAndRotateCompass";
|
||||
case location::FollowAndRotateRoute: return "FollowAndRotateRoute";
|
||||
default: return "Pending";
|
||||
}
|
||||
}
|
||||
@@ -324,8 +325,10 @@ bool FromString<location::EMyPositionMode>(string const & s, location::EMyPositi
|
||||
v = location::NotFollowNoPosition;
|
||||
else if (s == "Follow")
|
||||
v = location::Follow;
|
||||
else if (s == "FollowAndRotate")
|
||||
v = location::FollowAndRotate;
|
||||
else if (s == "FollowAndRotateCompass")
|
||||
v = location::FollowAndRotateCompass;
|
||||
else if (s == "FollowAndRotateRoute")
|
||||
v = location::FollowAndRotateRoute;
|
||||
else
|
||||
return false;
|
||||
|
||||
|
||||
@@ -237,4 +237,34 @@ Iter FollowedPolyline::GetClosestMatchingProjectionInInterval(m2::RectD const &
|
||||
|
||||
return nearestIter;
|
||||
}
|
||||
|
||||
m2::PointD FollowedPolyline::GetLookaheadPoint(double lookaheadDistanceM) const
|
||||
{
|
||||
if (!IsValid())
|
||||
return m2::PointD();
|
||||
|
||||
size_t segmentIdx = m_current.m_ind;
|
||||
m2::PointD prev = m_current.m_pt;
|
||||
|
||||
size_t const maxSegmentIdx = m_poly.GetSize() - 1;
|
||||
double remaining = lookaheadDistanceM;
|
||||
|
||||
while (segmentIdx < maxSegmentIdx)
|
||||
{
|
||||
m2::PointD const & next = m_poly.GetPoint(segmentIdx + 1);
|
||||
double segLen = mercator::DistanceOnEarth(prev, next);
|
||||
|
||||
if (remaining <= segLen)
|
||||
{
|
||||
double t = remaining / segLen;
|
||||
return prev + (next - prev) * t;
|
||||
}
|
||||
|
||||
remaining -= segLen;
|
||||
prev = next;
|
||||
segmentIdx += 1;
|
||||
}
|
||||
|
||||
return m_poly.GetPoint(segmentIdx);
|
||||
}
|
||||
} // namespace routing
|
||||
|
||||
@@ -125,6 +125,9 @@ public:
|
||||
|
||||
bool IsFakeSegment(size_t index) const;
|
||||
|
||||
/// \brief Obtain a point |lookaheadDistanceM| meters along the current route
|
||||
m2::PointD GetLookaheadPoint(double lookaheadDistanceM) const;
|
||||
|
||||
private:
|
||||
/// \returns iterator to the best projection of center of |posRect| to the |m_poly|.
|
||||
/// If there's a good projection of center of |posRect| to two closest segments of |m_poly|
|
||||
|
||||
Reference in New Issue
Block a user