Compare commits

...
Sign in to create a new pull request.

1 commit

Author SHA1 Message Date
Catarina Lemos
0ffe7a7e95 [ios][android] Add stop toast/hint and add stop button changes
Added a toast/hint to indicate to the user how
to add stops Users are now able to add
intermediate stop while building a route
and not only after finishing Added a add stop
button to the manage route menu in ios

Fixes: #7224

Signed-off-by: Catarina Lemos <catarina.s.lemos@tecnico.ulisboa.pt>
Co-authored-by: Rita Pessoa <rita.pessoa@tecnico.ulisboa.pt>
2024-05-27 16:55:25 +01:00
18 changed files with 311 additions and 56 deletions

View file

@ -1,19 +1,27 @@
package app.organicmaps.routing;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.content.SharedPreferences;
import android.widget.Toast;
import androidx.annotation.DrawableRes;
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar;
import androidx.core.view.ViewCompat;
import androidx.appcompat.app.AppCompatActivity;
import app.organicmaps.Framework;
import app.organicmaps.MwmApplication;
import app.organicmaps.R;
@ -181,6 +189,26 @@ public class RoutingPlanController extends ToolbarController
return (mFrameHeight > 0);
}
void addStopToast() {
SharedPreferences prefs = MwmApplication.prefs(requireActivity().getApplicationContext());
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean(String.valueOf(R.string.toast_dismiss), false);
editor.apply();
View progressFrame = getToolbar().findViewById(R.id.progress_frame);
LayoutInflater inflater = requireActivity().getLayoutInflater();
View layout = inflater.inflate(R.layout.toast_custom,null);
TextView text = layout.findViewById(R.id.toast_custom);
text.setText(R.string.add_stop_toast);
Toast toast = new Toast(progressFrame.getContext());
toast.setDuration(Toast.LENGTH_LONG);
toast.setGravity(Gravity.BOTTOM, 0, 160);
toast.setView(layout);
toast.show();
}
private void updateProgressLabels()
{
RoutingController.BuildState buildState = RoutingController.get().getBuildState();
@ -212,6 +240,13 @@ public class RoutingPlanController extends ToolbarController
final boolean showStartButton = !RoutingController.get().isRulerRouterType();
mRoutingBottomMenuController.setStartButton(showStartButton);
mRoutingBottomMenuController.showAltitudeChartAndRoutingDetails();
SharedPreferences prefs = MwmApplication.prefs(requireActivity().getApplicationContext());
boolean ShowToast = prefs.getBoolean(String.valueOf(R.string.toast_dismiss), true);
if (ShowToast) {
addStopToast();
}
}
public void updateBuildProgress(int progress, @Framework.RouterType int router)
@ -349,4 +384,4 @@ public class RoutingPlanController extends ToolbarController
mDrivingOptionsBtnContainer.removeOnLayoutChangeListener(this);
}
}
}
}

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#333333" />
<corners android:radius="16dp" />
<padding android:left="8dp" android:top="8dp" android:right="8dp" android:bottom="8dp" />
</shape>

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:background="@drawable/toast_custom_shape">
<TextView
android:id="@+id/toast_custom"
android:layout_width="340dp"
android:layout_height="30dp"
android:textColor="#FFFFFF"
android:textSize="16sp" />
</LinearLayout>

View file

@ -612,6 +612,8 @@
<string name="core_exit">Exit</string>
<string name="routing_add_start_point">Add a starting point to plan a route</string>
<string name="routing_add_finish_point">Add a destination to plan a route</string>
<string name="add_stop_toast">Select any place to add an intermediate stop</string>
<string name="toast_dismiss">Don\'t show again</string>
<string name="placepage_remove_stop">Remove</string>
<string name="placepage_add_stop">Add Stop</string>
<!-- Alert to ask user relogin to OpenStreetMap with OAuth2 flow after OAuth1 authentication is deprecated. -->
@ -697,6 +699,7 @@
<!-- Recommended length for CarPlay and Android Auto is around 25-27 characters -->
<string name="avoid_ferry">Avoid ferries</string>
<string name="avoid_motorways">Avoid freeways</string>
<string name="manage_route">Manage route</string>
<string name="unable_to_calc_alert_title">Unable to calculate route</string>
<string name="unable_to_calc_alert_subtitle">A route could not be found. This may be caused by your routing options or incomplete OpenStreetMap data. Please change your routing options and retry.</string>
<string name="define_to_avoid_btn">Define roads to avoid</string>
@ -2200,4 +2203,4 @@
<string name="type.amenity.events_venue">Events Venue</string>
<string name="type.shop.auction">Auction</string>
<string name="type.shop.collector">Collectables</string>
</resources>
</resources>

View file

@ -20194,6 +20194,51 @@
zh-Hans = 添加起点以规划路线
zh-Hant = 新增起點以計劃路線
[routing_add_stop_point]
tags = ios
en = Select any place to add an intermediate stop
af = Kies enige plek om n tussenstop by te voeg
ar = حدد أي مكان لإضافة توقف وسيط
az = Bir ara durmaq əlavə etmək üçün hər hansı bir yeri seçin
be = Выберыце любое месца, каб дадаць пралягчы стоп
bg = Изберете която и да е точка, за да добавите междинна спирка
ca = Seleccioneu qualsevol lloc per afegir una parada intermèdia
cs = Vyberte libovolné místo pro přidání mezipřistání
da = Vælg et hvilket som helst sted for at tilføje en mellemlanding
de = Wählen Sie einen beliebigen Ort aus, um einen Zwischenstopp hinzuzufügen
el = Επιλέξτε οποιοδήποτε μέρος για να προσθέσετε μια ενδιάμεση στάση
es = Selecciona cualquier lugar para añadir una parada intermedia
et = Valige mis tahes koht, et lisada vahepeatuse punkt
eu = Hautatu edozein toki eta gehitu eten erdia
fa = یک مکان را برای اضافه کردن یک توقف وسطی انتخاب کنید
fi = Valitse mikä tahansa paikka lisätäksesi välipysäkin
fr = Sélectionnez n'importe quel endroit pour ajouter un arrêt intermédiaire
he = בחרו בכל מקום להוסיף תחנה ביניים
hi = बिच का एक स्थान जोड़ने के लिए कोई भी स्थान चुनें
hu = Válasszon ki bármilyen helyet egy köztes megálló hozzáadásához
id = Pilih tempat mana pun untuk menambahkan titik persinggahan
it = Seleziona qualsiasi posto per aggiungere una fermata intermedia
ja = 中間停留所を追加する場所を選択してください
ko = 중간 정차지를 추가할 위치를 선택하세요
lt = Pasirinkite bet kurį vietą, kad pridėtumėte tarpinę stotelę
mr = बीचीकडील थांबायला कोणत्याही ठिकाणी निवडा
nb = Velg et hvilket som helst sted for å legge til en mellomlanding
nl = Selecteer een willekeurige plaats om een tussenstop toe te voegen
pl = Wybierz dowolne miejsce, aby dodać przystanek pośredni
pt = Selecione qualquer lugar para adicionar uma paragem intermédia
pt-BR = Selecione qualquer lugar para adicionar uma parada intermediária
ro = Selectați orice loc pentru a adăuga un punct intermediar de oprire
ru = Выберите любое место, чтобы добавить промежуточную остановку
sk = Vyberte ľubovoľné miesto pre pridanie medzipristátia
sv = Välj valfri plats för att lägga till ett mellanstopp
sw = Chagua eneo lolote kuongeza kituo cha kati
th = เลือกสถานที่ใดก็ได้เพื่อเพิ่มจุดหยุดระหว่างทาง
tr = Araya bir durak eklemek için herhangi bir yeri seçin
uk = Виберіть будь-яке місце, щоб додати проміжну зупинку
vi = Chọn bất kỳ địa điểm nào để thêm điểm dừng trung gian
zh-Hans = 选择任何地点添加中途停靠点
zh-Hant = 選擇任何地點添加中途停靠點
[routing_add_finish_point]
tags = android,ios
en = Add a destination to plan a route
@ -30345,4 +30390,4 @@
uk = Переглянути меню
vi = Xem thực đơn
zh-Hans = 查看菜单
zh-Hant = 查看選單
zh-Hant = 查看選單

View file

@ -57,4 +57,4 @@ final class BookmarksListInfoViewController: UIViewController {
let formattedText = NSAttributedString.string(withHtml: htmlText, defaultAttributes: [:])
return formattedText?.string
}
}
}

View file

@ -91,7 +91,43 @@ final class Toast: NSObject {
userInfo: nil,
repeats: false)
}
@objc func show(in view: UIView?, alignment: Alignment, pinToSafeArea: Bool = true, withOffset offset: CGFloat ) {
guard let view = view else { return }
blurView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(blurView)
let leadingConstraint = blurView.leadingAnchor.constraint(greaterThanOrEqualTo: view.leadingAnchor, constant: 16)
let trailingConstraint = blurView.trailingAnchor.constraint(lessThanOrEqualTo: view.trailingAnchor, constant: -16)
NSLayoutConstraint.activate([
leadingConstraint,
trailingConstraint
])
let verticalConstraint: NSLayoutConstraint
if alignment == .bottom {
verticalConstraint = blurView.bottomAnchor.constraint(equalTo: pinToSafeArea ? view.safeAreaLayoutGuide.bottomAnchor : view.bottomAnchor, constant: offset)
} else {
verticalConstraint = blurView.topAnchor.constraint(equalTo: pinToSafeArea ? view.safeAreaLayoutGuide.topAnchor : view.topAnchor, constant: offset)
}
NSLayoutConstraint.activate([
verticalConstraint,
blurView.centerXAnchor.constraint(equalTo: pinToSafeArea ? view.safeAreaLayoutGuide.centerXAnchor : view.centerXAnchor)
])
UIView.animate(withDuration: kDefaultAnimationDuration) {
self.blurView.alpha = 1
}
timer = Timer.scheduledTimer(timeInterval: 3,
target: self,
selector: #selector(hide),
userInfo: nil,
repeats: false)
}
@objc func hide() {
timer?.invalidate()
if self.blurView.superview != nil {
@ -101,4 +137,4 @@ final class Toast: NSObject {
Self.toasts.removeAll(where: { $0 === self }) }
}
}
}
}

View file

@ -0,0 +1,66 @@
import XCTest
@testable import Organic_Maps_Debug
final class MWMNavigationInfoViewTests: XCTestCase {
var userDefaults: UserDefaults!
override func setUp() {
super.setUp()
userDefaults = UserDefaults(suiteName: "com.example.OrganicMapsTests") // Use a test-specific suite
userDefaults.removePersistentDomain(forName: "com.example.OrganicMapsTests") // Clear UserDefaults before each test
}
override func tearDown() {
userDefaults = nil
super.tearDown()
}
func testAddStopToastAppearsOnlyInFirstSession() {
// Simulate first session
var isFirstSession = false;
if ( userDefaults.set(true, forKey: "isFirstSession") != nil ){
isFirstSession = true;
}
var hasStart = false;
var hasFinish = false;
//
if ( userDefaults.set(true, forKey: "hasStart") != nil ){
hasStart = true;
}
if ( userDefaults.set(true, forKey: "hasFinish") != nil ){
hasFinish = true;
}
if (hasStart && hasFinish && isFirstSession){
// Check if the toast view is visible
XCTAssertTrue(isFirstSession, "Toast view should be visible during the first session.")
}else if ( hasStart && hasFinish && !isFirstSession){
// Check if the toast view is hidden
XCTAssertFalse(!isFirstSession, "Toast view should not be visible after the first session.")
}
// Simulate subsequent session
if ( userDefaults.set(false, forKey: "isFirstSession") != nil){
isFirstSession = false;
}
if (hasStart && hasFinish && isFirstSession){
// Check if the toast view is visible
XCTAssertTrue(!isFirstSession, "Toast view should be visible during the first session.")
}else if ( hasStart && hasFinish && !isFirstSession){
// Check if the toast view is hidden
XCTAssertFalse(isFirstSession, "Toast view should not be visible after the first session.")
}
}
}

View file

@ -12,6 +12,8 @@
#import "UIImageView+Coloring.h"
#import "location_util.h"
#include <CoreApi/Framework.h>
#include "geometry/angles.hpp"
namespace
@ -92,6 +94,7 @@ BOOL defaultOrientation(CGSize const &size) {
@property(weak, nonatomic) MWMNavigationDashboardEntity *navigationInfo;
@property(nonatomic) BOOL hasLocation;
@property(nonatomic) BOOL hasShownAddStopToast;
@property(nonatomic) NSLayoutConstraint *topConstraint;
@property(nonatomic) NSLayoutConstraint *leftConstraint;
@ -117,8 +120,22 @@ BOOL defaultOrientation(CGSize const &size) {
BOOL const hasStart = ([MWMRouter startPoint] != nil);
BOOL const hasFinish = ([MWMRouter finishPoint] != nil);
Framework::DrapeCreationParams p;
BOOL const isFirstLaunch = [FirstSession isFirstSession];
BOOL const hasShownAddStopHint = [FirstSession hasShownAddStopToast];
self.hasLocation = ([MWMLocationManager lastLocation] != nil);
if (hasStart && hasFinish && isFirstLaunch && !hasShownAddStopHint) {
[self setToastViewHidden:YES];
MWMToast *toast = [MWMToast toastWithText:L(@"routing_add_stop_point")];
[toast showIn:[UIApplication sharedApplication].keyWindow alignment:MWMToastAlignmentBottom pinToSafeArea:YES withOffset:-103];
return;
}
if (hasStart && hasFinish) {
[self setToastViewHidden:YES];
return;
@ -144,6 +161,10 @@ BOOL defaultOrientation(CGSize const &size) {
[toastView configWithIsStart:YES withLocationButton:NO];
}
-(BOOL)isFirstLaunch{
return self.isFirstLaunch;
}
- (IBAction)openSearch {
BOOL const isStart = self.toastView.isStart;
auto searchManager = [MWMSearchManager manager];
@ -489,4 +510,4 @@ BOOL defaultOrientation(CGSize const &size) {
- (void)applyTheme {
[self setSearchState:_searchState];
}
@end
@end

View file

@ -207,6 +207,11 @@ final class RouteManagerViewController: MWMViewController, UITableViewDataSource
})
}
@IBAction func onAddStop(_ sender: UIButton) {
viewModel.addRoutePoint()
dismiss(animated: true, completion: nil)
}
@IBAction private func gestureRecognized(_ longPress: UIGestureRecognizer) {
let locationInView = gestureLocation(longPress, in: containerView)
let locationInTableView = gestureLocation(longPress, in: tableView)
@ -254,4 +259,4 @@ final class RouteManagerViewController: MWMViewController, UITableViewDataSource
func tableView(_: UITableView, editingStyleForRowAt _: IndexPath) -> UITableViewCell.EditingStyle {
return canDeleteRow ? .delete : .none
}
}
}

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15705" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15706"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21679"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@ -23,14 +23,14 @@
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3y4-jh-JUF" customClass="RouteManagerDimView" customModule="OMaps" customModuleProvider="target" propertyAccessControl="none">
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3y4-jh-JUF" customClass="RouteManagerDimView" customModule="Organic_Maps" customModuleProvider="target" propertyAccessControl="none">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<subviews>
<view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="TC7-xW-EwE">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="kTd-fr-ia7">
<rect key="frame" x="186" y="380" width="42" height="136.5"/>
<rect key="frame" x="186" y="379.5" width="42" height="137"/>
<subviews>
<imageView userInteractionEnabled="NO" alpha="0.0" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_route_manager_trash" translatesAutoresizingMaskIntoConstraints="NO" id="ZEN-kK-2CA">
<rect key="frame" x="-3" y="40" width="48" height="48"/>
@ -40,7 +40,7 @@
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" alpha="0.0" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1ks-iR-Wia">
<rect key="frame" x="0.0" y="96" width="42" height="20.5"/>
<rect key="frame" x="0.0" y="96" width="42" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
@ -72,6 +72,7 @@
</constraints>
</view>
</subviews>
<viewLayoutGuide key="safeArea" id="aNC-Xj-cAv"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<accessibility key="accessibilityConfiguration" identifier="DimView"/>
<gestureRecognizers/>
@ -81,7 +82,6 @@
<constraint firstAttribute="trailing" secondItem="TC7-xW-EwE" secondAttribute="trailing" id="kvp-My-OQZ"/>
<constraint firstItem="TC7-xW-EwE" firstAttribute="top" secondItem="3y4-jh-JUF" secondAttribute="top" id="lfE-MZ-Cvp"/>
</constraints>
<viewLayoutGuide key="safeArea" id="aNC-Xj-cAv"/>
<connections>
<outlet property="image" destination="ZEN-kK-2CA" id="zkv-Ag-qOp"/>
<outlet property="label" destination="1ks-iR-Wia" id="jsa-mi-iZ2"/>
@ -100,11 +100,11 @@
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="758" width="414" height="138"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="s0L-ul-gog" customClass="RouteManagerHeaderView" customModule="OMaps" customModuleProvider="target">
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="s0L-ul-gog" customClass="RouteManagerHeaderView" customModule="Organic_Maps" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="414" height="48"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="760" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dQh-uc-65J">
<rect key="frame" x="16" y="14" width="42" height="20.5"/>
<rect key="frame" x="16" y="13.5" width="42" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
@ -113,7 +113,7 @@
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="planning_route_manage_route"/>
</userDefinedRuntimeAttributes>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="0Fo-Ae-QrQ">
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="0Fo-Ae-QrQ">
<rect key="frame" x="370" y="0.0" width="28" height="48"/>
<inset key="contentEdgeInsets" minX="8" minY="0.0" maxX="0.0" maxY="0.0"/>
<inset key="imageEdgeInsets" minX="-8" minY="0.0" maxX="0.0" maxY="0.0"/>
@ -122,6 +122,18 @@
<action selector="onAdd" destination="-1" eventType="touchUpInside" id="zGx-NU-Hzq"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="tailTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="60p-dV-R5X" userLabel="Add Stop Button">
<rect key="frame" x="255" y="0.0" width="78" height="48"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<state key="normal" title="Button"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="FlatNormalTransButton"/>
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="Add Stop"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="onAddStop:" destination="-1" eventType="touchUpInside" id="AF7-kv-mKg"/>
</connections>
</button>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="MJB-bK-y5c">
<rect key="frame" x="16" y="47" width="398" height="1"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
@ -155,7 +167,7 @@
<outlet property="titleLabel" destination="dQh-uc-65J" id="xKP-bY-mQP"/>
</connections>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="mqc-M4-MPb" customClass="RouteManagerFooterView" customModule="OMaps" customModuleProvider="target">
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="mqc-M4-MPb" customClass="RouteManagerFooterView" customModule="Organic_Maps" customModuleProvider="target">
<rect key="frame" x="0.0" y="48" width="414" height="48"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="or0-Gy-0su">
@ -175,7 +187,7 @@
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Divider"/>
</userDefinedRuntimeAttributes>
</view>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="2WE-hS-hzl">
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="2WE-hS-hzl">
<rect key="frame" x="16" y="0.0" width="46" height="48"/>
<state key="normal" title="Button"/>
<userDefinedRuntimeAttributes>
@ -186,7 +198,7 @@
<action selector="onCancel" destination="-1" eventType="touchUpInside" id="Lzk-mL-6Jg"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="u3h-0s-UBo">
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="u3h-0s-UBo">
<rect key="frame" x="352" y="0.0" width="46" height="48"/>
<state key="normal" title="Button"/>
<userDefinedRuntimeAttributes>
@ -225,7 +237,7 @@
<outlet property="separator" destination="pjx-6o-NWS" id="EKT-Ba-Lcy"/>
</connections>
</view>
<tableView clipsSubviews="YES" contentMode="scaleToFill" showsHorizontalScrollIndicator="NO" style="plain" separatorStyle="none" allowsSelection="NO" rowHeight="48" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="Zqd-YQ-ksD" customClass="RouteManagerTableView" customModule="OMaps" customModuleProvider="target">
<tableView clipsSubviews="YES" contentMode="scaleToFill" showsHorizontalScrollIndicator="NO" style="plain" separatorStyle="none" allowsSelection="NO" rowHeight="48" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="Zqd-YQ-ksD" customClass="RouteManagerTableView" customModule="Organic_Maps" customModuleProvider="target">
<rect key="frame" x="0.0" y="48" width="414" height="0.0"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<gestureRecognizers/>
@ -263,6 +275,7 @@
</userDefinedRuntimeAttributes>
</view>
</subviews>
<viewLayoutGuide key="safeArea" id="hNI-dt-MwJ"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<accessibility key="accessibilityConfiguration" identifier="ControllerView"/>
<gestureRecognizers/>
@ -272,8 +285,7 @@
<constraint firstAttribute="bottom" secondItem="i5M-Pr-FkT" secondAttribute="bottom" id="qXL-Hf-8X6"/>
<constraint firstItem="i5M-Pr-FkT" firstAttribute="top" relation="greaterThanOrEqual" secondItem="TKX-qe-h4F" secondAttribute="top" constant="72" id="sYB-lu-PhM"/>
</constraints>
<viewLayoutGuide key="safeArea" id="hNI-dt-MwJ"/>
<point key="canvasLocation" x="-239.5" y="-48.5"/>
<point key="canvasLocation" x="-240.57971014492756" y="-48.883928571428569"/>
</view>
<tapGestureRecognizer id="deN-Z9-rbS" userLabel="Cancel Tap Gesture Recognizer">
<connections>
@ -290,4 +302,4 @@
<image name="ic_get_position" width="20" height="20"/>
<image name="ic_route_manager_trash" width="24" height="24"/>
</resources>
</document>
</document>

View file

@ -18,6 +18,9 @@ final class RouteManagerViewModel: NSObject, RouteManagerViewModelProtocol {
MWMRouter.updatePreviewMode()
refreshControlsCallback()
}
func addRoutePoint() {
MWMRouter.cancelRouteManagerTransaction()
}
func movePoint(at index: Int, to newIndex: Int) {
MWMRouter.movePoint(at: index, to: newIndex)
MWMRouter.updatePreviewMode()
@ -29,4 +32,4 @@ final class RouteManagerViewModel: NSObject, RouteManagerViewModelProtocol {
refreshControlsCallback()
reloadCallback()
}
}
}

View file

@ -10,6 +10,7 @@ protocol RouteManagerViewModelProtocol: AnyObject {
func cancelTransaction()
func addLocationPoint()
func addRoutePoint()
func movePoint(at index: Int, to newIndex: Int)
func deletePoint(at index: Int)
}
}

View file

@ -1,4 +1,4 @@
/*******************************************************************************
/***************************
The MIT License (MIT)
Copyright (c) 2021 Alexander Borsuk <me@alex.bio> from Minsk, Belarus
@ -20,7 +20,7 @@
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*******************************************************************************/
***************************/
#ifndef FIRSTSESSION_H
#define FIRSTSESSION_H
@ -40,6 +40,8 @@
+ (void)setup:(NSArray *)serverUrls withLaunchOptions:(NSDictionary *)options;
// Returns YES if it is a first session, before app goes into background.
+ (BOOL)isFirstSession;
// Returns YES if it has already showed the add stop hint.
+ (BOOL)hasShownAddStopToast;
// Returns summary time of all active user sessions up to now.
+ (NSInteger)totalSecondsSpentInTheApp;
@ -57,4 +59,4 @@
+ (NSString *)installationId;
@end
#endif // #ifndef FIRSTSESSION_H
#endif // #ifndef FIRSTSESSION_H

View file

@ -1,4 +1,4 @@
/*******************************************************************************
/***************************
The MIT License (MIT)
Copyright (c) 2021 Alexander Borsuk <me@alex.bio> from Minsk, Belarus
@ -20,7 +20,7 @@
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*******************************************************************************/
***************************/
#if ! __has_feature(objc_arc)
#error This file must be compiled with ARC. Either turn on ARC for the project or use -fobjc-arc flag
@ -60,6 +60,7 @@ static NSString * const kIsAlohalyticsDisabledKey = @"AlohalyticsDisabledKey";
// setup should be called to activate counting.
static NSDate * gSessionStartTime = nil;
static BOOL gIsFirstSession = NO;
static int gHasShownAddStopToast = 1;
static NSString * gInstallationId = nil;
@implementation FirstSession
@ -92,6 +93,11 @@ static NSString * gInstallationId = nil;
return [self installationId] != nil && gIsFirstSession;
}
+ (BOOL)hasShownAddStopToast {
gHasShownAddStopToast -= 1;
return gHasShownAddStopToast==1;
}
+ (NSDate *)firstLaunchDate {
NSUserDefaults * ud = [NSUserDefaults standardUserDefaults];
NSDate * date = [ud objectForKey:kFirstLaunchDateKey];
@ -147,4 +153,4 @@ static NSString * gInstallationId = nil;
return gInstallationId;
}
@end
@end

View file

@ -926,6 +926,8 @@
"routing_add_start_point" = "Add a starting point to plan a route";
"routing_add_stop_point" = "Select any place to add an intermediate stop"
"routing_add_finish_point" = "Add a destination to plan a route";
"planning_route_manage_route" = "Manage Route";
@ -3907,4 +3909,4 @@
"type.shop.auction" = "Auction";
"type.shop.collector" = "Collectables";
"type.shop.collector" = "Collectables";

View file

@ -926,6 +926,8 @@
"routing_add_start_point" = "Add a starting point to plan a route";
"routing_add_stop_point" = "Select any place to add an intermediate stop"
"routing_add_finish_point" = "Add a destination to plan a route";
"planning_route_manage_route" = "Manage Route";
@ -3907,4 +3909,4 @@
"type.shop.auction" = "Auction";
"type.shop.collector" = "Collectables";
"type.shop.collector" = "Collectables";

View file

@ -103,7 +103,6 @@ RouteMarkData GetLastPassedPoint(BookmarkManager * bmManager, vector<RouteMarkDa
{
data.m_position = bmManager->MyPositionMark().GetPivot();
data.m_isMyPosition = false;
data.m_replaceWithMyPositionAfterRestart = true;
}
return data;
@ -117,7 +116,6 @@ void SerializeRoutePoint(json_t * node, RouteMarkData const & data)
ToJSONObject(*node, "subtitle", data.m_subTitle);
ToJSONObject(*node, "x", data.m_position.x);
ToJSONObject(*node, "y", data.m_position.y);
ToJSONObject(*node, "replaceWithMyPosition", data.m_replaceWithMyPositionAfterRestart);
}
RouteMarkData DeserializeRoutePoint(json_t * node)
@ -135,8 +133,6 @@ RouteMarkData DeserializeRoutePoint(json_t * node)
FromJSONObject(node, "x", data.m_position.x);
FromJSONObject(node, "y", data.m_position.y);
FromJSONObject(node, "replaceWithMyPosition", data.m_replaceWithMyPositionAfterRestart);
return data;
}
@ -151,7 +147,7 @@ string SerializeRoutePoints(vector<RouteMarkData> const & points)
json_array_append_new(pointsNode.get(), pointNode.release());
}
unique_ptr<char, JSONFreeDeleter> buffer(
json_dumps(pointsNode.get(), JSON_COMPACT));
json_dumps(pointsNode.get(), JSON_COMPACT | JSON_ENSURE_ASCII));
return string(buffer.get());
}
@ -418,7 +414,7 @@ void RoutingManager::OnBuildRouteReady(Route const & route, RouterResultCode cod
m2::RectD routeRect = route.GetPoly().GetLimitRect();
routeRect.Scale(kRouteScaleMultiplier);
m_drapeEngine.SafeCall(&df::DrapeEngine::SetModelViewRect, routeRect,
true /* applyRotation */, -1 /* zoom */, true /* isAnim */,
true /* applyRotation /, -1 / zoom /, true / isAnim */,
true /* useVisibleViewport */);
}
@ -834,8 +830,12 @@ size_t RoutingManager::GetRoutePointsCount() const
bool RoutingManager::CouldAddIntermediatePoint() const
{
if (!IsRoutingActive())
//if there isn't already a start or a finish point, we can't add an intermediate point
RoutePointsLayout routePoints(*m_bmManager);
int pointsCount = routePoints.GetRoutePointsCount();
if ( pointsCount == 0){
return false;
}
return m_bmManager->GetUserMarkIds(UserMark::Type::ROUTING).size()
< RoutePointsLayout::kMaxIntermediatePointsCount + 2;
@ -995,9 +995,9 @@ void RoutingManager::ReorderIntermediatePoints()
prevPoints[i]->SetIntermediateIndex(i < insertIndex ? i : i + 1);
}
void RoutingManager::GenerateNotifications(vector<string> & turnNotifications, bool announceStreets)
void RoutingManager::GenerateNotifications(vector<string> & turnNotifications)
{
m_routingSession.GenerateNotifications(turnNotifications, announceStreets);
m_routingSession.GenerateNotifications(turnNotifications);
}
void RoutingManager::BuildRoute(uint32_t timeoutSec)
@ -1055,7 +1055,7 @@ void RoutingManager::BuildRoute(uint32_t timeoutSec)
//m2::RectD rect = ShowPreviewSegments(routePoints);
//rect.Scale(kRouteScaleMultiplier);
//m_drapeEngine.SafeCall(&df::DrapeEngine::SetModelViewRect, rect, true /* applyRotation */,
// -1 /* zoom */, true /* isAnim */, true /* useVisibleViewport */);
// -1 /* zoom /, true / isAnim /, true / useVisibleViewport */);
m_routingSession.ClearPositionAccumulator();
m_routingSession.SetUserCurrentPosition(routePoints.front().m_position);
@ -1111,7 +1111,7 @@ void RoutingManager::CheckLocationForRouting(location::GpsInfo const & info)
m_routingSession.RebuildRoute(
mercator::FromLatLon(info.m_latitude, info.m_longitude),
[this](Route const & route, RouterResultCode code) { OnRebuildRouteReady(route, code); },
nullptr /* needMoreMapsCallback */, nullptr /* removeRouteCallback */,
nullptr /* needMoreMapsCallback /, nullptr / removeRouteCallback */,
RouterDelegate::kNoTimeout, SessionState::RouteRebuilding, true /* adjustToPrevRoute */);
}
}
@ -1401,18 +1401,13 @@ void RoutingManager::LoadRoutePoints(LoadRouteHandler const & handler)
[this, handler, points = std::move(points)]() mutable
{
ASSERT(m_bmManager != nullptr, ());
// If we have found my position and the saved route used the user's position, we use my position as start point.
bool routeUsedPosition = false;
// If we have found my position, we use my position as start point.
auto const & myPosMark = m_bmManager->MyPositionMark();
auto editSession = m_bmManager->GetEditSession();
editSession.ClearGroup(UserMark::Type::ROUTING);
for (auto & p : points)
{
// Check if the saved route used the user's position
if (p.m_replaceWithMyPositionAfterRestart && p.m_pointType == RouteMarkType::Start)
routeUsedPosition = true;
if (p.m_replaceWithMyPositionAfterRestart && p.m_pointType == RouteMarkType::Start && myPosMark.HasPosition())
if (p.m_pointType == RouteMarkType::Start && myPosMark.HasPosition())
{
RouteMarkData startPt;
startPt.m_pointType = RouteMarkType::Start;
@ -1426,9 +1421,9 @@ void RoutingManager::LoadRoutePoints(LoadRouteHandler const & handler)
}
}
// If we don't have my position and the saved route used it, save loading timestamp.
// Probably we will get my position soon.
if (routeUsedPosition && !myPosMark.HasPosition())
// If we don't have my position, save loading timestamp. Probably
// we will get my position soon.
if (!myPosMark.HasPosition())
m_loadRoutePointsTimestamp = chrono::steady_clock::now();
if (handler)
@ -1574,4 +1569,4 @@ void RoutingManager::SetSubroutesVisibility(bool visible)
bool RoutingManager::IsSpeedCamLimitExceeded() const
{
return m_routingSession.IsSpeedCamLimitExceeded();
}
}