Compare commits
	
		
			15 Commits
		
	
	
		
			renovate/c
			...
			e70b455541
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| e70b455541 | |||
| ad72d104a0 | |||
| acc7631c55 | |||
| bea3a65fec | |||
| f71b9b19a6 | |||
| 89511f39cb | |||
| 71d9554d97 | |||
| c87a01b2e8 | |||
| 5748630b99 | |||
| 016622c7af | |||
| bf129b201d | |||
| 86bcec6b29 | |||
| 07dde5ab58 | |||
| db82495f11 | |||
| 889b6c2096 | 
							
								
								
									
										32
									
								
								.gitea/workflows/backend_run-lint.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								.gitea/workflows/backend_run-lint.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					on:
 | 
				
			||||||
 | 
					  pull_request:
 | 
				
			||||||
 | 
					    branches:
 | 
				
			||||||
 | 
					      - main
 | 
				
			||||||
 | 
					    paths:
 | 
				
			||||||
 | 
					      - backend/**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					name: Run linting on the backend code
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jobs:
 | 
				
			||||||
 | 
					  build:
 | 
				
			||||||
 | 
					    name: Build
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - uses: https://gitea.com/actions/checkout@v4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - name: Install dependencies
 | 
				
			||||||
 | 
					      run: |
 | 
				
			||||||
 | 
					        apt-get update && apt-get install -y python3 python3-pip
 | 
				
			||||||
 | 
					        pip install pipenv
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - name: Install packages
 | 
				
			||||||
 | 
					      run: |
 | 
				
			||||||
 | 
					        ls -la
 | 
				
			||||||
 | 
					        # only install dev-packages
 | 
				
			||||||
 | 
					        pipenv install --categories=dev-packages
 | 
				
			||||||
 | 
					      working-directory: backend
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - name: Run linter
 | 
				
			||||||
 | 
					      run: pipenv run pylint src
 | 
				
			||||||
 | 
					      working-directory: backend
 | 
				
			||||||
							
								
								
									
										32
									
								
								.gitea/workflows/backend_run-test.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								.gitea/workflows/backend_run-test.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					on:
 | 
				
			||||||
 | 
					  pull_request:
 | 
				
			||||||
 | 
					    branches:
 | 
				
			||||||
 | 
					      - main
 | 
				
			||||||
 | 
					    paths:
 | 
				
			||||||
 | 
					      - backend/**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					name: Run testing on the backend code
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jobs:
 | 
				
			||||||
 | 
					  build:
 | 
				
			||||||
 | 
					    name: Build
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - uses: https://gitea.com/actions/checkout@v4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - name: Install dependencies
 | 
				
			||||||
 | 
					      run: |
 | 
				
			||||||
 | 
					        apt-get update && apt-get install -y python3 python3-pip
 | 
				
			||||||
 | 
					        pip install pipenv
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - name: Install packages
 | 
				
			||||||
 | 
					      run: |
 | 
				
			||||||
 | 
					        ls -la
 | 
				
			||||||
 | 
					        # install all packages, including dev-packages
 | 
				
			||||||
 | 
					        pipenv install --dev
 | 
				
			||||||
 | 
					      working-directory: backend
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - name: Run Tests
 | 
				
			||||||
 | 
					      run: pipenv run pytest src
 | 
				
			||||||
 | 
					      working-directory: backend
 | 
				
			||||||
@@ -43,8 +43,10 @@ jobs:
 | 
				
			|||||||
      working-directory: ./frontend
 | 
					      working-directory: ./frontend
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    - name: Add required secrets
 | 
					    - name: Add required secrets
 | 
				
			||||||
 | 
					      env:
 | 
				
			||||||
 | 
					        ANDROID_SECRETS_PROPERTIES: ${{ secrets.ANDROID_SECRETS_PROPERTIES }}
 | 
				
			||||||
      run: |
 | 
					      run: |
 | 
				
			||||||
        echo ${{ secrets.ANDROID_SECRETS_PROPERTIES }} > ./android/secrets.properties
 | 
					        echo "$ANDROID_SECRETS_PROPERTIES" >> ./android/secrets.properties
 | 
				
			||||||
      working-directory: ./frontend
 | 
					      working-directory: ./frontend
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    - name: Sanity check
 | 
					    - name: Sanity check
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										6
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							@@ -14,9 +14,9 @@
 | 
				
			|||||||
                "DEBUG": "true"
 | 
					                "DEBUG": "true"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "args": [
 | 
					            "args": [
 | 
				
			||||||
                "--app-dir",
 | 
					                // "--app-dir",
 | 
				
			||||||
                "src",
 | 
					                // "src",
 | 
				
			||||||
                "main:app",
 | 
					                "src.main:app",
 | 
				
			||||||
                "--reload",
 | 
					                "--reload",
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
            "jinja": true,
 | 
					            "jinja": true,
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										6
									
								
								backend/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								backend/.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,6 +1,10 @@
 | 
				
			|||||||
# osm-cache
 | 
					# osm-cache and wikidata cache
 | 
				
			||||||
cache/
 | 
					cache/
 | 
				
			||||||
apicache/
 | 
					apicache/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# wikidata throttle
 | 
				
			||||||
 | 
					*.ctrl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Byte-compiled / optimized / DLL files
 | 
					# Byte-compiled / optimized / DLL files
 | 
				
			||||||
__pycache__/
 | 
					__pycache__/
 | 
				
			||||||
*.py[cod]
 | 
					*.py[cod]
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								backend/.pylintrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								backend/.pylintrc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					[MAIN]
 | 
				
			||||||
 | 
					max-line-length=240
 | 
				
			||||||
@@ -13,5 +13,6 @@ EXPOSE 8000
 | 
				
			|||||||
# Set environment variables used by the deployment. These can be overridden by the user using this image.
 | 
					# Set environment variables used by the deployment. These can be overridden by the user using this image.
 | 
				
			||||||
ENV NUM_WORKERS=1
 | 
					ENV NUM_WORKERS=1
 | 
				
			||||||
ENV OSM_CACHE_DIR=/cache
 | 
					ENV OSM_CACHE_DIR=/cache
 | 
				
			||||||
 | 
					ENV MEMCACHED_HOST_PATH=none
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CMD fastapi run src/main.py --port 8000 --workers $NUM_WORKERS
 | 
					CMD fastapi run src/main.py --port 8000 --workers $NUM_WORKERS
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,8 @@ verify_ssl = true
 | 
				
			|||||||
name = "pypi"
 | 
					name = "pypi"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dev-packages]
 | 
					[dev-packages]
 | 
				
			||||||
 | 
					pylint = "*"
 | 
				
			||||||
 | 
					pytest = "*"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[packages]
 | 
					[packages]
 | 
				
			||||||
numpy = "*"
 | 
					numpy = "*"
 | 
				
			||||||
@@ -14,3 +16,4 @@ shapely = "*"
 | 
				
			|||||||
scipy = "*"
 | 
					scipy = "*"
 | 
				
			||||||
osmpythontools = "*"
 | 
					osmpythontools = "*"
 | 
				
			||||||
pywikibot = "*"
 | 
					pywikibot = "*"
 | 
				
			||||||
 | 
					pymemcache = "*"
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										952
									
								
								backend/Pipfile.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										952
									
								
								backend/Pipfile.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										0
									
								
								backend/src/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								backend/src/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -15,13 +15,21 @@ OSM_CACHE_DIR = Path(cache_dir_string)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import logging
 | 
					import logging
 | 
				
			||||||
import yaml
 | 
					# if we are in a debug session, set verbose and rich logging
 | 
				
			||||||
 | 
					 | 
				
			||||||
LOGGING_CONFIG = LOCATION_PREFIX / 'log_config.yaml'
 | 
					 | 
				
			||||||
config = yaml.safe_load(LOGGING_CONFIG.read_text())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
logging.config.dictConfig(config)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# if we are in a debug session, set the log level to debug
 | 
					 | 
				
			||||||
if os.getenv('DEBUG', False):
 | 
					if os.getenv('DEBUG', False):
 | 
				
			||||||
    logging.getLogger().setLevel(logging.DEBUG)
 | 
					    from rich.logging import RichHandler
 | 
				
			||||||
 | 
					    logging.basicConfig(
 | 
				
			||||||
 | 
					        level=logging.DEBUG,
 | 
				
			||||||
 | 
					        format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
 | 
				
			||||||
 | 
					        handlers=[RichHandler()]
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					else:
 | 
				
			||||||
 | 
					    logging.basicConfig(
 | 
				
			||||||
 | 
					        level=logging.INFO,
 | 
				
			||||||
 | 
					        format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MEMCACHED_HOST_PATH = os.getenv('MEMCACHED_HOST_PATH', None)
 | 
				
			||||||
 | 
					if MEMCACHED_HOST_PATH == "none":
 | 
				
			||||||
 | 
					    MEMCACHED_HOST_PATH = None
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,34 +0,0 @@
 | 
				
			|||||||
version: 1
 | 
					 | 
				
			||||||
disable_existing_loggers: False
 | 
					 | 
				
			||||||
formatters:
 | 
					 | 
				
			||||||
  simple:
 | 
					 | 
				
			||||||
    format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
 | 
					 | 
				
			||||||
handlers:
 | 
					 | 
				
			||||||
  console:
 | 
					 | 
				
			||||||
    class: rich.logging.RichHandler
 | 
					 | 
				
			||||||
    formatter: simple
 | 
					 | 
				
			||||||
  # access:
 | 
					 | 
				
			||||||
  #   class: logging.FileHandler
 | 
					 | 
				
			||||||
  #   filename: logs/access.log
 | 
					 | 
				
			||||||
  #   level: INFO
 | 
					 | 
				
			||||||
  #   formatter: simple
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
loggers:
 | 
					 | 
				
			||||||
  uvicorn.error:
 | 
					 | 
				
			||||||
    level: INFO
 | 
					 | 
				
			||||||
    handlers:
 | 
					 | 
				
			||||||
      - console
 | 
					 | 
				
			||||||
    propagate: no
 | 
					 | 
				
			||||||
  # uvicorn.access:
 | 
					 | 
				
			||||||
  #   level: INFO
 | 
					 | 
				
			||||||
  #   handlers:
 | 
					 | 
				
			||||||
  #     - access
 | 
					 | 
				
			||||||
  #   propagate: no
 | 
					 | 
				
			||||||
root:
 | 
					 | 
				
			||||||
  level: INFO
 | 
					 | 
				
			||||||
  handlers:
 | 
					 | 
				
			||||||
    - console
 | 
					 | 
				
			||||||
  propagate: yes
 | 
					 | 
				
			||||||
@@ -1,12 +1,14 @@
 | 
				
			|||||||
import logging
 | 
					import logging
 | 
				
			||||||
from fastapi import FastAPI, Query, Body
 | 
					from fastapi import FastAPI, Query, Body, HTTPException
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from structs.landmark import Landmark
 | 
					from .structs.landmark import Landmark
 | 
				
			||||||
from structs.preferences import Preferences
 | 
					from .structs.preferences import Preferences
 | 
				
			||||||
from structs.linked_landmarks import LinkedLandmarks
 | 
					from .structs.linked_landmarks import LinkedLandmarks
 | 
				
			||||||
from utils.landmarks_manager import LandmarkManager
 | 
					from .structs.trip import Trip
 | 
				
			||||||
from utils.optimizer import Optimizer
 | 
					from .utils.landmarks_manager import LandmarkManager
 | 
				
			||||||
from utils.refiner import Refiner
 | 
					from .utils.optimizer import Optimizer
 | 
				
			||||||
 | 
					from .utils.refiner import Refiner
 | 
				
			||||||
 | 
					from .persistence import client as cache_client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
logger = logging.getLogger(__name__)
 | 
					logger = logging.getLogger(__name__)
 | 
				
			||||||
@@ -17,8 +19,8 @@ optimizer = Optimizer()
 | 
				
			|||||||
refiner = Refiner(optimizer=optimizer)
 | 
					refiner = Refiner(optimizer=optimizer)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@app.post("/route/new")
 | 
					@app.post("/trip/new")
 | 
				
			||||||
def get_route(preferences: Preferences, start: tuple[float, float], end: tuple[float, float] | None = None) -> str:
 | 
					def new_trip(preferences: Preferences, start: tuple[float, float], end: tuple[float, float] | None = None) -> Trip:
 | 
				
			||||||
    '''
 | 
					    '''
 | 
				
			||||||
    Main function to call the optimizer.
 | 
					    Main function to call the optimizer.
 | 
				
			||||||
    :param preferences: the preferences specified by the user as the post body
 | 
					    :param preferences: the preferences specified by the user as the post body
 | 
				
			||||||
@@ -27,15 +29,17 @@ def get_route(preferences: Preferences, start: tuple[float, float], end: tuple[f
 | 
				
			|||||||
    :return: the uuid of the first landmark in the optimized route
 | 
					    :return: the uuid of the first landmark in the optimized route
 | 
				
			||||||
    '''
 | 
					    '''
 | 
				
			||||||
    if preferences is None:
 | 
					    if preferences is None:
 | 
				
			||||||
        raise ValueError("Please provide preferences in the form of a 'Preference' BaseModel class.")
 | 
					        raise HTTPException(status_code=406, detail="Preferences not provided")
 | 
				
			||||||
 | 
					    if preferences.shopping.score == 0 and preferences.sightseeing.score == 0 and preferences.nature.score == 0:
 | 
				
			||||||
 | 
					        raise HTTPException(status_code=406, detail="All preferences are 0.")
 | 
				
			||||||
    if start is None:
 | 
					    if start is None:
 | 
				
			||||||
        raise ValueError("Please provide the starting coordinates as a tuple of floats.")
 | 
					        raise HTTPException(status_code=406, detail="Start coordinates not provided")
 | 
				
			||||||
    if end is None:
 | 
					    if end is None:
 | 
				
			||||||
        end = start
 | 
					        end = start
 | 
				
			||||||
        logger.info("No end coordinates provided. Using start=end.")
 | 
					        logger.info("No end coordinates provided. Using start=end.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    start_landmark = Landmark(name='start', type='start', location=(start[0], start[1]), osm_type='start', osm_id=0, attractiveness=0, must_do=True, n_tags = 0)
 | 
					    start_landmark = Landmark(name='start', type='start', location=(start[0], start[1]), osm_type='start', osm_id=0, attractiveness=0, must_do=True, n_tags = 0)
 | 
				
			||||||
    end_landmark = Landmark(name='end', type='finish', location=(end[0], end[1]), osm_type='end', osm_id=0, attractiveness=0, must_do=True, n_tags = 0)
 | 
					    end_landmark = Landmark(name='finish', type='finish', location=(end[0], end[1]), osm_type='end', osm_id=0, attractiveness=0, must_do=True, n_tags = 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Generate the landmarks from the start location
 | 
					    # Generate the landmarks from the start location
 | 
				
			||||||
    landmarks, landmarks_short = manager.generate_landmarks_list(
 | 
					    landmarks, landmarks_short = manager.generate_landmarks_list(
 | 
				
			||||||
@@ -47,22 +51,37 @@ def get_route(preferences: Preferences, start: tuple[float, float], end: tuple[f
 | 
				
			|||||||
    landmarks_short.insert(0, start_landmark)
 | 
					    landmarks_short.insert(0, start_landmark)
 | 
				
			||||||
    landmarks_short.append(end_landmark)
 | 
					    landmarks_short.append(end_landmark)
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    # TODO infer these parameters from the preferences
 | 
					 | 
				
			||||||
    max_walking_time = 4    # hours
 | 
					 | 
				
			||||||
    detour = 30             # minutes
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # First stage optimization
 | 
					    # First stage optimization
 | 
				
			||||||
    base_tour = optimizer.solve_optimization(max_walking_time*60, landmarks_short)
 | 
					    try:
 | 
				
			||||||
 | 
					        base_tour = optimizer.solve_optimization(preferences.max_time_minute, landmarks_short)
 | 
				
			||||||
 | 
					    except ArithmeticError:
 | 
				
			||||||
 | 
					        raise HTTPException(status_code=500, detail="No solution found")
 | 
				
			||||||
 | 
					    except TimeoutError:
 | 
				
			||||||
 | 
					        raise HTTPException(status_code=500, detail="Optimzation took too long")
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    # Second stage optimization
 | 
					    # Second stage optimization
 | 
				
			||||||
    refined_tour = refiner.refine_optimization(landmarks, base_tour, max_walking_time*60, detour)
 | 
					    refined_tour = refiner.refine_optimization(landmarks, base_tour, preferences.max_time_minute, preferences.detour_tolerance_minute)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    linked_tour = LinkedLandmarks(refined_tour)
 | 
					    linked_tour = LinkedLandmarks(refined_tour)
 | 
				
			||||||
    return linked_tour[0].uuid
 | 
					    # upon creation of the trip, persistence of both the trip and its landmarks is ensured. Ca
 | 
				
			||||||
 | 
					    trip = Trip.from_linked_landmarks(linked_tour, cache_client)
 | 
				
			||||||
 | 
					    return trip
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### For already existing trips/landmarks
 | 
				
			||||||
 | 
					@app.get("/trip/{trip_uuid}")
 | 
				
			||||||
 | 
					def get_trip(trip_uuid: str) -> Trip:
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        trip = cache_client.get(f"trip_{trip_uuid}")
 | 
				
			||||||
 | 
					        return trip
 | 
				
			||||||
 | 
					    except KeyError:
 | 
				
			||||||
 | 
					        raise HTTPException(status_code=404, detail="Trip not found")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@app.get("/landmark/{landmark_uuid}")
 | 
					@app.get("/landmark/{landmark_uuid}")
 | 
				
			||||||
def get_landmark(landmark_uuid: str) -> Landmark:
 | 
					def get_landmark(landmark_uuid: str) -> Landmark:
 | 
				
			||||||
    #cherche dans linked_tour et retourne le landmark correspondant
 | 
					    try:
 | 
				
			||||||
    pass
 | 
					        landmark = cache_client.get(f"landmark_{landmark_uuid}")
 | 
				
			||||||
 | 
					        return landmark
 | 
				
			||||||
 | 
					    except KeyError:
 | 
				
			||||||
 | 
					        raise HTTPException(status_code=404, detail="Landmark not found")
 | 
				
			||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
city_bbox_side: 5000 #m
 | 
					city_bbox_side: 5000 #m
 | 
				
			||||||
radius_close_to: 50
 | 
					radius_close_to: 50
 | 
				
			||||||
church_coeff: 0.8
 | 
					church_coeff: 0.8
 | 
				
			||||||
park_coeff: 1.2
 | 
					park_coeff: 1.0
 | 
				
			||||||
tag_coeff: 10
 | 
					tag_coeff: 10
 | 
				
			||||||
N_important: 40
 | 
					N_important: 40
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										26
									
								
								backend/src/persistence.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								backend/src/persistence.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					from pymemcache.client.base import Client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .constants import MEMCACHED_HOST_PATH
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DummyClient:
 | 
				
			||||||
 | 
					    _data = {}
 | 
				
			||||||
 | 
					    def set(self, key, value, **kwargs):
 | 
				
			||||||
 | 
					        self._data[key] = value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def set_many(self, data, **kwargs):
 | 
				
			||||||
 | 
					        self._data.update(data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get(self, key, **kwargs):
 | 
				
			||||||
 | 
					        return self._data[key]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if MEMCACHED_HOST_PATH is None:
 | 
				
			||||||
 | 
					    client = DummyClient()
 | 
				
			||||||
 | 
					else:
 | 
				
			||||||
 | 
					    client = Client(
 | 
				
			||||||
 | 
					        MEMCACHED_HOST_PATH,
 | 
				
			||||||
 | 
					        timeout=1,
 | 
				
			||||||
 | 
					        allow_unicode_keys=True,
 | 
				
			||||||
 | 
					        encoding='utf-8'
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
@@ -1,6 +1,5 @@
 | 
				
			|||||||
import uuid
 | 
					 | 
				
			||||||
from .landmark import Landmark
 | 
					from .landmark import Landmark
 | 
				
			||||||
from utils.get_time_separation import get_time
 | 
					from ..utils.get_time_separation import get_time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LinkedLandmarks:
 | 
					class LinkedLandmarks:
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
@@ -9,8 +8,7 @@ class LinkedLandmarks:
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    _landmarks = list[Landmark]
 | 
					    _landmarks = list[Landmark]
 | 
				
			||||||
    total_time = int
 | 
					    total_time: int = 0
 | 
				
			||||||
    uuid = str
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, data: list[Landmark] = None) -> None:
 | 
					    def __init__(self, data: list[Landmark] = None) -> None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
@@ -19,7 +17,6 @@ class LinkedLandmarks:
 | 
				
			|||||||
        Args:
 | 
					        Args:
 | 
				
			||||||
            data (list[Landmark], optional): The list of landmarks that are linked together. Defaults to None.
 | 
					            data (list[Landmark], optional): The list of landmarks that are linked together. Defaults to None.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        self.uuid = uuid.uuid4()
 | 
					 | 
				
			||||||
        self._landmarks = data if data else []
 | 
					        self._landmarks = data if data else []
 | 
				
			||||||
        self._link_landmarks()
 | 
					        self._link_landmarks()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -28,7 +25,6 @@ class LinkedLandmarks:
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        Create the links between the landmarks in the list by setting their .next_uuid and the .time_to_next attributes.
 | 
					        Create the links between the landmarks in the list by setting their .next_uuid and the .time_to_next attributes.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        self.total_time = 0
 | 
					 | 
				
			||||||
        for i, landmark in enumerate(self._landmarks[:-1]):
 | 
					        for i, landmark in enumerate(self._landmarks[:-1]):
 | 
				
			||||||
            landmark.next_uuid = self._landmarks[i + 1].uuid
 | 
					            landmark.next_uuid = self._landmarks[i + 1].uuid
 | 
				
			||||||
            time_to_next = get_time(landmark.location, self._landmarks[i + 1].location)
 | 
					            time_to_next = get_time(landmark.location, self._landmarks[i + 1].location)
 | 
				
			||||||
@@ -44,18 +40,4 @@ class LinkedLandmarks:
 | 
				
			|||||||
    
 | 
					    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __str__(self) -> str:
 | 
					    def __str__(self) -> str:
 | 
				
			||||||
        return f"LinkedLandmarks, total time: {self.total_time} minutes, {len(self._landmarks)} stops: [{','.join([str(landmark) for landmark in self._landmarks])}]"
 | 
					        return f"LinkedLandmarks [{' ->'.join([str(landmark) for landmark in self._landmarks])}]"
 | 
				
			||||||
    
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def asdict(self) -> dict:
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Convert the linked landmarks to a json serializable dictionary.
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        Returns:
 | 
					 | 
				
			||||||
            dict: A dictionary representation of the linked landmarks.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        return {
 | 
					 | 
				
			||||||
            'uuid': self.uuid,
 | 
					 | 
				
			||||||
            'total_time': self.total_time,
 | 
					 | 
				
			||||||
            'landmarks': [landmark.dict() for landmark in self._landmarks]
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,6 @@ from pydantic import BaseModel
 | 
				
			|||||||
from typing import Optional, Literal
 | 
					from typing import Optional, Literal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Preference(BaseModel) :
 | 
					class Preference(BaseModel) :
 | 
				
			||||||
    name: str
 | 
					 | 
				
			||||||
    type: Literal['sightseeing', 'nature', 'shopping', 'start', 'finish']
 | 
					    type: Literal['sightseeing', 'nature', 'shopping', 'start', 'finish']
 | 
				
			||||||
    score: int          # score could be from 1 to 5
 | 
					    score: int          # score could be from 1 to 5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -17,5 +16,5 @@ class Preferences(BaseModel) :
 | 
				
			|||||||
    # Shopping (diriger plutôt vers des zones / rues commerçantes)
 | 
					    # Shopping (diriger plutôt vers des zones / rues commerçantes)
 | 
				
			||||||
    shopping : Preference
 | 
					    shopping : Preference
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    max_time_minute: Optional[int] = 6*60 
 | 
					    max_time_minute: Optional[int] = 6*60
 | 
				
			||||||
    detour_tolerance_minute: Optional[int] = 0
 | 
					    detour_tolerance_minute: Optional[int] = 0
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										30
									
								
								backend/src/structs/trip.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								backend/src/structs/trip.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					from pydantic import BaseModel, Field
 | 
				
			||||||
 | 
					from pymemcache.client.base import Client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .linked_landmarks import LinkedLandmarks
 | 
				
			||||||
 | 
					import uuid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Trip(BaseModel):
 | 
				
			||||||
 | 
					    uuid: str = Field(default_factory=uuid.uuid4)
 | 
				
			||||||
 | 
					    total_time: int
 | 
				
			||||||
 | 
					    first_landmark_uuid: str
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def from_linked_landmarks(self, landmarks: LinkedLandmarks, cache_client: Client) -> "Trip":
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Initialize a new Trip object and ensure it is stored in the cache.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        trip = Trip(
 | 
				
			||||||
 | 
					            total_time = landmarks.total_time,
 | 
				
			||||||
 | 
					            first_landmark_uuid = str(landmarks[0].uuid)
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Store the trip in the cache
 | 
				
			||||||
 | 
					        cache_client.set(f"trip_{trip.uuid}", trip)
 | 
				
			||||||
 | 
					        cache_client.set_many({f"landmark_{landmark.uuid}": landmark for landmark in landmarks}, expire=3600)
 | 
				
			||||||
 | 
					        # is equivalent to:
 | 
				
			||||||
 | 
					        # for landmark in landmarks:
 | 
				
			||||||
 | 
					        #     cache_client.set(f"landmark_{landmark.uuid}", landmark, expire=3600)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return trip
 | 
				
			||||||
@@ -20,22 +20,13 @@ def test(start_coords: tuple[float, float], finish_coords: tuple[float, float] =
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    preferences = Preferences(
 | 
					    preferences = Preferences(
 | 
				
			||||||
                    sightseeing=Preference(
 | 
					        sightseeing=Preference(type='sightseeing', score = 5),
 | 
				
			||||||
                                  name='sightseeing', 
 | 
					        nature=Preference(type='nature', score = 5),
 | 
				
			||||||
                                  type='sightseeing',
 | 
					        shopping=Preference(type='shopping', score = 5),
 | 
				
			||||||
                                  score = 5),
 | 
					 | 
				
			||||||
                    nature=Preference(
 | 
					 | 
				
			||||||
                                  name='nature', 
 | 
					 | 
				
			||||||
                                  type='nature',
 | 
					 | 
				
			||||||
                                  score = 5),
 | 
					 | 
				
			||||||
                    shopping=Preference(
 | 
					 | 
				
			||||||
                                  name='shopping', 
 | 
					 | 
				
			||||||
                                  type='shopping',
 | 
					 | 
				
			||||||
                                  score = 5),
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    max_time_minute=180,
 | 
					        max_time_minute=180,
 | 
				
			||||||
                    detour_tolerance_minute=30
 | 
					        detour_tolerance_minute=30
 | 
				
			||||||
                    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Create start and finish 
 | 
					    # Create start and finish 
 | 
				
			||||||
    if finish_coords is None :
 | 
					    if finish_coords is None :
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										0
									
								
								backend/src/tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								backend/src/tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										34
									
								
								backend/src/tests/test_main.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								backend/src/tests/test_main.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					from fastapi.testclient import TestClient
 | 
				
			||||||
 | 
					import pytest
 | 
				
			||||||
 | 
					from ..main import app
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@pytest.fixture()
 | 
				
			||||||
 | 
					def client():
 | 
				
			||||||
 | 
					    return TestClient(app)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_new_trip_invalid_prefs(client):
 | 
				
			||||||
 | 
					    response = client.post(
 | 
				
			||||||
 | 
					        "/trip/new",
 | 
				
			||||||
 | 
					        json={
 | 
				
			||||||
 | 
					            "preferences": {},
 | 
				
			||||||
 | 
					            "start": [48.8566, 2.3522]
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    assert response.status_code == 422
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_new_trip_single_prefs(client):
 | 
				
			||||||
 | 
					    response = client.post(
 | 
				
			||||||
 | 
					        "/trip/new",
 | 
				
			||||||
 | 
					        json={
 | 
				
			||||||
 | 
					            "preferences": {"sightseeing": {"type": "sightseeing", "score": 1}, "nature": {"type": "nature", "score": 1}, "shopping": {"type": "shopping", "score": 1}, "max_time_minute": 360, "detour_tolerance_minute": 0},
 | 
				
			||||||
 | 
					            "start": [48.8566, 2.3522]
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    assert response.status_code == 200
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_new_trip_matches_prefs(client):
 | 
				
			||||||
 | 
					    # todo
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
@@ -1,9 +1,9 @@
 | 
				
			|||||||
import yaml
 | 
					import yaml
 | 
				
			||||||
from geopy.distance import geodesic
 | 
					from geopy.distance import geodesic
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import constants
 | 
					from ..constants import OPTIMIZER_PARAMETERS_PATH
 | 
				
			||||||
 | 
					
 | 
				
			||||||
with constants.OPTIMIZER_PARAMETERS_PATH.open('r') as f:
 | 
					with OPTIMIZER_PARAMETERS_PATH.open('r') as f:
 | 
				
			||||||
    parameters = yaml.safe_load(f)
 | 
					    parameters = yaml.safe_load(f)
 | 
				
			||||||
    DETOUR_FACTOR = parameters['detour_factor']
 | 
					    DETOUR_FACTOR = parameters['detour_factor']
 | 
				
			||||||
    AVERAGE_WALKING_SPEED = parameters['average_walking_speed']
 | 
					    AVERAGE_WALKING_SPEED = parameters['average_walking_speed']
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,15 +9,10 @@ from pywikibot import config
 | 
				
			|||||||
config.put_throttle = 0
 | 
					config.put_throttle = 0
 | 
				
			||||||
config.maxlag = 0
 | 
					config.maxlag = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from structs.preferences import Preferences, Preference
 | 
					from ..structs.preferences import Preferences, Preference
 | 
				
			||||||
from structs.landmark import Landmark
 | 
					from ..structs.landmark import Landmark
 | 
				
			||||||
from .take_most_important import take_most_important
 | 
					from .take_most_important import take_most_important
 | 
				
			||||||
import constants
 | 
					from ..constants import AMENITY_SELECTORS_PATH, LANDMARK_PARAMETERS_PATH, OPTIMIZER_PARAMETERS_PATH, OSM_CACHE_DIR
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
SIGHTSEEING = 'sightseeing'
 | 
					 | 
				
			||||||
NATURE = 'nature'
 | 
					 | 
				
			||||||
SHOPPING = 'shopping'
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -25,7 +20,6 @@ class LandmarkManager:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    logger = logging.getLogger(__name__)
 | 
					    logger = logging.getLogger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    city_bbox_side: int     # bbox side in meters
 | 
					 | 
				
			||||||
    radius_close_to: int    # radius in meters
 | 
					    radius_close_to: int    # radius in meters
 | 
				
			||||||
    church_coeff: float     # coeff to adjsut score of churches
 | 
					    church_coeff: float     # coeff to adjsut score of churches
 | 
				
			||||||
    park_coeff: float       # coeff to adjust score of parks
 | 
					    park_coeff: float       # coeff to adjust score of parks
 | 
				
			||||||
@@ -35,20 +29,25 @@ class LandmarkManager:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def __init__(self) -> None:
 | 
					    def __init__(self) -> None:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        with constants.AMENITY_SELECTORS_PATH.open('r') as f:
 | 
					        with AMENITY_SELECTORS_PATH.open('r') as f:
 | 
				
			||||||
            self.amenity_selectors = yaml.safe_load(f)
 | 
					            self.amenity_selectors = yaml.safe_load(f)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        with constants.LANDMARK_PARAMETERS_PATH.open('r') as f:
 | 
					        with LANDMARK_PARAMETERS_PATH.open('r') as f:
 | 
				
			||||||
            parameters = yaml.safe_load(f)
 | 
					            parameters = yaml.safe_load(f)
 | 
				
			||||||
            self.city_bbox_side = parameters['city_bbox_side']
 | 
					            self.max_bbox_side = parameters['city_bbox_side']
 | 
				
			||||||
            self.radius_close_to = parameters['radius_close_to']
 | 
					            self.radius_close_to = parameters['radius_close_to']
 | 
				
			||||||
            self.church_coeff = parameters['church_coeff']
 | 
					            self.church_coeff = parameters['church_coeff']
 | 
				
			||||||
            self.park_coeff = parameters['park_coeff']
 | 
					            self.park_coeff = parameters['park_coeff']
 | 
				
			||||||
            self.tag_coeff = parameters['tag_coeff']
 | 
					            self.tag_coeff = parameters['tag_coeff']
 | 
				
			||||||
            self.N_important = parameters['N_important']
 | 
					            self.N_important = parameters['N_important']
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        with OPTIMIZER_PARAMETERS_PATH.open('r') as f:
 | 
				
			||||||
 | 
					            parameters = yaml.safe_load(f)
 | 
				
			||||||
 | 
					            self.walking_speed = parameters['average_walking_speed']
 | 
				
			||||||
 | 
					            self.detour_factor = parameters['detour_factor']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.overpass = Overpass()
 | 
					        self.overpass = Overpass()
 | 
				
			||||||
        CachingStrategy.use(JSON, cacheDir=constants.OSM_CACHE_DIR)
 | 
					        CachingStrategy.use(JSON, cacheDir=OSM_CACHE_DIR)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def generate_landmarks_list(self, center_coordinates: tuple[float, float], preferences: Preferences) -> tuple[list[Landmark], list[Landmark]]:
 | 
					    def generate_landmarks_list(self, center_coordinates: tuple[float, float], preferences: Preferences) -> tuple[list[Landmark], list[Landmark]]:
 | 
				
			||||||
@@ -69,30 +68,33 @@ class LandmarkManager:
 | 
				
			|||||||
                - A list of the most important landmarks based on the user's preferences.
 | 
					                - A list of the most important landmarks based on the user's preferences.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        max_walk_dist = (preferences.max_time_minute/2)/60*self.walking_speed*1000/self.detour_factor
 | 
				
			||||||
 | 
					        reachable_bbox_side = min(max_walk_dist, self.max_bbox_side)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        L = []
 | 
					        L = []
 | 
				
			||||||
        bbox = self.create_bbox(center_coordinates)
 | 
					        bbox = self.create_bbox(center_coordinates, reachable_bbox_side)
 | 
				
			||||||
        # list for sightseeing
 | 
					        # list for sightseeing
 | 
				
			||||||
        if preferences.sightseeing.score != 0:
 | 
					        if preferences.sightseeing.score != 0:
 | 
				
			||||||
            score_function = lambda loc, n_tags: int((self.count_elements_close_to(loc) + ((n_tags**1.2)*self.tag_coeff) )*self.church_coeff)  
 | 
					            score_function = lambda loc, n_tags: int((((n_tags**1.2)*self.tag_coeff) )*self.church_coeff)   # self.count_elements_close_to(loc) +
 | 
				
			||||||
            L1 = self.fetch_landmarks(bbox, self.amenity_selectors['sightseeing'], SIGHTSEEING, score_function)
 | 
					            L1 = self.fetch_landmarks(bbox, self.amenity_selectors['sightseeing'], preferences.sightseeing.type, score_function)
 | 
				
			||||||
            self.correct_score(L1, preferences.sightseeing)
 | 
					 | 
				
			||||||
            L += L1
 | 
					            L += L1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # list for nature
 | 
					        # list for nature
 | 
				
			||||||
        if preferences.nature.score != 0:
 | 
					        if preferences.nature.score != 0:
 | 
				
			||||||
            score_function = lambda loc, n_tags: int((self.count_elements_close_to(loc) + ((n_tags**1.2)*self.tag_coeff) )*self.park_coeff)  
 | 
					            score_function = lambda loc, n_tags: int((((n_tags**1.2)*self.tag_coeff) )*self.park_coeff)   # self.count_elements_close_to(loc) +
 | 
				
			||||||
            L2 = self.fetch_landmarks(bbox, self.amenity_selectors['nature'], NATURE, score_function)
 | 
					            L2 = self.fetch_landmarks(bbox, self.amenity_selectors['nature'], preferences.nature.type, score_function)
 | 
				
			||||||
            self.correct_score(L2, preferences.nature)
 | 
					 | 
				
			||||||
            L += L2
 | 
					            L += L2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # list for shopping
 | 
					        # list for shopping
 | 
				
			||||||
        if preferences.shopping.score != 0:
 | 
					        if preferences.shopping.score != 0:
 | 
				
			||||||
            score_function = lambda loc, n_tags: int(self.count_elements_close_to(loc) + ((n_tags**1.2)*self.tag_coeff))
 | 
					            score_function = lambda loc, n_tags: int(((n_tags**1.2)*self.tag_coeff)) # self.count_elements_close_to(loc) +
 | 
				
			||||||
            L3 = self.fetch_landmarks(bbox, self.amenity_selectors['shopping'], SHOPPING, score_function)
 | 
					            L3 = self.fetch_landmarks(bbox, self.amenity_selectors['shopping'], preferences.shopping.type, score_function)
 | 
				
			||||||
            self.correct_score(L3, preferences.shopping)
 | 
					 | 
				
			||||||
            L += L3
 | 
					            L += L3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        L = self.remove_duplicates(L)
 | 
					        L = self.remove_duplicates(L)
 | 
				
			||||||
 | 
					        self.correct_score(L, preferences)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        L_constrained = take_most_important(L, self.N_important)
 | 
					        L_constrained = take_most_important(L, self.N_important)
 | 
				
			||||||
        self.logger.info(f'Generated {len(L)} landmarks around {center_coordinates}, and constrained to {len(L_constrained)} most important ones.')
 | 
					        self.logger.info(f'Generated {len(L)} landmarks around {center_coordinates}, and constrained to {len(L_constrained)} most important ones.')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -123,7 +125,7 @@ class LandmarkManager:
 | 
				
			|||||||
        return L_clean
 | 
					        return L_clean
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def correct_score(self, landmarks: list[Landmark], preference: Preference):
 | 
					    def correct_score(self, landmarks: list[Landmark], preferences: Preferences) -> None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Adjust the attractiveness score of each landmark in the list based on user preferences.
 | 
					        Adjust the attractiveness score of each landmark in the list based on user preferences.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -132,20 +134,16 @@ class LandmarkManager:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        Args:
 | 
					        Args:
 | 
				
			||||||
            landmarks (list[Landmark]): A list of landmarks whose scores need to be corrected.
 | 
					            landmarks (list[Landmark]): A list of landmarks whose scores need to be corrected.
 | 
				
			||||||
            preference (Preference): The user's preference settings that influence the attractiveness score adjustment.
 | 
					            preferences (Preferences): The user's preference settings that influence the attractiveness score adjustment.
 | 
				
			||||||
 | 
					 | 
				
			||||||
        Raises:
 | 
					 | 
				
			||||||
            TypeError: If the type of any landmark in the list does not match the expected type in the preference.
 | 
					 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if len(landmarks) == 0:
 | 
					        score_dict = {
 | 
				
			||||||
            return
 | 
					            preferences.sightseeing.type: preferences.sightseeing.score,
 | 
				
			||||||
        
 | 
					            preferences.nature.type: preferences.nature.score,
 | 
				
			||||||
        if landmarks[0].type != preference.type:
 | 
					            preferences.shopping.type: preferences.shopping.score
 | 
				
			||||||
            raise TypeError(f"LandmarkType {preference.type} does not match the type of Landmark {landmarks[0].name}")
 | 
					        }
 | 
				
			||||||
 | 
					        for landmark in landmarks:
 | 
				
			||||||
        for elem in landmarks:
 | 
					            landmark.attractiveness = int(landmark.attractiveness * score_dict[landmark.type] / 5)        
 | 
				
			||||||
            elem.attractiveness = int(elem.attractiveness*preference.score/5)     # arbitrary computation
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def count_elements_close_to(self, coordinates: tuple[float, float]) -> int:
 | 
					    def count_elements_close_to(self, coordinates: tuple[float, float]) -> int:
 | 
				
			||||||
@@ -191,12 +189,13 @@ class LandmarkManager:
 | 
				
			|||||||
            return 0
 | 
					            return 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def create_bbox(self, coordinates: tuple[float, float]) -> tuple[float, float, float, float]:
 | 
					    def create_bbox(self, coordinates: tuple[float, float], reachable_bbox_side: int) -> tuple[float, float, float, float]:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Create a bounding box around the given coordinates.
 | 
					        Create a bounding box around the given coordinates.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Args:
 | 
					        Args:
 | 
				
			||||||
            coordinates (tuple[float, float]): The latitude and longitude of the center of the bounding box.
 | 
					            coordinates (tuple[float, float]): The latitude and longitude of the center of the bounding box.
 | 
				
			||||||
 | 
					            reachable_bbox_side (int): The side length of the bounding box in meters.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Returns:
 | 
					        Returns:
 | 
				
			||||||
            tuple[float, float, float, float]: The minimum latitude, minimum longitude, maximum latitude, and maximum longitude
 | 
					            tuple[float, float, float, float]: The minimum latitude, minimum longitude, maximum latitude, and maximum longitude
 | 
				
			||||||
@@ -207,7 +206,7 @@ class LandmarkManager:
 | 
				
			|||||||
        lon = coordinates[1]
 | 
					        lon = coordinates[1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Half the side length in km (since it's a square bbox)
 | 
					        # Half the side length in km (since it's a square bbox)
 | 
				
			||||||
        half_side_length_km = self.city_bbox_side / 2 / 1000
 | 
					        half_side_length_km = reachable_bbox_side / 2 / 1000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Convert distance to degrees
 | 
					        # Convert distance to degrees
 | 
				
			||||||
        lat_diff = half_side_length_km / 111  # 1 degree latitude is approximately 111 km
 | 
					        lat_diff = half_side_length_km / 111  # 1 degree latitude is approximately 111 km
 | 
				
			||||||
@@ -296,21 +295,26 @@ class LandmarkManager:
 | 
				
			|||||||
                        break
 | 
					                        break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if "wikipedia" in tag:
 | 
					                    if "wikipedia" in tag:
 | 
				
			||||||
                        n_tags += 3             # wikipedia entries count more
 | 
					                        n_tags += 1             # wikipedia entries count more
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if tag == "wikidata":
 | 
					                    # if tag == "wikidata":
 | 
				
			||||||
                        Q = elem.tag('wikidata')
 | 
					                    #     Q = elem.tag('wikidata')
 | 
				
			||||||
                        site = Site("wikidata", "wikidata")
 | 
					                    #     site = Site("wikidata", "wikidata")
 | 
				
			||||||
                        item = ItemPage(site, Q)
 | 
					                    #     item = ItemPage(site, Q)
 | 
				
			||||||
                        item.get()
 | 
					                    #     item.get()
 | 
				
			||||||
                        n_languages = len(item.labels)
 | 
					                    #     n_languages = len(item.labels)
 | 
				
			||||||
                        n_tags += n_languages/10
 | 
					                    #     n_tags += n_languages/10
 | 
				
			||||||
 | 
					                    if "viewpoint" in tag:
 | 
				
			||||||
 | 
					                        n_tags += 10
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if elem_type != "nature":
 | 
					                    if elem_type != "nature":
 | 
				
			||||||
                        if "leisure" in tag and elem.tag('leisure') == "park":
 | 
					                        if "leisure" in tag and elem.tag('leisure') == "park":
 | 
				
			||||||
                            elem_type = "nature"
 | 
					                            elem_type = "nature"
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    if elem_type == "nature":
 | 
				
			||||||
 | 
					                        n_tags += 1 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if landmarktype != SHOPPING:
 | 
					                    if landmarktype != "shopping":
 | 
				
			||||||
                        if "shop" in tag:
 | 
					                        if "shop" in tag:
 | 
				
			||||||
                            skip = True
 | 
					                            skip = True
 | 
				
			||||||
                            break
 | 
					                            break
 | 
				
			||||||
@@ -318,7 +322,6 @@ class LandmarkManager:
 | 
				
			|||||||
                        if tag == "building" and elem.tag('building') in ['retail', 'supermarket', 'parking']:
 | 
					                        if tag == "building" and elem.tag('building') in ['retail', 'supermarket', 'parking']:
 | 
				
			||||||
                            skip = True
 | 
					                            skip = True
 | 
				
			||||||
                            break
 | 
					                            break
 | 
				
			||||||
 | 
					 | 
				
			||||||
                if skip:
 | 
					                if skip:
 | 
				
			||||||
                    continue
 | 
					                    continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,9 +5,9 @@ from scipy.optimize import linprog
 | 
				
			|||||||
from collections import defaultdict, deque
 | 
					from collections import defaultdict, deque
 | 
				
			||||||
from geopy.distance import geodesic
 | 
					from geopy.distance import geodesic
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from structs.landmark import Landmark
 | 
					from ..structs.landmark import Landmark
 | 
				
			||||||
from .get_time_separation import get_time
 | 
					from .get_time_separation import get_time
 | 
				
			||||||
import constants
 | 
					from ..constants import OPTIMIZER_PARAMETERS_PATH
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -26,7 +26,7 @@ class Optimizer:
 | 
				
			|||||||
    def __init__(self) :
 | 
					    def __init__(self) :
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # load parameters from file
 | 
					        # load parameters from file
 | 
				
			||||||
        with constants.OPTIMIZER_PARAMETERS_PATH.open('r') as f:
 | 
					        with OPTIMIZER_PARAMETERS_PATH.open('r') as f:
 | 
				
			||||||
            parameters = yaml.safe_load(f)
 | 
					            parameters = yaml.safe_load(f)
 | 
				
			||||||
            self.detour_factor = parameters['detour_factor']
 | 
					            self.detour_factor = parameters['detour_factor']
 | 
				
			||||||
            self.average_walking_speed = parameters['average_walking_speed']
 | 
					            self.average_walking_speed = parameters['average_walking_speed']
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,10 +3,10 @@ import yaml, logging
 | 
				
			|||||||
from shapely import buffer, LineString, Point, Polygon, MultiPoint, concave_hull
 | 
					from shapely import buffer, LineString, Point, Polygon, MultiPoint, concave_hull
 | 
				
			||||||
from math import pi
 | 
					from math import pi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from structs.landmark import Landmark
 | 
					from ..structs.landmark import Landmark
 | 
				
			||||||
from . import take_most_important, get_time_separation
 | 
					from . import take_most_important, get_time_separation
 | 
				
			||||||
from .optimizer import Optimizer
 | 
					from .optimizer import Optimizer
 | 
				
			||||||
import constants
 | 
					from ..constants import OPTIMIZER_PARAMETERS_PATH
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -24,7 +24,7 @@ class Refiner :
 | 
				
			|||||||
        self.optimizer = optimizer
 | 
					        self.optimizer = optimizer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # load parameters from file
 | 
					        # load parameters from file
 | 
				
			||||||
        with constants.OPTIMIZER_PARAMETERS_PATH.open('r') as f:
 | 
					        with OPTIMIZER_PARAMETERS_PATH.open('r') as f:
 | 
				
			||||||
            parameters = yaml.safe_load(f)
 | 
					            parameters = yaml.safe_load(f)
 | 
				
			||||||
            self.detour_factor = parameters['detour_factor']
 | 
					            self.detour_factor = parameters['detour_factor']
 | 
				
			||||||
            self.detour_corridor_width = parameters['detour_corridor_width']
 | 
					            self.detour_corridor_width = parameters['detour_corridor_width']
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
from structs.landmark import Landmark
 | 
					from ..structs.landmark import Landmark
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def take_most_important(landmarks: list[Landmark], N_important) -> list[Landmark] :
 | 
					def take_most_important(landmarks: list[Landmark], N_important) -> list[Landmark] :
 | 
				
			||||||
    L = len(landmarks)
 | 
					    L = len(landmarks)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -42,7 +42,7 @@ if (secretPropertiesFile.exists()) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
android {
 | 
					android {
 | 
				
			||||||
    namespace "com.example.fast_network_navigation"
 | 
					    namespace "com.anydev.anyway"
 | 
				
			||||||
    compileSdk flutter.compileSdkVersion
 | 
					    compileSdk flutter.compileSdkVersion
 | 
				
			||||||
    ndkVersion flutter.ndkVersion
 | 
					    ndkVersion flutter.ndkVersion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -61,7 +61,7 @@ android {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    defaultConfig {
 | 
					    defaultConfig {
 | 
				
			||||||
        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
 | 
					        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
 | 
				
			||||||
        applicationId "com.example.fast_network_navigation"
 | 
					        applicationId "com.anydev.anyway"
 | 
				
			||||||
        // You can update the following values to match your application needs.
 | 
					        // You can update the following values to match your application needs.
 | 
				
			||||||
        // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
 | 
					        // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
 | 
				
			||||||
        // Minimum Android version for Google Maps SDK
 | 
					        // Minimum Android version for Google Maps SDK
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,7 +3,7 @@
 | 
				
			|||||||
    <uses-permission android:name="android.permission.INTERNET"/>
 | 
					    <uses-permission android:name="android.permission.INTERNET"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <application
 | 
					    <application
 | 
				
			||||||
        android:label="fast_network_navigation"
 | 
					        android:label="anyway"
 | 
				
			||||||
        android:name="${applicationName}"
 | 
					        android:name="${applicationName}"
 | 
				
			||||||
        android:icon="@mipmap/ic_launcher">
 | 
					        android:icon="@mipmap/ic_launcher">
 | 
				
			||||||
        <activity
 | 
					        <activity
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
package com.example.fast_network_navigation
 | 
					package com.anydev.anyway
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import io.flutter.embedding.android.FlutterActivity
 | 
					import io.flutter.embedding.android.FlutterActivity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,7 +13,7 @@
 | 
				
			|||||||
	<key>CFBundleInfoDictionaryVersion</key>
 | 
						<key>CFBundleInfoDictionaryVersion</key>
 | 
				
			||||||
	<string>6.0</string>
 | 
						<string>6.0</string>
 | 
				
			||||||
	<key>CFBundleName</key>
 | 
						<key>CFBundleName</key>
 | 
				
			||||||
	<string>fast_network_navigation</string>
 | 
						<string>anyway</string>
 | 
				
			||||||
	<key>CFBundlePackageType</key>
 | 
						<key>CFBundlePackageType</key>
 | 
				
			||||||
	<string>APPL</string>
 | 
						<string>APPL</string>
 | 
				
			||||||
	<key>CFBundleShortVersionString</key>
 | 
						<key>CFBundleShortVersionString</key>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										4
									
								
								frontend/lib/constants.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								frontend/lib/constants.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					const String APP_NAME = 'AnyWay';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					String API_URL_BASE = 'https://anyway.kluster.moll.re';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1,23 +1,29 @@
 | 
				
			|||||||
import 'package:fast_network_navigation/modules/trips_overview.dart';
 | 
					import 'dart:collection';
 | 
				
			||||||
import 'package:fast_network_navigation/pages/new_trip.dart';
 | 
					
 | 
				
			||||||
import 'package:fast_network_navigation/pages/tutorial.dart';
 | 
					import 'package:anyway/structs/landmark.dart';
 | 
				
			||||||
import 'package:fast_network_navigation/structs/trip.dart';
 | 
					 | 
				
			||||||
import 'package:fast_network_navigation/utils/load_trips.dart';
 | 
					 | 
				
			||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import 'package:fast_network_navigation/pages/overview.dart';
 | 
					import 'package:anyway/constants.dart';
 | 
				
			||||||
import 'package:fast_network_navigation/pages/profile.dart';
 | 
					
 | 
				
			||||||
 | 
					import 'package:anyway/structs/trip.dart';
 | 
				
			||||||
 | 
					import 'package:anyway/modules/trips_overview.dart';
 | 
				
			||||||
 | 
					import 'package:anyway/utils/load_trips.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import 'package:anyway/pages/new_trip.dart';
 | 
				
			||||||
 | 
					import 'package:anyway/pages/tutorial.dart';
 | 
				
			||||||
 | 
					import 'package:anyway/pages/overview.dart';
 | 
				
			||||||
 | 
					import 'package:anyway/pages/profile.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// BasePage is the scaffold that holds all other pages
 | 
					// BasePage is the scaffold that holds all other pages
 | 
				
			||||||
// A side drawer is used to switch between pages
 | 
					// A side drawer is used to switch between pages
 | 
				
			||||||
class BasePage extends StatefulWidget {
 | 
					class BasePage extends StatefulWidget {
 | 
				
			||||||
  final String mainScreen;
 | 
					  final String mainScreen;
 | 
				
			||||||
  final Future<Trip>? trip;
 | 
					  final Trip? trip;
 | 
				
			||||||
  
 | 
					
 | 
				
			||||||
  const BasePage({
 | 
					  const BasePage({
 | 
				
			||||||
    super.key,
 | 
					    super.key,
 | 
				
			||||||
    required this.mainScreen,
 | 
					    required this.mainScreen,
 | 
				
			||||||
    this.trip
 | 
					    this.trip,
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
@@ -43,20 +49,20 @@ class _BasePageState extends State<BasePage> {
 | 
				
			|||||||
    final ThemeData theme = Theme.of(context);
 | 
					    final ThemeData theme = Theme.of(context);
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    return Scaffold(
 | 
					    return Scaffold(
 | 
				
			||||||
      appBar: AppBar(title: Text("City Nav")),
 | 
					      appBar: AppBar(title: Text(APP_NAME)),
 | 
				
			||||||
      body: Center(child: currentView),
 | 
					      body: Center(child: currentView),
 | 
				
			||||||
      drawer: Drawer(
 | 
					      drawer: Drawer(
 | 
				
			||||||
        child: Column(
 | 
					        child: Column(
 | 
				
			||||||
          children: [
 | 
					          children: [
 | 
				
			||||||
            DrawerHeader(
 | 
					            DrawerHeader(
 | 
				
			||||||
              decoration: BoxDecoration(
 | 
					              decoration: BoxDecoration(
 | 
				
			||||||
                gradient: LinearGradient(colors: [Colors.cyan, theme.primaryColor])
 | 
					                gradient: LinearGradient(colors: [Colors.red, Colors.yellow])
 | 
				
			||||||
              ),
 | 
					              ),
 | 
				
			||||||
              child: Center(
 | 
					              child: Center(
 | 
				
			||||||
                child: Text(
 | 
					                child: Text(
 | 
				
			||||||
                  'City Nav',
 | 
					                  APP_NAME,
 | 
				
			||||||
                  style: TextStyle(
 | 
					                  style: TextStyle(
 | 
				
			||||||
                    color: Colors.white,
 | 
					                    color: Colors.grey[800],
 | 
				
			||||||
                    fontSize: 24,
 | 
					                    fontSize: 24,
 | 
				
			||||||
                    fontWeight: FontWeight.bold,
 | 
					                    fontWeight: FontWeight.bold,
 | 
				
			||||||
                  ),
 | 
					                  ),
 | 
				
			||||||
@@ -126,9 +132,71 @@ class _BasePageState extends State<BasePage> {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This function is used to get the first trip from a list of trips
 | 
				
			||||||
 | 
					// TODO: Implement this function
 | 
				
			||||||
Future<Trip> getFirstTrip (Future<List<Trip>> trips) async {
 | 
					Trip getFirstTrip(Future<List<Trip>> trips) {
 | 
				
			||||||
  List<Trip> tripsf = await trips;
 | 
					  Trip t1 = Trip(uuid: '1', landmarks: LinkedList<Landmark>());
 | 
				
			||||||
  return tripsf[0];
 | 
					  t1.landmarks.add(
 | 
				
			||||||
 | 
					    Landmark(
 | 
				
			||||||
 | 
					      uuid: '0',
 | 
				
			||||||
 | 
					      name: "Start",
 | 
				
			||||||
 | 
					      location: [48.85, 2.32],
 | 
				
			||||||
 | 
					      type: start,
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					  t1.landmarks.add(
 | 
				
			||||||
 | 
					    Landmark(
 | 
				
			||||||
 | 
					      uuid: '1',
 | 
				
			||||||
 | 
					      name: "Eiffel Tower",
 | 
				
			||||||
 | 
					      location: [48.859, 2.295],
 | 
				
			||||||
 | 
					      type: sightseeing,
 | 
				
			||||||
 | 
					      imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a8/Tour_Eiffel_Wikimedia_Commons.jpg/1037px-Tour_Eiffel_Wikimedia_Commons.jpg"
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					  t1.landmarks.add(
 | 
				
			||||||
 | 
					    Landmark(
 | 
				
			||||||
 | 
					      uuid: "2",
 | 
				
			||||||
 | 
					      name: "Notre Dame Cathedral",
 | 
				
			||||||
 | 
					      location: [48.8530, 2.3498],
 | 
				
			||||||
 | 
					      type: sightseeing,
 | 
				
			||||||
 | 
					      imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f7/Notre-Dame_de_Paris%2C_4_October_2017.jpg/440px-Notre-Dame_de_Paris%2C_4_October_2017.jpg"
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					  t1.landmarks.add(
 | 
				
			||||||
 | 
					    Landmark(
 | 
				
			||||||
 | 
					      uuid: "3",
 | 
				
			||||||
 | 
					      name: "Louvre palace",
 | 
				
			||||||
 | 
					      location: [48.8606, 2.3376],
 | 
				
			||||||
 | 
					      type: sightseeing,
 | 
				
			||||||
 | 
					      imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/6/66/Louvre_Museum_Wikimedia_Commons.jpg/540px-Louvre_Museum_Wikimedia_Commons.jpg"
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					  t1.landmarks.add(
 | 
				
			||||||
 | 
					    Landmark(
 | 
				
			||||||
 | 
					      uuid: "4",
 | 
				
			||||||
 | 
					      name: "Pont-des-arts",
 | 
				
			||||||
 | 
					      location: [48.8585, 2.3376],
 | 
				
			||||||
 | 
					      type: sightseeing,
 | 
				
			||||||
 | 
					      imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d1/Pont_des_Arts%2C_6e_Arrondissement%2C_Paris_%28HDR%29_20140320_1.jpg/560px-Pont_des_Arts%2C_6e_Arrondissement%2C_Paris_%28HDR%29_20140320_1.jpg"
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					  t1.landmarks.add(
 | 
				
			||||||
 | 
					    Landmark(
 | 
				
			||||||
 | 
					      uuid: "5",
 | 
				
			||||||
 | 
					      name: "Panthéon",
 | 
				
			||||||
 | 
					      location: [48.847, 2.347],
 | 
				
			||||||
 | 
					      type: sightseeing,
 | 
				
			||||||
 | 
					      imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/8/80/Pantheon_of_Paris_007.JPG/1280px-Pantheon_of_Paris_007.JPG"
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					  t1.landmarks.add(
 | 
				
			||||||
 | 
					    Landmark(
 | 
				
			||||||
 | 
					      uuid: "6",
 | 
				
			||||||
 | 
					      name: "Galeries Lafayette",
 | 
				
			||||||
 | 
					      location: [48.87, 2.32],
 | 
				
			||||||
 | 
					      type: shopping,
 | 
				
			||||||
 | 
					      imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/d/de/GaleriesLafayetteNuit.jpg/220px-GaleriesLafayetteNuit.jpg"
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					  return t1;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,19 +1,19 @@
 | 
				
			|||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
import 'package:fast_network_navigation/layout.dart';
 | 
					import 'package:anyway/constants.dart';
 | 
				
			||||||
 | 
					import 'package:anyway/layout.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void main() => runApp(const App());
 | 
					void main() => runApp(const App());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class App extends StatelessWidget {
 | 
					class App extends StatelessWidget {
 | 
				
			||||||
  const App({super.key});
 | 
					  const App({super.key});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static const appTitle = 'City Nav';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
    return MaterialApp(
 | 
					    return MaterialApp(
 | 
				
			||||||
      title: appTitle,
 | 
					      title: APP_NAME,
 | 
				
			||||||
      home: BasePage(mainScreen: "map"),
 | 
					      home: BasePage(mainScreen: "map"),
 | 
				
			||||||
      theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.green),
 | 
					      theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.red[600]),
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,14 +1,15 @@
 | 
				
			|||||||
import 'package:fast_network_navigation/structs/trip.dart';
 | 
					import 'dart:developer';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import 'package:anyway/structs/trip.dart';
 | 
				
			||||||
 | 
					import 'package:auto_size_text/auto_size_text.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Greeter extends StatefulWidget {
 | 
					class Greeter extends StatefulWidget {
 | 
				
			||||||
  final Future<Trip> trip;
 | 
					  final Trip trip;
 | 
				
			||||||
  final bool standalone;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Greeter({
 | 
					  Greeter({
 | 
				
			||||||
    required this.standalone,
 | 
					    required this.trip,
 | 
				
			||||||
    required this.trip
 | 
					 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
@@ -16,43 +17,91 @@ class Greeter extends StatefulWidget {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
class _GreeterState extends State<Greeter> {
 | 
					class _GreeterState extends State<Greeter> {
 | 
				
			||||||
  Widget greeterBuild (BuildContext context, AsyncSnapshot<Trip> snapshot) {
 | 
					  
 | 
				
			||||||
 | 
					  Widget greeterBuilder (BuildContext context, Widget? child) {
 | 
				
			||||||
    ThemeData theme = Theme.of(context);
 | 
					    ThemeData theme = Theme.of(context);
 | 
				
			||||||
    String cityName = "";
 | 
					    TextStyle greeterStyle = TextStyle(color: theme.primaryColor, fontWeight: FontWeight.bold, fontSize: 24);
 | 
				
			||||||
    if (snapshot.hasData) {
 | 
					 | 
				
			||||||
      cityName = snapshot.data?.cityName ?? '...';
 | 
					 | 
				
			||||||
    } else if (snapshot.hasError) {
 | 
					 | 
				
			||||||
      cityName = "error";
 | 
					 | 
				
			||||||
    } else { // still awaiting the cityname
 | 
					 | 
				
			||||||
      cityName = "...";
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Widget topGreeter = Text(
 | 
					    Widget topGreeter;
 | 
				
			||||||
      'Welcome to $cityName!',
 | 
					 | 
				
			||||||
      style: TextStyle(color: theme.primaryColor, fontWeight: FontWeight.bold, fontSize: 24),
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (widget.standalone) {
 | 
					    if (widget.trip.uuid != 'pending') {
 | 
				
			||||||
      return Center(
 | 
					      topGreeter = FutureBuilder(
 | 
				
			||||||
        child: Padding(
 | 
					        future: widget.trip.cityName,
 | 
				
			||||||
          padding: EdgeInsets.only(top: 24.0),
 | 
					        builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
 | 
				
			||||||
          child: topGreeter,
 | 
					          if (snapshot.hasData) {
 | 
				
			||||||
        ),
 | 
					            return AutoSizeText(
 | 
				
			||||||
 | 
					              maxLines: 1,
 | 
				
			||||||
 | 
					              'Welcome to ${snapshot.data}!',
 | 
				
			||||||
 | 
					              style: greeterStyle
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					          } else if (snapshot.hasError) {
 | 
				
			||||||
 | 
					            log('Error while fetching city name');
 | 
				
			||||||
 | 
					            return AutoSizeText(
 | 
				
			||||||
 | 
					              maxLines: 1,
 | 
				
			||||||
 | 
					              'Welcome to your trip!',
 | 
				
			||||||
 | 
					              style: greeterStyle
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            return AutoSizeText(
 | 
				
			||||||
 | 
					              maxLines: 1,
 | 
				
			||||||
 | 
					              'Welcome to ...',
 | 
				
			||||||
 | 
					              style: greeterStyle
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      return Center(
 | 
					      // still awaiting the trip
 | 
				
			||||||
        child: Column(
 | 
					      // We can hopefully infer the city name from the cityName future
 | 
				
			||||||
          children: [
 | 
					      // Show a linear loader at the bottom and an info message above
 | 
				
			||||||
            Padding(padding: EdgeInsets.only(top: 24.0)),
 | 
					      topGreeter = Column(
 | 
				
			||||||
            topGreeter,
 | 
					        mainAxisAlignment: MainAxisAlignment.end,
 | 
				
			||||||
            bottomGreeter,
 | 
					        children: [
 | 
				
			||||||
            Padding(padding: EdgeInsets.only(bottom: 24.0)),
 | 
					          FutureBuilder(
 | 
				
			||||||
          ],
 | 
					            future: widget.trip.cityName,
 | 
				
			||||||
        )
 | 
					            builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
 | 
				
			||||||
 | 
					              if (snapshot.hasData) {
 | 
				
			||||||
 | 
					                return AutoSizeText(
 | 
				
			||||||
 | 
					                  maxLines: 1,
 | 
				
			||||||
 | 
					                  'Generating your trip to ${snapshot.data}...',
 | 
				
			||||||
 | 
					                  style: greeterStyle
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					              } else if (snapshot.hasError) {
 | 
				
			||||||
 | 
					                // the exact error is shown in the central part of the trip overview. No need to show it here
 | 
				
			||||||
 | 
					                return AutoSizeText(
 | 
				
			||||||
 | 
					                  maxLines: 1,
 | 
				
			||||||
 | 
					                  'Error while loading trip.',
 | 
				
			||||||
 | 
					                  style: greeterStyle
 | 
				
			||||||
 | 
					                  );
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					              return AutoSizeText(
 | 
				
			||||||
 | 
					                  maxLines: 1,
 | 
				
			||||||
 | 
					                  'Generating your trip...',
 | 
				
			||||||
 | 
					                  style: greeterStyle
 | 
				
			||||||
 | 
					                  );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					          Padding(
 | 
				
			||||||
 | 
					            padding: EdgeInsets.all(5),
 | 
				
			||||||
 | 
					            child: const LinearProgressIndicator()
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return Center(
 | 
				
			||||||
 | 
					      child: Column(
 | 
				
			||||||
 | 
					        children: [
 | 
				
			||||||
 | 
					          // Padding(padding: EdgeInsets.only(top: 20)),
 | 
				
			||||||
 | 
					          topGreeter,
 | 
				
			||||||
 | 
					          Padding(
 | 
				
			||||||
 | 
					            padding: EdgeInsets.all(20),
 | 
				
			||||||
 | 
					            child: bottomGreeter
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Widget bottomGreeter = const Text(
 | 
					  Widget bottomGreeter = const Text(
 | 
				
			||||||
@@ -65,9 +114,9 @@ class _GreeterState extends State<Greeter> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
    return FutureBuilder(
 | 
					    return ListenableBuilder(
 | 
				
			||||||
      future: widget.trip,
 | 
					      listenable: widget.trip,
 | 
				
			||||||
      builder: greeterBuild,
 | 
					      builder: greeterBuilder,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
import 'package:fast_network_navigation/structs/landmark.dart';
 | 
					import 'package:anyway/structs/landmark.dart';
 | 
				
			||||||
 | 
					import 'package:cached_network_image/cached_network_image.dart';
 | 
				
			||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -31,9 +32,10 @@ class _LandmarkCardState extends State<LandmarkCard> {
 | 
				
			|||||||
              height: double.infinity,
 | 
					              height: double.infinity,
 | 
				
			||||||
              // force a fixed width
 | 
					              // force a fixed width
 | 
				
			||||||
              width: 160,
 | 
					              width: 160,
 | 
				
			||||||
              child: Image.network(
 | 
					              child: CachedNetworkImage(
 | 
				
			||||||
                widget.landmark.imageURL ?? '',
 | 
					                imageUrl: widget.landmark.imageURL ?? '',
 | 
				
			||||||
                errorBuilder: (context, error, stackTrace) => Icon(Icons.question_mark_outlined),
 | 
					                placeholder: (context, url) => CircularProgressIndicator(),
 | 
				
			||||||
 | 
					                errorWidget: (context, error, stackTrace) => Icon(Icons.question_mark_outlined),
 | 
				
			||||||
                // TODO: make this a switch statement to load a placeholder if null
 | 
					                // TODO: make this a switch statement to load a placeholder if null
 | 
				
			||||||
                // cover the whole container meaning the image will be cropped
 | 
					                // cover the whole container meaning the image will be cropped
 | 
				
			||||||
                fit: BoxFit.cover,
 | 
					                fit: BoxFit.cover,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,17 +1,16 @@
 | 
				
			|||||||
import 'dart:collection';
 | 
					import 'dart:developer';
 | 
				
			||||||
 | 
					 | 
				
			||||||
import 'package:fast_network_navigation/modules/landmark_card.dart';
 | 
					 | 
				
			||||||
import 'package:fast_network_navigation/structs/landmark.dart';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import 'package:fast_network_navigation/structs/trip.dart';
 | 
					 | 
				
			||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
import 'package:shared_preferences/shared_preferences.dart';
 | 
					import 'package:shared_preferences/shared_preferences.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import 'package:anyway/modules/landmark_card.dart';
 | 
				
			||||||
 | 
					import 'package:anyway/structs/landmark.dart';
 | 
				
			||||||
 | 
					import 'package:anyway/structs/trip.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LandmarksOverview extends StatefulWidget {
 | 
					class LandmarksOverview extends StatefulWidget {
 | 
				
			||||||
  final Future<Trip>? trip;
 | 
					  final Trip? trip;
 | 
				
			||||||
  const LandmarksOverview({super.key, this.trip});
 | 
					  const LandmarksOverview({super.key, this.trip});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
@@ -19,22 +18,37 @@ class LandmarksOverview extends StatefulWidget {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class _LandmarksOverviewState extends State<LandmarksOverview> {
 | 
					class _LandmarksOverviewState extends State<LandmarksOverview> {
 | 
				
			||||||
  // final Future<List<Landmark>> _landmarks = fetchLandmarks();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
    final Future<LinkedList<Landmark>> _landmarks = getLandmarks(widget.trip);
 | 
					    return ListenableBuilder(
 | 
				
			||||||
    return DefaultTextStyle(
 | 
					      listenable: widget.trip!,
 | 
				
			||||||
      style: Theme.of(context).textTheme.displayMedium!,
 | 
					      builder: (BuildContext context, Widget? child) {
 | 
				
			||||||
      textAlign: TextAlign.center,
 | 
					        Trip trip = widget.trip!;
 | 
				
			||||||
      child: FutureBuilder<LinkedList<Landmark>>(
 | 
					        log("Trip ${trip.uuid} ${trip.landmarks.length} landmarks");
 | 
				
			||||||
        future: _landmarks,
 | 
					        
 | 
				
			||||||
        builder: (BuildContext context, AsyncSnapshot<LinkedList<Landmark>> snapshot) {
 | 
					        List<Widget> children;
 | 
				
			||||||
          List<Widget> children;
 | 
					        
 | 
				
			||||||
          if (snapshot.hasData) {
 | 
					        if (trip.uuid != 'pending' && trip.uuid != 'error') {
 | 
				
			||||||
            children = [landmarksWithSteps(snapshot.data!), saveButton()];
 | 
					          log("Trip ${trip.uuid} ${trip.landmarks.length} landmarks");
 | 
				
			||||||
          } else if (snapshot.hasError) {
 | 
					          if (trip.landmarks.length <= 1) {
 | 
				
			||||||
            children = <Widget>[
 | 
					            children = [
 | 
				
			||||||
 | 
					              const Text("No landmarks in this trip"),
 | 
				
			||||||
 | 
					            ];
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            children = [
 | 
				
			||||||
 | 
					              landmarksWithSteps(),
 | 
				
			||||||
 | 
					              saveButton(),
 | 
				
			||||||
 | 
					            ];
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        } else if(trip.uuid == 'pending') {
 | 
				
			||||||
 | 
					          // the trip is still being fetched from the api
 | 
				
			||||||
 | 
					          children = [Center(child: CircularProgressIndicator())];
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // trip.uuid == 'error'
 | 
				
			||||||
 | 
					            // show the error raised by the api
 | 
				
			||||||
 | 
					            // String error = 
 | 
				
			||||||
 | 
					            children = [
 | 
				
			||||||
              const Icon(
 | 
					              const Icon(
 | 
				
			||||||
                Icons.error_outline,
 | 
					                Icons.error_outline,
 | 
				
			||||||
                color: Colors.red,
 | 
					                color: Colors.red,
 | 
				
			||||||
@@ -42,20 +56,15 @@ class _LandmarksOverviewState extends State<LandmarksOverview> {
 | 
				
			|||||||
              ),
 | 
					              ),
 | 
				
			||||||
              Padding(
 | 
					              Padding(
 | 
				
			||||||
                padding: const EdgeInsets.only(top: 16),
 | 
					                padding: const EdgeInsets.only(top: 16),
 | 
				
			||||||
                child: Text('Error: ${snapshot.error}', style: TextStyle(fontSize: 12)),
 | 
					                child: Text('Error: ${trip.errorDescription}'),
 | 
				
			||||||
              ),
 | 
					              ),
 | 
				
			||||||
            ];
 | 
					            ];
 | 
				
			||||||
          } else {
 | 
					        }
 | 
				
			||||||
            children = [Center(child: CircularProgressIndicator())];
 | 
					
 | 
				
			||||||
          }
 | 
					        return Column(
 | 
				
			||||||
          return Center(
 | 
					          children: children,
 | 
				
			||||||
            child: Column(
 | 
					        );
 | 
				
			||||||
              mainAxisAlignment: MainAxisAlignment.center,
 | 
					      },
 | 
				
			||||||
              children: children,
 | 
					 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
          );
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      ),
 | 
					 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  Widget saveButton() => ElevatedButton(
 | 
					  Widget saveButton() => ElevatedButton(
 | 
				
			||||||
@@ -67,29 +76,56 @@ class _LandmarksOverviewState extends State<LandmarksOverview> {
 | 
				
			|||||||
    child: const Text('Save'),
 | 
					    child: const Text('Save'),
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					  Widget landmarksWithSteps() {
 | 
				
			||||||
 | 
					    return ListenableBuilder(
 | 
				
			||||||
Widget landmarksWithSteps(LinkedList<Landmark> landmarks) {
 | 
					      listenable: widget.trip!,
 | 
				
			||||||
  List<Widget> children = [];
 | 
					      builder: (BuildContext context, Widget? child) {
 | 
				
			||||||
  for (Landmark landmark in landmarks) {
 | 
					        List<Widget> children = [];
 | 
				
			||||||
    children.add(LandmarkCard(landmark));
 | 
					        for (Landmark landmark in widget.trip!.landmarks) {
 | 
				
			||||||
    if (landmark.next != null) {
 | 
					          children.add(
 | 
				
			||||||
      Widget step = stepBetweenLandmarks(landmark, landmark.next!);
 | 
					            Dismissible(
 | 
				
			||||||
      children.add(step);
 | 
					              key: ValueKey<int>(landmark.hashCode),
 | 
				
			||||||
    }
 | 
					              child: LandmarkCard(landmark),
 | 
				
			||||||
 | 
					              dismissThresholds: {DismissDirection.endToStart: 0.6},
 | 
				
			||||||
 | 
					              onDismissed: (direction) {
 | 
				
			||||||
 | 
					                // Remove the item from the data source.
 | 
				
			||||||
 | 
					                  log(landmark.name);
 | 
				
			||||||
 | 
					                setState(() {
 | 
				
			||||||
 | 
					                  widget.trip!.removeLandmark(landmark);
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					                // Then show a snackbar.
 | 
				
			||||||
 | 
					                ScaffoldMessenger.of(context)
 | 
				
			||||||
 | 
					                    .showSnackBar(SnackBar(content: Text("We won't show ${landmark.name} again")));
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					              background: Container(color: Colors.red),
 | 
				
			||||||
 | 
					              secondaryBackground: Container(
 | 
				
			||||||
 | 
					                color: Colors.red,
 | 
				
			||||||
 | 
					                child: Icon(
 | 
				
			||||||
 | 
					                  Icons.delete,
 | 
				
			||||||
 | 
					                  color: Colors.white,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                padding: EdgeInsets.all(15),
 | 
				
			||||||
 | 
					                alignment: Alignment.centerRight,
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					          if (landmark.next != null) {
 | 
				
			||||||
 | 
					            Widget step = stepBetweenLandmarks(landmark, landmark.next!);
 | 
				
			||||||
 | 
					            children.add(step);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return Column(
 | 
				
			||||||
 | 
					          children: children  
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  return Column(
 | 
					 | 
				
			||||||
    children: children
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Widget stepBetweenLandmarks(Landmark before, Landmark after) {
 | 
					Widget stepBetweenLandmarks(Landmark current, Landmark next) {
 | 
				
			||||||
  // This is a simple widget that draws a line between landmark-cards
 | 
					  int timeRounded = 5 * (current.tripTime?.inMinutes ?? 0) ~/ 5;
 | 
				
			||||||
  // It's a vertical dotted line
 | 
					  // ~/ is integer division (rounding)
 | 
				
			||||||
  // Next to the line is the icon for the mode of transport (walking for now) and the estimated time
 | 
					 | 
				
			||||||
  // There is also a button to open the navigation instructions as a new intent
 | 
					 | 
				
			||||||
  return Container(
 | 
					  return Container(
 | 
				
			||||||
    margin: EdgeInsets.all(10),
 | 
					    margin: EdgeInsets.all(10),
 | 
				
			||||||
    padding: EdgeInsets.all(10),
 | 
					    padding: EdgeInsets.all(10),
 | 
				
			||||||
@@ -108,7 +144,7 @@ Widget stepBetweenLandmarks(Landmark before, Landmark after) {
 | 
				
			|||||||
        Column(
 | 
					        Column(
 | 
				
			||||||
          children: [
 | 
					          children: [
 | 
				
			||||||
            Icon(Icons.directions_walk),
 | 
					            Icon(Icons.directions_walk),
 | 
				
			||||||
            Text("5 min", style: TextStyle(fontSize: 10)),
 | 
					            Text("~$timeRounded min", style: TextStyle(fontSize: 10)),
 | 
				
			||||||
          ],
 | 
					          ],
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
        Spacer(),
 | 
					        Spacer(),
 | 
				
			||||||
@@ -116,15 +152,17 @@ Widget stepBetweenLandmarks(Landmark before, Landmark after) {
 | 
				
			|||||||
          onPressed: () {
 | 
					          onPressed: () {
 | 
				
			||||||
            // Open navigation instructions
 | 
					            // Open navigation instructions
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          child: Text("Navigate"),
 | 
					          child: Row(
 | 
				
			||||||
        ),
 | 
					            children: [
 | 
				
			||||||
 | 
					              Icon(Icons.directions),
 | 
				
			||||||
 | 
					              Text("Directions"),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
      ],
 | 
					      ],
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Future<LinkedList<Landmark>> getLandmarks (Future<Trip>? trip) async {
 | 
					
 | 
				
			||||||
  Trip tripf = await trip!;
 | 
					 | 
				
			||||||
  return tripf.landmarks;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,13 +1,16 @@
 | 
				
			|||||||
import 'dart:collection';
 | 
					import 'dart:collection';
 | 
				
			||||||
 | 
					import 'dart:developer';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import 'package:fast_network_navigation/structs/landmark.dart';
 | 
					 | 
				
			||||||
import 'package:fast_network_navigation/structs/trip.dart';
 | 
					 | 
				
			||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					import 'package:anyway/structs/landmark.dart';
 | 
				
			||||||
 | 
					import 'package:anyway/structs/trip.dart';
 | 
				
			||||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
 | 
					import 'package:google_maps_flutter/google_maps_flutter.dart';
 | 
				
			||||||
 | 
					import 'package:widget_to_marker/widget_to_marker.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MapWidget extends StatefulWidget {
 | 
					class MapWidget extends StatefulWidget {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  final Future<Trip>? trip;
 | 
					  final Trip? trip;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  MapWidget({
 | 
					  MapWidget({
 | 
				
			||||||
    this.trip
 | 
					    this.trip
 | 
				
			||||||
@@ -19,58 +22,130 @@ class MapWidget extends StatefulWidget {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class _MapWidgetState extends State<MapWidget> {
 | 
					class _MapWidgetState extends State<MapWidget> {
 | 
				
			||||||
  late GoogleMapController mapController;
 | 
					  late GoogleMapController mapController;
 | 
				
			||||||
  // coordinates of Paris
 | 
					
 | 
				
			||||||
  CameraPosition _cameraPosition = CameraPosition(
 | 
					  CameraPosition _cameraPosition = CameraPosition(
 | 
				
			||||||
    target: LatLng(48.8566, 2.3522),
 | 
					    target: LatLng(48.8566, 2.3522),
 | 
				
			||||||
    zoom: 11.0,
 | 
					    zoom: 11.0,
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
  Set<Marker> markers = <Marker>{};
 | 
					  Set<Marker> mapMarkers = <Marker>{};
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void _onMapCreated(GoogleMapController controller) async {
 | 
					  void _onMapCreated(GoogleMapController controller) async {
 | 
				
			||||||
    mapController = controller;
 | 
					    mapController = controller;
 | 
				
			||||||
    Trip? trip = await widget.trip;
 | 
					    List<double>? newLocation = widget.trip?.landmarks.firstOrNull?.location;
 | 
				
			||||||
    List<double>? newLocation = trip?.landmarks.first.location;
 | 
					 | 
				
			||||||
    if (newLocation != null) {
 | 
					    if (newLocation != null) {
 | 
				
			||||||
      CameraUpdate update = CameraUpdate.newLatLng(LatLng(newLocation[0], newLocation[1]));
 | 
					      CameraUpdate update = CameraUpdate.newLatLng(LatLng(newLocation[0], newLocation[1]));
 | 
				
			||||||
      controller.moveCamera(update);
 | 
					      controller.moveCamera(update);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    drawLandmarks();
 | 
					    setMapMarkers();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
  void _onCameraIdle() {
 | 
					  void _onCameraIdle() {
 | 
				
			||||||
    // print(mapController.getLatLng(ScreenCoordinate(x: 0, y: 0)));
 | 
					    // print(mapController.getLatLng(ScreenCoordinate(x: 0, y: 0)));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void drawLandmarks() async {
 | 
					  void setMapMarkers() async {
 | 
				
			||||||
    // (re)draws landmarks on the map
 | 
					    List<Landmark> landmarks = widget.trip?.landmarks.toList() ?? [];
 | 
				
			||||||
    Trip? trip = await widget.trip;
 | 
					    Set<Marker> newMarkers = <Marker>{};
 | 
				
			||||||
    LinkedList<Landmark>? landmarks = trip?.landmarks;
 | 
					    for (int i = 0; i < landmarks.length; i++) {
 | 
				
			||||||
    if (landmarks != null){
 | 
					      Landmark landmark = landmarks[i];
 | 
				
			||||||
      setState(() {
 | 
					      List<double> location = landmark.location;
 | 
				
			||||||
        for (Landmark landmark in landmarks) {
 | 
					      Marker marker = Marker(
 | 
				
			||||||
          markers.add(Marker(
 | 
					        markerId: MarkerId(landmark.uuid),
 | 
				
			||||||
            markerId: MarkerId(landmark.name),
 | 
					        position: LatLng(location[0], location[1]),
 | 
				
			||||||
            position: LatLng(landmark.location[0], landmark.location[1]),
 | 
					        icon: await CustomMarker(landmark: landmark, position: i).toBitmapDescriptor(
 | 
				
			||||||
            infoWindow: InfoWindow(title: landmark.name, snippet: landmark.type.name),
 | 
					          logicalSize: const Size(150, 150),
 | 
				
			||||||
          ));
 | 
					          imageSize: const Size(150, 150)
 | 
				
			||||||
        }
 | 
					        ),
 | 
				
			||||||
      });
 | 
					      );
 | 
				
			||||||
 | 
					      newMarkers.add(marker);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    setState(() {
 | 
				
			||||||
 | 
					      mapMarkers = newMarkers;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
 | 
					    widget.trip?.addListener(setMapMarkers);
 | 
				
			||||||
    return GoogleMap(
 | 
					    return GoogleMap(
 | 
				
			||||||
      onMapCreated: _onMapCreated,
 | 
					      onMapCreated: _onMapCreated,
 | 
				
			||||||
      initialCameraPosition: _cameraPosition,
 | 
					      initialCameraPosition: _cameraPosition,
 | 
				
			||||||
      onCameraIdle: _onCameraIdle,
 | 
					      onCameraIdle: _onCameraIdle,
 | 
				
			||||||
      // onLongPress: ,
 | 
					      // onLongPress: ,
 | 
				
			||||||
      markers: markers,
 | 
					      markers: mapMarkers,
 | 
				
			||||||
      cloudMapId: '41c21ac9b81dbfd8',
 | 
					      cloudMapId: '41c21ac9b81dbfd8',
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CustomMarker extends StatelessWidget {
 | 
				
			||||||
 | 
					  final Landmark landmark;
 | 
				
			||||||
 | 
					  final int position;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  CustomMarker({
 | 
				
			||||||
 | 
					    super.key,
 | 
				
			||||||
 | 
					    required this.landmark,
 | 
				
			||||||
 | 
					    required this.position
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
 | 
					    // This returns an outlined circle, with an icon corresponding to the landmark type
 | 
				
			||||||
 | 
					    // As a small dot, the number of the landmark is displayed in the top right
 | 
				
			||||||
 | 
					    Icon icon;
 | 
				
			||||||
 | 
					    if (landmark.type == sightseeing) {
 | 
				
			||||||
 | 
					      icon = Icon(Icons.church, color: Colors.black, size: 50);
 | 
				
			||||||
 | 
					    } else if (landmark.type == nature) {
 | 
				
			||||||
 | 
					      icon = Icon(Icons.park, color: Colors.black, size: 50);
 | 
				
			||||||
 | 
					    } else if (landmark.type == shopping) {
 | 
				
			||||||
 | 
					      icon = Icon(Icons.shopping_cart, color: Colors.black, size: 50);
 | 
				
			||||||
 | 
					    } else if (landmark.type == start || landmark.type == finish) {
 | 
				
			||||||
 | 
					      icon = Icon(Icons.flag, color: Colors.black, size: 50);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      icon = Icon(Icons.location_on, color: Colors.black, size: 50);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Widget? positionIndicator;
 | 
				
			||||||
 | 
					    if (landmark.type != start && landmark.type != finish) {
 | 
				
			||||||
 | 
					      positionIndicator = Positioned(
 | 
				
			||||||
 | 
					        top: 0,
 | 
				
			||||||
 | 
					        right: 0,
 | 
				
			||||||
 | 
					        child: Container(
 | 
				
			||||||
 | 
					          padding: EdgeInsets.all(5),
 | 
				
			||||||
 | 
					          decoration: BoxDecoration(
 | 
				
			||||||
 | 
					            color: Theme.of(context).primaryColor,
 | 
				
			||||||
 | 
					            shape: BoxShape.circle,
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					          child: Text('$position', style: TextStyle(color: Colors.white, fontSize: 20)),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return RepaintBoundary(
 | 
				
			||||||
 | 
					      child: Stack(
 | 
				
			||||||
 | 
					        children: [
 | 
				
			||||||
 | 
					          Container(
 | 
				
			||||||
 | 
					            // these are not the final sizes, since the final size is set in the toBitmapDescriptor method
 | 
				
			||||||
 | 
					            // they are useful nevertheless to ensure the scale of the components are correct
 | 
				
			||||||
 | 
					            width: 75,
 | 
				
			||||||
 | 
					            height: 75,
 | 
				
			||||||
 | 
					            decoration: BoxDecoration(
 | 
				
			||||||
 | 
					              gradient: LinearGradient(
 | 
				
			||||||
 | 
					                begin: Alignment.topLeft,
 | 
				
			||||||
 | 
					                end: Alignment.bottomRight,
 | 
				
			||||||
 | 
					                colors: [Colors.red, Colors.yellow]
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					              shape: BoxShape.circle,
 | 
				
			||||||
 | 
					              border: Border.all(color: Colors.black, width: 5),
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            child: icon,
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					          positionIndicator ?? Container(),
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import 'package:fast_network_navigation/layout.dart';
 | 
					import 'package:anyway/layout.dart';
 | 
				
			||||||
import 'package:fast_network_navigation/structs/trip.dart';
 | 
					import 'package:anyway/structs/trip.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TripsOverview extends StatefulWidget {
 | 
					class TripsOverview extends StatefulWidget {
 | 
				
			||||||
@@ -25,12 +25,23 @@ class _TripsOverviewState extends State<TripsOverview> {
 | 
				
			|||||||
      children = List<Widget>.generate(snapshot.data!.length, (index) {
 | 
					      children = List<Widget>.generate(snapshot.data!.length, (index) {
 | 
				
			||||||
        Trip trip = snapshot.data![index];
 | 
					        Trip trip = snapshot.data![index];
 | 
				
			||||||
        return ListTile(
 | 
					        return ListTile(
 | 
				
			||||||
          title: Text("Trip to ${trip.cityName}"),
 | 
					          title: FutureBuilder(
 | 
				
			||||||
 | 
					            future: trip.cityName,
 | 
				
			||||||
 | 
					            builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
 | 
				
			||||||
 | 
					              if (snapshot.hasData) {
 | 
				
			||||||
 | 
					                return Text("Trip to ${snapshot.data}");
 | 
				
			||||||
 | 
					              } else if (snapshot.hasError) {
 | 
				
			||||||
 | 
					                return Text("Error: ${snapshot.error}");
 | 
				
			||||||
 | 
					              } else {
 | 
				
			||||||
 | 
					                return const Text("Trip to ...");
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
          leading: Icon(Icons.pin_drop),
 | 
					          leading: Icon(Icons.pin_drop),
 | 
				
			||||||
          onTap: () {
 | 
					          onTap: () {
 | 
				
			||||||
            Navigator.of(context).push(
 | 
					            Navigator.of(context).push(
 | 
				
			||||||
              MaterialPageRoute(
 | 
					              MaterialPageRoute(
 | 
				
			||||||
                builder: (context) => BasePage(mainScreen: "map", trip: Future.value(trip))
 | 
					                builder: (context) => BasePage(mainScreen: "map", trip: trip)
 | 
				
			||||||
              )
 | 
					              )
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,13 @@
 | 
				
			|||||||
 | 
					import 'package:anyway/structs/landmark.dart';
 | 
				
			||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					import 'package:geocoding/geocoding.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import 'package:anyway/layout.dart';
 | 
				
			||||||
 | 
					import 'package:anyway/utils/fetch_trip.dart';
 | 
				
			||||||
 | 
					import 'package:anyway/structs/preferences.dart';
 | 
				
			||||||
 | 
					import "package:anyway/structs/trip.dart";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class NewTripPage extends StatefulWidget {
 | 
					class NewTripPage extends StatefulWidget {
 | 
				
			||||||
  const NewTripPage({Key? key}) : super(key: key);
 | 
					  const NewTripPage({Key? key}) : super(key: key);
 | 
				
			||||||
@@ -9,22 +17,77 @@ class NewTripPage extends StatefulWidget {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class _NewTripPageState extends State<NewTripPage> {
 | 
					class _NewTripPageState extends State<NewTripPage> {
 | 
				
			||||||
 | 
					  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
 | 
				
			||||||
 | 
					  final TextEditingController latController = TextEditingController();
 | 
				
			||||||
 | 
					  final TextEditingController lonController = TextEditingController();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
    return Scaffold(
 | 
					    return Scaffold(
 | 
				
			||||||
      appBar: AppBar(
 | 
					      appBar: AppBar(
 | 
				
			||||||
        title: const Text('New Trip'),
 | 
					        title: const Text('New Trip'),
 | 
				
			||||||
      ),
 | 
					      ),
 | 
				
			||||||
      body: Center(
 | 
					      body: Form(
 | 
				
			||||||
        child: Column(
 | 
					        key: _formKey,
 | 
				
			||||||
          mainAxisAlignment: MainAxisAlignment.center,
 | 
					        child: Padding(
 | 
				
			||||||
          children: <Widget>[
 | 
					          padding: const EdgeInsets.all(15.0),
 | 
				
			||||||
            const Text(
 | 
					          child: Column(
 | 
				
			||||||
              'Create a new trip',
 | 
					            crossAxisAlignment: CrossAxisAlignment.start,
 | 
				
			||||||
            ),
 | 
					            
 | 
				
			||||||
          ],
 | 
					            children: <Widget>[
 | 
				
			||||||
        ),
 | 
					              TextFormField(
 | 
				
			||||||
      ),
 | 
					                decoration: const InputDecoration(hintText: 'Lat'),
 | 
				
			||||||
 | 
					                controller: latController,
 | 
				
			||||||
 | 
					                validator: (String? value) {
 | 
				
			||||||
 | 
					                  if (value == null || value.isEmpty || double.tryParse(value) == null){
 | 
				
			||||||
 | 
					                    return 'Please enter a floating point number';
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                  return null;
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					              TextFormField(
 | 
				
			||||||
 | 
					                decoration: const InputDecoration(hintText: 'Lon'),
 | 
				
			||||||
 | 
					                controller: lonController,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                validator: (String? value) {
 | 
				
			||||||
 | 
					                  if (value == null || value.isEmpty || double.tryParse(value) == null){
 | 
				
			||||||
 | 
					                    return 'Please enter a floating point number';
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                  return null;
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					              Divider(height: 15, color: Colors.transparent),
 | 
				
			||||||
 | 
					              ElevatedButton(
 | 
				
			||||||
 | 
					                child: const Text('Create trip'),
 | 
				
			||||||
 | 
					                onPressed: () {
 | 
				
			||||||
 | 
					                  if (_formKey.currentState!.validate()) {
 | 
				
			||||||
 | 
					                    List<double> startPoint = [
 | 
				
			||||||
 | 
					                      double.parse(latController.text),
 | 
				
			||||||
 | 
					                      double.parse(lonController.text)
 | 
				
			||||||
 | 
					                    ];
 | 
				
			||||||
 | 
					                    Future<UserPreferences> preferences = loadUserPreferences();
 | 
				
			||||||
 | 
					                    Trip trip = Trip();
 | 
				
			||||||
 | 
					                    trip.landmarks.add(
 | 
				
			||||||
 | 
					                      Landmark(
 | 
				
			||||||
 | 
					                        location: startPoint,
 | 
				
			||||||
 | 
					                        name: "Start",
 | 
				
			||||||
 | 
					                        type: start,
 | 
				
			||||||
 | 
					                        uuid: "pending"
 | 
				
			||||||
 | 
					                      )
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                    fetchTrip(trip, preferences);
 | 
				
			||||||
 | 
					                    Navigator.of(context).push(
 | 
				
			||||||
 | 
					                      MaterialPageRoute(
 | 
				
			||||||
 | 
					                        builder: (context) => BasePage(mainScreen: "map", trip: trip)
 | 
				
			||||||
 | 
					                      )
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,19 +1,19 @@
 | 
				
			|||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
import 'package:sliding_up_panel/sliding_up_panel.dart';
 | 
					import 'package:sliding_up_panel/sliding_up_panel.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import 'package:fast_network_navigation/structs/trip.dart';
 | 
					import 'package:anyway/structs/trip.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import 'package:fast_network_navigation/modules/landmarks_overview.dart';
 | 
					import 'package:anyway/modules/landmarks_overview.dart';
 | 
				
			||||||
import 'package:fast_network_navigation/modules/map.dart';
 | 
					import 'package:anyway/modules/map.dart';
 | 
				
			||||||
import 'package:fast_network_navigation/modules/greeter.dart';
 | 
					import 'package:anyway/modules/greeter.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class NavigationOverview extends StatefulWidget {
 | 
					class NavigationOverview extends StatefulWidget {
 | 
				
			||||||
  final Future<Trip> trip;
 | 
					  final Trip trip;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  NavigationOverview({
 | 
					  NavigationOverview({
 | 
				
			||||||
    required this.trip
 | 
					    required this.trip,
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
@@ -27,53 +27,56 @@ class _NavigationOverviewState extends State<NavigationOverview> {
 | 
				
			|||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
    return SlidingUpPanel(
 | 
					    return SlidingUpPanel(
 | 
				
			||||||
        renderPanelSheet: false,
 | 
					 | 
				
			||||||
        panel: _floatingPanel(),
 | 
					        panel: _floatingPanel(),
 | 
				
			||||||
        collapsed: _floatingCollapsed(),
 | 
					        // collapsed: _floatingCollapsed(),
 | 
				
			||||||
        body: MapWidget(trip: widget.trip)
 | 
					        body: MapWidget(trip: widget.trip),
 | 
				
			||||||
 | 
					        // renderPanelSheet: false,
 | 
				
			||||||
 | 
					        // backdropEnabled: true,
 | 
				
			||||||
 | 
					        maxHeight: MediaQuery.of(context).size.height * 0.8,
 | 
				
			||||||
 | 
					        padding: EdgeInsets.all(10),
 | 
				
			||||||
 | 
					        // panelSnapping: false,
 | 
				
			||||||
 | 
					        borderRadius: BorderRadius.only(topLeft: Radius.circular(25), topRight: Radius.circular(25)),
 | 
				
			||||||
 | 
					        boxShadow: [
 | 
				
			||||||
 | 
					          BoxShadow(
 | 
				
			||||||
 | 
					            blurRadius: 20.0,
 | 
				
			||||||
 | 
					            color: Colors.black,
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Widget _floatingCollapsed(){
 | 
					  Widget _floatingCollapsed(){
 | 
				
			||||||
    final ThemeData theme = Theme.of(context);
 | 
					    return Greeter(
 | 
				
			||||||
    return Container(
 | 
					      trip: widget.trip
 | 
				
			||||||
      decoration: BoxDecoration(
 | 
					 | 
				
			||||||
        color: theme.canvasColor,
 | 
					 | 
				
			||||||
        borderRadius: BorderRadius.only(topLeft: Radius.circular(24.0), topRight: Radius.circular(24.0)),
 | 
					 | 
				
			||||||
        boxShadow: []
 | 
					 | 
				
			||||||
      ),
 | 
					 | 
				
			||||||
      
 | 
					 | 
				
			||||||
      child: Greeter(standalone: true, trip: widget.trip)
 | 
					 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Widget _floatingPanel(){
 | 
					  Widget _floatingPanel(){
 | 
				
			||||||
    final ThemeData theme = Theme.of(context);
 | 
					    return Column(
 | 
				
			||||||
    return Container(
 | 
					      children: [
 | 
				
			||||||
      decoration: BoxDecoration(
 | 
					        Padding(
 | 
				
			||||||
        color: Colors.white,
 | 
					          padding: const EdgeInsets.all(15),
 | 
				
			||||||
        borderRadius: BorderRadius.all(Radius.circular(24.0)),
 | 
					          child: 
 | 
				
			||||||
        boxShadow: [
 | 
					            Center(
 | 
				
			||||||
          BoxShadow(
 | 
					              child: Container(
 | 
				
			||||||
            blurRadius: 20.0,
 | 
					                width: 40,
 | 
				
			||||||
            color: theme.shadowColor,
 | 
					                height: 5,
 | 
				
			||||||
          ),
 | 
					                decoration: BoxDecoration(
 | 
				
			||||||
        ]
 | 
					                  color: Colors.grey[300],
 | 
				
			||||||
      ),
 | 
					                  borderRadius: BorderRadius.all(Radius.circular(12.0)),
 | 
				
			||||||
      child: Center(
 | 
					                ),
 | 
				
			||||||
        child: Padding(
 | 
					              ),
 | 
				
			||||||
        padding: EdgeInsets.all(8.0),
 | 
					            ),
 | 
				
			||||||
        child: SingleChildScrollView(
 | 
					 | 
				
			||||||
          child: Column(
 | 
					 | 
				
			||||||
            children: <Widget>[
 | 
					 | 
				
			||||||
              Greeter(standalone: false, trip: widget.trip),
 | 
					 | 
				
			||||||
              LandmarksOverview(trip: widget.trip),
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
          ),
 | 
					 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
      ),
 | 
					        Expanded(
 | 
				
			||||||
    ),
 | 
					          child: ListView(
 | 
				
			||||||
 | 
					            children: [
 | 
				
			||||||
 | 
					              Greeter(trip: widget.trip),
 | 
				
			||||||
 | 
					              LandmarksOverview(trip: widget.trip)
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,9 @@
 | 
				
			|||||||
import 'package:fast_network_navigation/structs/preferences.dart';
 | 
					import 'package:anyway/constants.dart';
 | 
				
			||||||
 | 
					import 'package:anyway/structs/preferences.dart';
 | 
				
			||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool debugMode = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ProfilePage extends StatefulWidget {
 | 
					class ProfilePage extends StatefulWidget {
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
@@ -9,6 +11,56 @@ class ProfilePage extends StatefulWidget {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class _ProfilePageState extends State<ProfilePage> {
 | 
					class _ProfilePageState extends State<ProfilePage> {
 | 
				
			||||||
 | 
					  Future<UserPreferences> _prefs = loadUserPreferences();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Widget debugButton() {
 | 
				
			||||||
 | 
					    return Padding(
 | 
				
			||||||
 | 
					      padding: EdgeInsets.only(top: 20),
 | 
				
			||||||
 | 
					      child: Row(
 | 
				
			||||||
 | 
					        children: [
 | 
				
			||||||
 | 
					          Text('Debug mode'),
 | 
				
			||||||
 | 
					          Switch(
 | 
				
			||||||
 | 
					            value: debugMode,
 | 
				
			||||||
 | 
					            onChanged: (bool? newValue) {
 | 
				
			||||||
 | 
					              setState(() {
 | 
				
			||||||
 | 
					                debugMode = newValue!;
 | 
				
			||||||
 | 
					                showDialog(
 | 
				
			||||||
 | 
					                  context: context,
 | 
				
			||||||
 | 
					                  builder: (BuildContext context) {
 | 
				
			||||||
 | 
					                    return AlertDialog(
 | 
				
			||||||
 | 
					                      title: Text('Debug mode - custom API'),
 | 
				
			||||||
 | 
					                      content: TextField(
 | 
				
			||||||
 | 
					                        decoration: InputDecoration(
 | 
				
			||||||
 | 
					                          hintText: 'http://localhost:8000'
 | 
				
			||||||
 | 
					                        ),
 | 
				
			||||||
 | 
					                      onChanged: (value) {
 | 
				
			||||||
 | 
					                        setState(() {
 | 
				
			||||||
 | 
					                          API_URL_BASE = value;
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                      },
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                    actions: [
 | 
				
			||||||
 | 
					                      TextButton(
 | 
				
			||||||
 | 
					                        child: Text('OK'),
 | 
				
			||||||
 | 
					                        onPressed: () {
 | 
				
			||||||
 | 
					                          Navigator.of(context).pop();
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                      ),
 | 
				
			||||||
 | 
					                    ],
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					              });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
    return ListView(
 | 
					    return ListView(
 | 
				
			||||||
@@ -24,66 +76,82 @@ class _ProfilePageState extends State<ProfilePage> {
 | 
				
			|||||||
          child: Text('Curious traveler', style: TextStyle(fontSize: 24))
 | 
					          child: Text('Curious traveler', style: TextStyle(fontSize: 24))
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Padding(padding: EdgeInsets.all(10)),
 | 
					        Divider(indent: 25, endIndent: 25, height: 50),
 | 
				
			||||||
        Divider(indent: 25, endIndent: 25),
 | 
					 | 
				
			||||||
        Padding(padding: EdgeInsets.all(10)),
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Padding(
 | 
					        Center(
 | 
				
			||||||
          padding: EdgeInsets.only(left: 10, right: 10, top: 0, bottom: 10),
 | 
					          child: Padding(
 | 
				
			||||||
          child: Text('Please rate your personal preferences so that we can taylor your experience.', style: TextStyle(fontSize: 18))
 | 
					           padding: EdgeInsets.only(left: 10, right: 10, top: 0, bottom: 10),
 | 
				
			||||||
 | 
					            child: Text('For a tailored experience, please rate your discovery preferences.', style: TextStyle(fontSize: 18))
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Now the sliders
 | 
					        FutureBuilder(future: _prefs, builder: futureSliders),
 | 
				
			||||||
        ImportanceSliders()
 | 
					        debugButton()
 | 
				
			||||||
      ]
 | 
					      ]
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Widget futureSliders(BuildContext context, AsyncSnapshot<UserPreferences> snapshot) {
 | 
				
			||||||
 | 
					    if (snapshot.connectionState == ConnectionState.done) {
 | 
				
			||||||
 | 
					      UserPreferences prefs = snapshot.data!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return Column(
 | 
				
			||||||
 | 
					        children: [
 | 
				
			||||||
 | 
					          PreferenceSliders(prefs: [prefs.maxTime, prefs.maxDetour]),
 | 
				
			||||||
 | 
					          Divider(indent: 25, endIndent: 25, height: 50),
 | 
				
			||||||
 | 
					          PreferenceSliders(prefs: [prefs.sightseeing, prefs.shopping, prefs.nature])
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      return CircularProgressIndicator();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PreferenceSliders extends StatefulWidget {
 | 
				
			||||||
 | 
					  final List<SinglePreference> prefs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ImportanceSliders extends StatefulWidget {
 | 
					  PreferenceSliders({required this.prefs});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  State<ImportanceSliders> createState() => _ImportanceSlidersState();
 | 
					  State<PreferenceSliders> createState() => _PreferenceSlidersState();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class _ImportanceSlidersState extends State<ImportanceSliders> {
 | 
					class _PreferenceSlidersState extends State<PreferenceSliders> {
 | 
				
			||||||
 | 
					 | 
				
			||||||
  UserPreferences _prefs = UserPreferences();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  List<Card> _createSliders() {
 | 
					 | 
				
			||||||
    List<Card> sliders = [];
 | 
					 | 
				
			||||||
    for (SinglePreference pref in _prefs.preferences) {
 | 
					 | 
				
			||||||
      sliders.add(Card(
 | 
					 | 
				
			||||||
        child: ListTile(
 | 
					 | 
				
			||||||
          leading: pref.icon,
 | 
					 | 
				
			||||||
          title: Text(pref.name),
 | 
					 | 
				
			||||||
          subtitle: Slider(
 | 
					 | 
				
			||||||
            value: pref.value.toDouble(),
 | 
					 | 
				
			||||||
            min: 0,
 | 
					 | 
				
			||||||
            max: 10,
 | 
					 | 
				
			||||||
            divisions: 10,
 | 
					 | 
				
			||||||
            label: pref.value.toString(),
 | 
					 | 
				
			||||||
            onChanged: (double newValue) {
 | 
					 | 
				
			||||||
              setState(() {
 | 
					 | 
				
			||||||
                pref.value = newValue.toInt();
 | 
					 | 
				
			||||||
                _prefs.save();
 | 
					 | 
				
			||||||
              });
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
          )
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
        margin: const EdgeInsets.only(left: 10, right: 10, top: 10, bottom: 0),
 | 
					 | 
				
			||||||
        shadowColor: Colors.grey,
 | 
					 | 
				
			||||||
      ));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return sliders;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
 | 
					    List<Card> sliders = [];
 | 
				
			||||||
 | 
					      for (SinglePreference pref in widget.prefs) {
 | 
				
			||||||
 | 
					      sliders.add(
 | 
				
			||||||
 | 
					        Card(
 | 
				
			||||||
 | 
					          child: ListTile(
 | 
				
			||||||
 | 
					            leading: pref.icon,
 | 
				
			||||||
 | 
					            title: Text(pref.name),
 | 
				
			||||||
 | 
					            subtitle: Slider(
 | 
				
			||||||
 | 
					              value: pref.value.toDouble(),
 | 
				
			||||||
 | 
					              min: pref.minVal.toDouble(),
 | 
				
			||||||
 | 
					              max: pref.maxVal.toDouble(),
 | 
				
			||||||
 | 
					              divisions: pref.maxVal - pref.minVal,
 | 
				
			||||||
 | 
					              label: pref.value.toString(),
 | 
				
			||||||
 | 
					              onChanged: (double newValue) {
 | 
				
			||||||
 | 
					                setState(() {
 | 
				
			||||||
 | 
					                  pref.value = newValue.toInt();
 | 
				
			||||||
 | 
					                  pref.save();
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					          margin: const EdgeInsets.only(left: 10, right: 10, top: 10, bottom: 0),
 | 
				
			||||||
 | 
					          shadowColor: Colors.grey,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return Column(children: _createSliders());
 | 
					    return Column(
 | 
				
			||||||
 | 
					      children: sliders);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,15 @@ import 'dart:convert';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import 'package:shared_preferences/shared_preferences.dart';
 | 
					import 'package:shared_preferences/shared_preferences.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const LandmarkType sightseeing = LandmarkType(name: 'sightseeing');
 | 
				
			||||||
 | 
					const LandmarkType nature = LandmarkType(name: 'nature');
 | 
				
			||||||
 | 
					const LandmarkType shopping = LandmarkType(name: 'shopping');
 | 
				
			||||||
 | 
					// const LandmarkType museum = LandmarkType(name: 'Museum');
 | 
				
			||||||
 | 
					// const LandmarkType restaurant = LandmarkType(name: 'Restaurant');
 | 
				
			||||||
 | 
					const LandmarkType start = LandmarkType(name: 'start');
 | 
				
			||||||
 | 
					const LandmarkType finish = LandmarkType(name: 'finish');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
final class Landmark extends LinkedListEntry<Landmark>{
 | 
					final class Landmark extends LinkedListEntry<Landmark>{
 | 
				
			||||||
  // A linked node of a list of Landmarks
 | 
					  // A linked node of a list of Landmarks
 | 
				
			||||||
  final String uuid;
 | 
					  final String uuid;
 | 
				
			||||||
@@ -47,7 +56,7 @@ final class Landmark extends LinkedListEntry<Landmark>{
 | 
				
			|||||||
        'location': List<dynamic> location,
 | 
					        'location': List<dynamic> location,
 | 
				
			||||||
        'type': String type,
 | 
					        'type': String type,
 | 
				
			||||||
      }) {
 | 
					      }) {
 | 
				
			||||||
      // refine the parsing on a few
 | 
					      // refine the parsing on a few fields
 | 
				
			||||||
      List<double> locationFixed = List<double>.from(location);
 | 
					      List<double> locationFixed = List<double>.from(location);
 | 
				
			||||||
      // parse the rest separately, they could be missing
 | 
					      // parse the rest separately, they could be missing
 | 
				
			||||||
      LandmarkType typeFixed = LandmarkType(name: type);
 | 
					      LandmarkType typeFixed = LandmarkType(name: type);
 | 
				
			||||||
@@ -55,11 +64,12 @@ final class Landmark extends LinkedListEntry<Landmark>{
 | 
				
			|||||||
      final imageURL = json['image_url'] as String?;
 | 
					      final imageURL = json['image_url'] as String?;
 | 
				
			||||||
      final description = json['description'] as String?;
 | 
					      final description = json['description'] as String?;
 | 
				
			||||||
      var duration = Duration(minutes: json['duration'] ?? 0) as Duration?;
 | 
					      var duration = Duration(minutes: json['duration'] ?? 0) as Duration?;
 | 
				
			||||||
      if (duration == const Duration()) {duration = null;};
 | 
					      // if (duration == const Duration()) {duration = null;};
 | 
				
			||||||
      final visited = json['visited'] as bool?;
 | 
					      final visited = json['visited'] as bool?;
 | 
				
			||||||
 | 
					      var tripTime = Duration(minutes: json['time_to_reach_next'] ?? 0) as Duration?;
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
      return Landmark(
 | 
					      return Landmark(
 | 
				
			||||||
        uuid: uuid, name: name, location: locationFixed, type: typeFixed, isSecondary: isSecondary, imageURL: imageURL, description: description, duration: duration, visited: visited);
 | 
					        uuid: uuid, name: name, location: locationFixed, type: typeFixed, isSecondary: isSecondary, imageURL: imageURL, description: description, duration: duration, visited: visited, tripTime: tripTime);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      throw FormatException('Invalid JSON: $json');
 | 
					      throw FormatException('Invalid JSON: $json');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -81,7 +91,8 @@ final class Landmark extends LinkedListEntry<Landmark>{
 | 
				
			|||||||
    'image_url': imageURL,
 | 
					    'image_url': imageURL,
 | 
				
			||||||
    'description': description,
 | 
					    'description': description,
 | 
				
			||||||
    'duration': duration?.inMinutes,
 | 
					    'duration': duration?.inMinutes,
 | 
				
			||||||
    'visited': visited
 | 
					    'visited': visited,
 | 
				
			||||||
 | 
					    'trip_time': tripTime?.inMinutes,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -96,6 +107,14 @@ class LandmarkType {
 | 
				
			|||||||
    // required this.description,
 | 
					    // required this.description,
 | 
				
			||||||
    // required this.icon,
 | 
					    // required this.icon,
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  bool operator ==(Object other) {
 | 
				
			||||||
 | 
					    if (other is LandmarkType) {
 | 
				
			||||||
 | 
					      return name == other.name;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,46 +0,0 @@
 | 
				
			|||||||
// import "package:fast_network_navigation/structs/landmark.dart";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// class Linked<Landmark> {
 | 
					 | 
				
			||||||
//   Landmark? head;
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
//   Linked();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//   // class methods
 | 
					 | 
				
			||||||
//   bool get isEmpty => head == null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//   // Add a new node to the end of the list
 | 
					 | 
				
			||||||
//   void add(Landmark value) {
 | 
					 | 
				
			||||||
//     if (isEmpty) {
 | 
					 | 
				
			||||||
//       // If the list is empty, set the new node as the head
 | 
					 | 
				
			||||||
//       head = value;
 | 
					 | 
				
			||||||
//     } else {
 | 
					 | 
				
			||||||
//       Landmark? current = head;
 | 
					 | 
				
			||||||
//       while (current!.next != null) {
 | 
					 | 
				
			||||||
//         // Traverse the list to find the last node
 | 
					 | 
				
			||||||
//         current = current.next;
 | 
					 | 
				
			||||||
//       }
 | 
					 | 
				
			||||||
//       current.next = value; // Set the new node as the next node of the last node
 | 
					 | 
				
			||||||
//     }
 | 
					 | 
				
			||||||
//   }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//   // Remove the first node with the given value
 | 
					 | 
				
			||||||
//   void remove(Landmark value) {
 | 
					 | 
				
			||||||
//     if (isEmpty) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//     // If the value is in the head node, update the head to the next node
 | 
					 | 
				
			||||||
//     if (head! == value) {
 | 
					 | 
				
			||||||
//       head = head.next;
 | 
					 | 
				
			||||||
//       return;
 | 
					 | 
				
			||||||
//     }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//     var current = head;
 | 
					 | 
				
			||||||
//     while (current!.next != null) {
 | 
					 | 
				
			||||||
//       if (current.next! == value) {
 | 
					 | 
				
			||||||
//         // If the value is found in the next node, skip the next node
 | 
					 | 
				
			||||||
//         current.next = current.next.next;
 | 
					 | 
				
			||||||
//         return;
 | 
					 | 
				
			||||||
//       }
 | 
					 | 
				
			||||||
//       current = current.next;
 | 
					 | 
				
			||||||
//     }
 | 
					 | 
				
			||||||
//   }
 | 
					 | 
				
			||||||
// }
 | 
					 | 
				
			||||||
@@ -3,80 +3,100 @@ import 'package:shared_preferences/shared_preferences.dart';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SinglePreference {
 | 
					class SinglePreference {
 | 
				
			||||||
 | 
					  String slug;
 | 
				
			||||||
  String name;
 | 
					  String name;
 | 
				
			||||||
  String description;
 | 
					  String description;
 | 
				
			||||||
  int value;
 | 
					  int value;
 | 
				
			||||||
 | 
					  int minVal;
 | 
				
			||||||
 | 
					  int maxVal;
 | 
				
			||||||
  Icon icon;
 | 
					  Icon icon;
 | 
				
			||||||
  String key;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  SinglePreference({
 | 
					  SinglePreference({
 | 
				
			||||||
 | 
					    required this.slug,
 | 
				
			||||||
    required this.name,
 | 
					    required this.name,
 | 
				
			||||||
    required this.description,
 | 
					    required this.description,
 | 
				
			||||||
    required this.value,
 | 
					    required this.value,
 | 
				
			||||||
    required this.icon,
 | 
					    required this.icon,
 | 
				
			||||||
    required this.key,
 | 
					    this.minVal = 0,
 | 
				
			||||||
 | 
					    this.maxVal = 5,
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class UserPreferences {
 | 
					 | 
				
			||||||
  List<SinglePreference> preferences = [
 | 
					 | 
				
			||||||
    SinglePreference(
 | 
					 | 
				
			||||||
      name: "Sightseeing",
 | 
					 | 
				
			||||||
      description: "How much do you like sightseeing?",
 | 
					 | 
				
			||||||
      value: 0,
 | 
					 | 
				
			||||||
      icon: Icon(Icons.church),
 | 
					 | 
				
			||||||
      key: "sightseeing",
 | 
					 | 
				
			||||||
    ),
 | 
					 | 
				
			||||||
    SinglePreference(
 | 
					 | 
				
			||||||
      name: "Shopping",
 | 
					 | 
				
			||||||
      description: "How much do you like shopping?",
 | 
					 | 
				
			||||||
      value: 0,
 | 
					 | 
				
			||||||
      icon: Icon(Icons.shopping_bag),
 | 
					 | 
				
			||||||
      key: "shopping",
 | 
					 | 
				
			||||||
    ),
 | 
					 | 
				
			||||||
    SinglePreference(
 | 
					 | 
				
			||||||
      name: "Foods & Drinks",
 | 
					 | 
				
			||||||
      description: "How much do you like eating?",
 | 
					 | 
				
			||||||
      value: 0,
 | 
					 | 
				
			||||||
      icon: Icon(Icons.restaurant),
 | 
					 | 
				
			||||||
      key: "eating",
 | 
					 | 
				
			||||||
    ),
 | 
					 | 
				
			||||||
    SinglePreference(
 | 
					 | 
				
			||||||
      name: "Nightlife",
 | 
					 | 
				
			||||||
      description: "How much do you like nightlife?",
 | 
					 | 
				
			||||||
      value: 0,
 | 
					 | 
				
			||||||
      icon: Icon(Icons.wine_bar),
 | 
					 | 
				
			||||||
      key: "nightlife",
 | 
					 | 
				
			||||||
    ),
 | 
					 | 
				
			||||||
    SinglePreference(
 | 
					 | 
				
			||||||
      name: "Nature",
 | 
					 | 
				
			||||||
      description: "How much do you like nature?",
 | 
					 | 
				
			||||||
      value: 0,
 | 
					 | 
				
			||||||
      icon: Icon(Icons.landscape),
 | 
					 | 
				
			||||||
      key: "nature",
 | 
					 | 
				
			||||||
    ),
 | 
					 | 
				
			||||||
    SinglePreference(
 | 
					 | 
				
			||||||
      name: "Culture",
 | 
					 | 
				
			||||||
      description: "How much do you like culture?",
 | 
					 | 
				
			||||||
      value: 0,
 | 
					 | 
				
			||||||
      icon: Icon(Icons.palette),
 | 
					 | 
				
			||||||
      key: "culture",
 | 
					 | 
				
			||||||
    ),
 | 
					 | 
				
			||||||
  ];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void save() async {
 | 
					  void save() async {
 | 
				
			||||||
    SharedPreferences sharedPrefs = await SharedPreferences.getInstance();
 | 
					    SharedPreferences sharedPrefs = await SharedPreferences.getInstance();
 | 
				
			||||||
    for (SinglePreference pref in preferences) {
 | 
					      sharedPrefs.setInt('pref_$slug', value);
 | 
				
			||||||
      sharedPrefs.setInt(pref.key, pref.value);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void load() async {
 | 
					  void load() async {
 | 
				
			||||||
    SharedPreferences sharedPrefs = await SharedPreferences.getInstance();
 | 
					    SharedPreferences sharedPrefs = await SharedPreferences.getInstance();
 | 
				
			||||||
    for (SinglePreference pref in preferences) {
 | 
					      value = sharedPrefs.getInt('pref_$slug') ?? minVal;
 | 
				
			||||||
      pref.value = sharedPrefs.getInt(pref.key) ?? 0;
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class UserPreferences {
 | 
				
			||||||
 | 
					  SinglePreference sightseeing = SinglePreference(
 | 
				
			||||||
 | 
					    name: "Sightseeing",
 | 
				
			||||||
 | 
					    slug: "sightseeing",
 | 
				
			||||||
 | 
					    description: "How much do you like sightseeing?",
 | 
				
			||||||
 | 
					    value: 0,
 | 
				
			||||||
 | 
					    icon: Icon(Icons.church),
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					  SinglePreference shopping = SinglePreference(
 | 
				
			||||||
 | 
					    name: "Shopping",
 | 
				
			||||||
 | 
					    slug: "shopping",
 | 
				
			||||||
 | 
					    description: "How much do you like shopping?",
 | 
				
			||||||
 | 
					    value: 0,
 | 
				
			||||||
 | 
					    icon: Icon(Icons.shopping_bag),
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					  SinglePreference nature = SinglePreference(
 | 
				
			||||||
 | 
					    name: "Nature",
 | 
				
			||||||
 | 
					    slug: "nature",
 | 
				
			||||||
 | 
					    description: "How much do you like nature?",
 | 
				
			||||||
 | 
					    value: 0,
 | 
				
			||||||
 | 
					    icon: Icon(Icons.landscape),
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  SinglePreference maxTime = SinglePreference(
 | 
				
			||||||
 | 
					    name: "Trip duration",
 | 
				
			||||||
 | 
					    slug: "duration",
 | 
				
			||||||
 | 
					    description: "How long do you want your trip to be?",
 | 
				
			||||||
 | 
					    value: 30,
 | 
				
			||||||
 | 
					    minVal: 30,
 | 
				
			||||||
 | 
					    maxVal: 720,
 | 
				
			||||||
 | 
					    icon: Icon(Icons.timer),
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					  SinglePreference maxDetour = SinglePreference(
 | 
				
			||||||
 | 
					    name: "Trip detours",
 | 
				
			||||||
 | 
					    slug: "detours",
 | 
				
			||||||
 | 
					    description: "Are you okay with roaming even if makes the trip longer?",
 | 
				
			||||||
 | 
					    value: 0,
 | 
				
			||||||
 | 
					    maxVal: 30,
 | 
				
			||||||
 | 
					    icon: Icon(Icons.loupe_sharp),
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Future<void> load() async {
 | 
				
			||||||
 | 
					    for (SinglePreference pref in [sightseeing, shopping, nature, maxTime, maxDetour]) {
 | 
				
			||||||
 | 
					      pref.load();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Map<String, dynamic> toJson() {
 | 
				
			||||||
 | 
					      // This is "opinionated" JSON, corresponding to the backend's expectations
 | 
				
			||||||
 | 
					      return {
 | 
				
			||||||
 | 
					        "sightseeing": {"type": "sightseeing", "score": sightseeing.value},
 | 
				
			||||||
 | 
					        "shopping": {"type": "shopping", "score": shopping.value},
 | 
				
			||||||
 | 
					        "nature": {"type": "nature", "score": nature.value},
 | 
				
			||||||
 | 
					        "max_time_minute": maxTime.value,
 | 
				
			||||||
 | 
					        "detour_tolerance_minute": maxDetour.value
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Future<UserPreferences> loadUserPreferences() async {
 | 
				
			||||||
 | 
					  UserPreferences prefs = UserPreferences();
 | 
				
			||||||
 | 
					  await prefs.load();
 | 
				
			||||||
 | 
					  return prefs;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,14 +0,0 @@
 | 
				
			|||||||
import "package:fast_network_navigation/structs/landmark.dart";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Route {
 | 
					 | 
				
			||||||
  final String name;
 | 
					 | 
				
			||||||
  final Duration duration;
 | 
					 | 
				
			||||||
  final List<Landmark> landmarks;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  Route({
 | 
					 | 
				
			||||||
    required this.name,
 | 
					 | 
				
			||||||
    required this.duration,
 | 
					 | 
				
			||||||
    required this.landmarks
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -4,32 +4,72 @@
 | 
				
			|||||||
import 'dart:collection';
 | 
					import 'dart:collection';
 | 
				
			||||||
import 'dart:convert';
 | 
					import 'dart:convert';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import 'package:fast_network_navigation/structs/landmark.dart';
 | 
					import 'package:anyway/structs/landmark.dart';
 | 
				
			||||||
 | 
					import 'package:flutter/foundation.dart';
 | 
				
			||||||
 | 
					import 'package:geocoding/geocoding.dart';
 | 
				
			||||||
import 'package:shared_preferences/shared_preferences.dart';
 | 
					import 'package:shared_preferences/shared_preferences.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Trip {
 | 
					class Trip with ChangeNotifier {
 | 
				
			||||||
  final String uuid;
 | 
					  String uuid;
 | 
				
			||||||
  final String cityName;
 | 
					  int totalTime;
 | 
				
			||||||
  // TODO: cityName should be inferred from coordinates of the Landmarks
 | 
					  LinkedList<Landmark> landmarks;
 | 
				
			||||||
  final LinkedList<Landmark> landmarks;
 | 
					 | 
				
			||||||
  // could be empty as well
 | 
					  // could be empty as well
 | 
				
			||||||
 | 
					  String? errorDescription;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Future<String> get cityName async {
 | 
				
			||||||
 | 
					    List<double>? location = landmarks.firstOrNull?.location; 
 | 
				
			||||||
 | 
					    if (GeocodingPlatform.instance == null) {
 | 
				
			||||||
 | 
					      return '$location';
 | 
				
			||||||
 | 
					    } else if (location == null) {
 | 
				
			||||||
 | 
					      return 'Unknown';
 | 
				
			||||||
 | 
					    } else{
 | 
				
			||||||
 | 
					      List<Placemark> placemarks = await placemarkFromCoordinates(location[0], location[1]);
 | 
				
			||||||
 | 
					      return placemarks.first.locality ?? 'Unknown';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Trip({
 | 
					  Trip({
 | 
				
			||||||
    required this.uuid,
 | 
					    this.uuid = 'pending',
 | 
				
			||||||
    required this.cityName,
 | 
					    this.totalTime = 0,
 | 
				
			||||||
    required this.landmarks,
 | 
					    LinkedList<Landmark>? landmarks
 | 
				
			||||||
  });
 | 
					    // a trip can be created with no landmarks, but the list should be initialized anyway
 | 
				
			||||||
 | 
					  }) : landmarks = landmarks ?? LinkedList<Landmark>();
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  factory Trip.fromJson(Map<String, dynamic> json) {
 | 
					  factory Trip.fromJson(Map<String, dynamic> json) {
 | 
				
			||||||
    return Trip(
 | 
					    Trip trip = Trip(
 | 
				
			||||||
      uuid: json['uuid'],
 | 
					      uuid: json['uuid'],
 | 
				
			||||||
      cityName: json['city_name'],
 | 
					      totalTime: json['total_time'],
 | 
				
			||||||
      landmarks: LinkedList()
 | 
					 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return trip;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void loadFromJson(Map<String, dynamic> json) {
 | 
				
			||||||
 | 
					    uuid = json['uuid'];
 | 
				
			||||||
 | 
					    totalTime = json['total_time'];
 | 
				
			||||||
 | 
					    notifyListeners();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  void addLandmark(Landmark landmark) {
 | 
				
			||||||
 | 
					    landmarks.add(landmark);
 | 
				
			||||||
 | 
					    notifyListeners();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void updateUUID(String newUUID) {
 | 
				
			||||||
 | 
					    uuid = newUUID;
 | 
				
			||||||
 | 
					    notifyListeners();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void removeLandmark(Landmark landmark) {
 | 
				
			||||||
 | 
					    landmarks.remove(landmark);
 | 
				
			||||||
 | 
					    notifyListeners();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  void updateError(String error) {
 | 
				
			||||||
 | 
					    errorDescription = error;
 | 
				
			||||||
 | 
					    notifyListeners();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  factory Trip.fromPrefs(SharedPreferences prefs, String uuid) {
 | 
					  factory Trip.fromPrefs(SharedPreferences prefs, String uuid) {
 | 
				
			||||||
    String? content = prefs.getString('trip_$uuid');
 | 
					    String? content = prefs.getString('trip_$uuid');
 | 
				
			||||||
@@ -43,8 +83,8 @@ class Trip {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  Map<String, dynamic> toJson() => {
 | 
					  Map<String, dynamic> toJson() => {
 | 
				
			||||||
    'uuid': uuid,
 | 
					    'uuid': uuid,
 | 
				
			||||||
    'city_name': cityName,
 | 
					    'total_time': totalTime,
 | 
				
			||||||
    'entry_uuid': landmarks.first?.uuid ?? ''
 | 
					    'first_landmark_uuid': landmarks.first.uuid
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,52 +0,0 @@
 | 
				
			|||||||
// import "package:fast_network_navigation/structs/landmark.dart";
 | 
					 | 
				
			||||||
// import 'package:http/http.dart' as http;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Future<List<Landmark>> fetchLandmarks() async {
 | 
					 | 
				
			||||||
//   // final response = await http
 | 
					 | 
				
			||||||
//   //     .get(Uri.parse('https://nav.kluster.moll.re/v1/destination/1'));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//   // if (response.statusCode == 200) {
 | 
					 | 
				
			||||||
//     // If the server did return a 200 OK response,
 | 
					 | 
				
			||||||
//     // then parse the JSON.
 | 
					 | 
				
			||||||
//     List<Landmark> landmarks = [
 | 
					 | 
				
			||||||
//       // 48°51′29.6″N 2°17′40.2″E
 | 
					 | 
				
			||||||
//       Landmark(
 | 
					 | 
				
			||||||
//         name: "Eiffel Tower",
 | 
					 | 
				
			||||||
//         location: [48.51296, 2.17402],
 | 
					 | 
				
			||||||
//         type: LandmarkType(name: "Tower"),
 | 
					 | 
				
			||||||
//         imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a8/Tour_Eiffel_Wikimedia_Commons.jpg/1037px-Tour_Eiffel_Wikimedia_Commons.jpg"
 | 
					 | 
				
			||||||
//         ),
 | 
					 | 
				
			||||||
//       Landmark(
 | 
					 | 
				
			||||||
//         name: "Notre Dame Cathedral",
 | 
					 | 
				
			||||||
//         location: [48.8530, 2.3498],
 | 
					 | 
				
			||||||
//         type: LandmarkType(name: "Monument"),
 | 
					 | 
				
			||||||
//         imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f7/Notre-Dame_de_Paris%2C_4_October_2017.jpg/440px-Notre-Dame_de_Paris%2C_4_October_2017.jpg"
 | 
					 | 
				
			||||||
//         ),
 | 
					 | 
				
			||||||
//       Landmark(
 | 
					 | 
				
			||||||
//         name: "Louvre palace",
 | 
					 | 
				
			||||||
//         location: [48.8606, 2.3376],
 | 
					 | 
				
			||||||
//         type: LandmarkType(name: "Museum"),
 | 
					 | 
				
			||||||
//         imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/6/66/Louvre_Museum_Wikimedia_Commons.jpg/540px-Louvre_Museum_Wikimedia_Commons.jpg"
 | 
					 | 
				
			||||||
//         ),
 | 
					 | 
				
			||||||
//       Landmark(
 | 
					 | 
				
			||||||
//         name: "Pont-des-arts",
 | 
					 | 
				
			||||||
//         location: [48.5130, 2.2015],
 | 
					 | 
				
			||||||
//         type: LandmarkType(name: "Bridge"),
 | 
					 | 
				
			||||||
//         imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d1/Pont_des_Arts%2C_6e_Arrondissement%2C_Paris_%28HDR%29_20140320_1.jpg/560px-Pont_des_Arts%2C_6e_Arrondissement%2C_Paris_%28HDR%29_20140320_1.jpg"),
 | 
					 | 
				
			||||||
//       Landmark(
 | 
					 | 
				
			||||||
//         name: "Panthéon",
 | 
					 | 
				
			||||||
//         location: [48.5046, 2.2046],
 | 
					 | 
				
			||||||
//         type: LandmarkType(name: "Monument"),
 | 
					 | 
				
			||||||
//         imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/8/80/Pantheon_of_Paris_007.JPG/1280px-Pantheon_of_Paris_007.JPG"
 | 
					 | 
				
			||||||
//         ),
 | 
					 | 
				
			||||||
//     ];
 | 
					 | 
				
			||||||
//     // sleep 10 seconds
 | 
					 | 
				
			||||||
//     await Future.delayed(Duration(seconds: 5));
 | 
					 | 
				
			||||||
//     return landmarks;
 | 
					 | 
				
			||||||
//   // } else {
 | 
					 | 
				
			||||||
//   //   // If the server did not return a 200 OK response,
 | 
					 | 
				
			||||||
//   //   // then throw an exception.
 | 
					 | 
				
			||||||
//   //   throw Exception('Failed to load destination');
 | 
					 | 
				
			||||||
//   // }
 | 
					 | 
				
			||||||
// }
 | 
					 | 
				
			||||||
							
								
								
									
										93
									
								
								frontend/lib/utils/fetch_trip.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								frontend/lib/utils/fetch_trip.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,93 @@
 | 
				
			|||||||
 | 
					import "dart:convert";
 | 
				
			||||||
 | 
					import "dart:developer";
 | 
				
			||||||
 | 
					import 'package:dio/dio.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import 'package:anyway/constants.dart';
 | 
				
			||||||
 | 
					import "package:anyway/structs/landmark.dart";
 | 
				
			||||||
 | 
					import "package:anyway/structs/trip.dart";
 | 
				
			||||||
 | 
					import "package:anyway/structs/preferences.dart";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Dio dio = Dio(
 | 
				
			||||||
 | 
					    BaseOptions(
 | 
				
			||||||
 | 
					      baseUrl: API_URL_BASE,
 | 
				
			||||||
 | 
					      connectTimeout: const Duration(seconds: 5),
 | 
				
			||||||
 | 
					      receiveTimeout: const Duration(seconds: 120),
 | 
				
			||||||
 | 
					      // also accept 500 errors, since we cannot rule out that the server is at fault. We still want to gracefully handle these errors
 | 
				
			||||||
 | 
					      validateStatus: (status) => status! <= 500,
 | 
				
			||||||
 | 
					      receiveDataWhenStatusError: true,
 | 
				
			||||||
 | 
					      // api is notoriously slow
 | 
				
			||||||
 | 
					      // headers: {
 | 
				
			||||||
 | 
					      //   HttpHeaders.userAgentHeader: 'dio',
 | 
				
			||||||
 | 
					      //   'api': '1.0.0',
 | 
				
			||||||
 | 
					      // },
 | 
				
			||||||
 | 
					      contentType: Headers.jsonContentType,
 | 
				
			||||||
 | 
					      responseType: ResponseType.json,
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					  ),
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fetchTrip(
 | 
				
			||||||
 | 
					  Trip trip,
 | 
				
			||||||
 | 
					  Future<UserPreferences> preferences,
 | 
				
			||||||
 | 
					) async {
 | 
				
			||||||
 | 
					  UserPreferences prefs = await preferences;
 | 
				
			||||||
 | 
					  Map<String, dynamic> data = {
 | 
				
			||||||
 | 
					    "preferences": prefs.toJson(),
 | 
				
			||||||
 | 
					    "start": trip.landmarks!.first.location,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  String dataString = jsonEncode(data);
 | 
				
			||||||
 | 
					  log(dataString);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  final response = await dio.post(
 | 
				
			||||||
 | 
					    "/trip/new",
 | 
				
			||||||
 | 
					    data: data
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // handle errors
 | 
				
			||||||
 | 
					  if (response.statusCode != 200) {
 | 
				
			||||||
 | 
					    trip.updateUUID("error");
 | 
				
			||||||
 | 
					    if (response.data["detail"] != null) {
 | 
				
			||||||
 | 
					      trip.updateError(response.data["detail"]);
 | 
				
			||||||
 | 
					      log(response.data["detail"]);
 | 
				
			||||||
 | 
					      // throw Exception(response.data["detail"]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    Map<String, dynamic> json = response.data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // only fill in the trip "meta" data for now
 | 
				
			||||||
 | 
					    trip.loadFromJson(json);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // now fill the trip with landmarks
 | 
				
			||||||
 | 
					    // we are going to recreate ALL the landmarks from the information given by the api
 | 
				
			||||||
 | 
					    trip.landmarks.remove(trip.landmarks.first);
 | 
				
			||||||
 | 
					    String? nextUUID = json["first_landmark_uuid"];
 | 
				
			||||||
 | 
					    while (nextUUID != null) {
 | 
				
			||||||
 | 
					      var (landmark, newUUID) = await fetchLandmark(nextUUID);
 | 
				
			||||||
 | 
					      trip.addLandmark(landmark);
 | 
				
			||||||
 | 
					      nextUUID = newUUID;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  log(response.data.toString());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Future<(Landmark, String?)> fetchLandmark(String uuid) async {
 | 
				
			||||||
 | 
					  final response = await dio.get(
 | 
				
			||||||
 | 
					    "/landmark/$uuid"
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // handle errors
 | 
				
			||||||
 | 
					  if (response.statusCode != 200) {
 | 
				
			||||||
 | 
					    throw Exception('Failed to load landmark');
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (response.data["detail"] != null) {
 | 
				
			||||||
 | 
					    throw Exception(response.data["detail"]);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  log(response.data.toString());
 | 
				
			||||||
 | 
					  Map<String, dynamic> json = response.data;
 | 
				
			||||||
 | 
					  String? nextUUID = json["next_uuid"];
 | 
				
			||||||
 | 
					  return (Landmark.fromJson(json), nextUUID);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
import 'dart:collection';
 | 
					import 'dart:collection';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import 'package:fast_network_navigation/structs/trip.dart';
 | 
					import 'package:anyway/structs/trip.dart';
 | 
				
			||||||
import 'package:fast_network_navigation/structs/landmark.dart';
 | 
					import 'package:anyway/structs/landmark.dart';
 | 
				
			||||||
import 'package:shared_preferences/shared_preferences.dart';
 | 
					import 'package:shared_preferences/shared_preferences.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Future<List<Trip>> loadTrips() async {
 | 
					Future<List<Trip>> loadTrips() async {
 | 
				
			||||||
@@ -17,7 +17,7 @@ Future<List<Trip>> loadTrips() async {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (trips.isEmpty) {
 | 
					  if (trips.isEmpty) {
 | 
				
			||||||
    Trip t1 = Trip(uuid: '1', cityName: 'Paris', landmarks: LinkedList<Landmark>());
 | 
					    Trip t1 = Trip(uuid: '1', landmarks: LinkedList<Landmark>());
 | 
				
			||||||
    t1.landmarks.add(
 | 
					    t1.landmarks.add(
 | 
				
			||||||
      Landmark(
 | 
					      Landmark(
 | 
				
			||||||
        uuid: '1',
 | 
					        uuid: '1',
 | 
				
			||||||
@@ -66,7 +66,7 @@ Future<List<Trip>> loadTrips() async {
 | 
				
			|||||||
    trips.add(t1);
 | 
					    trips.add(t1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Trip t2 = Trip(uuid: '2', cityName: 'Vienna', landmarks: LinkedList<Landmark>());
 | 
					    Trip t2 = Trip(uuid: '2', landmarks: LinkedList<Landmark>());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    t2.landmarks.add(
 | 
					    t2.landmarks.add(
 | 
				
			||||||
      Landmark(
 | 
					      Landmark(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,10 +4,10 @@ project(runner LANGUAGES CXX)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# The name of the executable created for the application. Change this to change
 | 
					# The name of the executable created for the application. Change this to change
 | 
				
			||||||
# the on-disk name of your application.
 | 
					# the on-disk name of your application.
 | 
				
			||||||
set(BINARY_NAME "fast_network_navigation")
 | 
					set(BINARY_NAME "anyway")
 | 
				
			||||||
# The unique GTK application identifier for this application. See:
 | 
					# The unique GTK application identifier for this application. See:
 | 
				
			||||||
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
 | 
					# https://wiki.gnome.org/HowDoI/ChooseApplicationID
 | 
				
			||||||
set(APPLICATION_ID "com.example.fast_network_navigation")
 | 
					set(APPLICATION_ID "com.example.anyway")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
 | 
					# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
 | 
				
			||||||
# versions of CMake.
 | 
					# versions of CMake.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,11 +40,11 @@ static void my_application_activate(GApplication* application) {
 | 
				
			|||||||
  if (use_header_bar) {
 | 
					  if (use_header_bar) {
 | 
				
			||||||
    GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
 | 
					    GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
 | 
				
			||||||
    gtk_widget_show(GTK_WIDGET(header_bar));
 | 
					    gtk_widget_show(GTK_WIDGET(header_bar));
 | 
				
			||||||
    gtk_header_bar_set_title(header_bar, "fast_network_navigation");
 | 
					    gtk_header_bar_set_title(header_bar, "anyway");
 | 
				
			||||||
    gtk_header_bar_set_show_close_button(header_bar, TRUE);
 | 
					    gtk_header_bar_set_show_close_button(header_bar, TRUE);
 | 
				
			||||||
    gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
 | 
					    gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    gtk_window_set_title(window, "fast_network_navigation");
 | 
					    gtk_window_set_title(window, "anyway");
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  gtk_window_set_default_size(window, 1280, 720);
 | 
					  gtk_window_set_default_size(window, 1280, 720);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,8 +5,12 @@
 | 
				
			|||||||
import FlutterMacOS
 | 
					import FlutterMacOS
 | 
				
			||||||
import Foundation
 | 
					import Foundation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import path_provider_foundation
 | 
				
			||||||
import shared_preferences_foundation
 | 
					import shared_preferences_foundation
 | 
				
			||||||
 | 
					import sqflite
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
 | 
					func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
 | 
				
			||||||
 | 
					  PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
 | 
				
			||||||
  SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
 | 
					  SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
 | 
				
			||||||
 | 
					  SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -64,7 +64,7 @@
 | 
				
			|||||||
		331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
 | 
							331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
 | 
				
			||||||
		333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
 | 
							333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
 | 
				
			||||||
		335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
 | 
							335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
 | 
				
			||||||
		33CC10ED2044A3C60003C045 /* fast_network_navigation.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "fast_network_navigation.app"; sourceTree = BUILT_PRODUCTS_DIR; };
 | 
							33CC10ED2044A3C60003C045 /* anyway.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "anyway.app"; sourceTree = BUILT_PRODUCTS_DIR; };
 | 
				
			||||||
		33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
 | 
							33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
 | 
				
			||||||
		33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
 | 
							33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
 | 
				
			||||||
		33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
 | 
							33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
 | 
				
			||||||
@@ -131,7 +131,7 @@
 | 
				
			|||||||
		33CC10EE2044A3C60003C045 /* Products */ = {
 | 
							33CC10EE2044A3C60003C045 /* Products */ = {
 | 
				
			||||||
			isa = PBXGroup;
 | 
								isa = PBXGroup;
 | 
				
			||||||
			children = (
 | 
								children = (
 | 
				
			||||||
				33CC10ED2044A3C60003C045 /* fast_network_navigation.app */,
 | 
									33CC10ED2044A3C60003C045 /* anyway.app */,
 | 
				
			||||||
				331C80D5294CF71000263BE5 /* RunnerTests.xctest */,
 | 
									331C80D5294CF71000263BE5 /* RunnerTests.xctest */,
 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
			name = Products;
 | 
								name = Products;
 | 
				
			||||||
@@ -217,7 +217,7 @@
 | 
				
			|||||||
			);
 | 
								);
 | 
				
			||||||
			name = Runner;
 | 
								name = Runner;
 | 
				
			||||||
			productName = Runner;
 | 
								productName = Runner;
 | 
				
			||||||
			productReference = 33CC10ED2044A3C60003C045 /* fast_network_navigation.app */;
 | 
								productReference = 33CC10ED2044A3C60003C045 /* anyway.app */;
 | 
				
			||||||
			productType = "com.apple.product-type.application";
 | 
								productType = "com.apple.product-type.application";
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
/* End PBXNativeTarget section */
 | 
					/* End PBXNativeTarget section */
 | 
				
			||||||
@@ -388,7 +388,7 @@
 | 
				
			|||||||
				PRODUCT_BUNDLE_IDENTIFIER = com.example.fastNetworkNavigation.RunnerTests;
 | 
									PRODUCT_BUNDLE_IDENTIFIER = com.example.fastNetworkNavigation.RunnerTests;
 | 
				
			||||||
				PRODUCT_NAME = "$(TARGET_NAME)";
 | 
									PRODUCT_NAME = "$(TARGET_NAME)";
 | 
				
			||||||
				SWIFT_VERSION = 5.0;
 | 
									SWIFT_VERSION = 5.0;
 | 
				
			||||||
				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/fast_network_navigation.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/fast_network_navigation";
 | 
									TEST_HOST = "$(BUILT_PRODUCTS_DIR)/anyway.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/anyway";
 | 
				
			||||||
			};
 | 
								};
 | 
				
			||||||
			name = Debug;
 | 
								name = Debug;
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
@@ -402,7 +402,7 @@
 | 
				
			|||||||
				PRODUCT_BUNDLE_IDENTIFIER = com.example.fastNetworkNavigation.RunnerTests;
 | 
									PRODUCT_BUNDLE_IDENTIFIER = com.example.fastNetworkNavigation.RunnerTests;
 | 
				
			||||||
				PRODUCT_NAME = "$(TARGET_NAME)";
 | 
									PRODUCT_NAME = "$(TARGET_NAME)";
 | 
				
			||||||
				SWIFT_VERSION = 5.0;
 | 
									SWIFT_VERSION = 5.0;
 | 
				
			||||||
				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/fast_network_navigation.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/fast_network_navigation";
 | 
									TEST_HOST = "$(BUILT_PRODUCTS_DIR)/anyway.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/anyway";
 | 
				
			||||||
			};
 | 
								};
 | 
				
			||||||
			name = Release;
 | 
								name = Release;
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
@@ -416,7 +416,7 @@
 | 
				
			|||||||
				PRODUCT_BUNDLE_IDENTIFIER = com.example.fastNetworkNavigation.RunnerTests;
 | 
									PRODUCT_BUNDLE_IDENTIFIER = com.example.fastNetworkNavigation.RunnerTests;
 | 
				
			||||||
				PRODUCT_NAME = "$(TARGET_NAME)";
 | 
									PRODUCT_NAME = "$(TARGET_NAME)";
 | 
				
			||||||
				SWIFT_VERSION = 5.0;
 | 
									SWIFT_VERSION = 5.0;
 | 
				
			||||||
				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/fast_network_navigation.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/fast_network_navigation";
 | 
									TEST_HOST = "$(BUILT_PRODUCTS_DIR)/anyway.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/anyway";
 | 
				
			||||||
			};
 | 
								};
 | 
				
			||||||
			name = Profile;
 | 
								name = Profile;
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,7 +15,7 @@
 | 
				
			|||||||
            <BuildableReference
 | 
					            <BuildableReference
 | 
				
			||||||
               BuildableIdentifier = "primary"
 | 
					               BuildableIdentifier = "primary"
 | 
				
			||||||
               BlueprintIdentifier = "33CC10EC2044A3C60003C045"
 | 
					               BlueprintIdentifier = "33CC10EC2044A3C60003C045"
 | 
				
			||||||
               BuildableName = "fast_network_navigation.app"
 | 
					               BuildableName = "anyway.app"
 | 
				
			||||||
               BlueprintName = "Runner"
 | 
					               BlueprintName = "Runner"
 | 
				
			||||||
               ReferencedContainer = "container:Runner.xcodeproj">
 | 
					               ReferencedContainer = "container:Runner.xcodeproj">
 | 
				
			||||||
            </BuildableReference>
 | 
					            </BuildableReference>
 | 
				
			||||||
@@ -31,7 +31,7 @@
 | 
				
			|||||||
         <BuildableReference
 | 
					         <BuildableReference
 | 
				
			||||||
            BuildableIdentifier = "primary"
 | 
					            BuildableIdentifier = "primary"
 | 
				
			||||||
            BlueprintIdentifier = "33CC10EC2044A3C60003C045"
 | 
					            BlueprintIdentifier = "33CC10EC2044A3C60003C045"
 | 
				
			||||||
            BuildableName = "fast_network_navigation.app"
 | 
					            BuildableName = "anyway.app"
 | 
				
			||||||
            BlueprintName = "Runner"
 | 
					            BlueprintName = "Runner"
 | 
				
			||||||
            ReferencedContainer = "container:Runner.xcodeproj">
 | 
					            ReferencedContainer = "container:Runner.xcodeproj">
 | 
				
			||||||
         </BuildableReference>
 | 
					         </BuildableReference>
 | 
				
			||||||
@@ -65,7 +65,7 @@
 | 
				
			|||||||
         <BuildableReference
 | 
					         <BuildableReference
 | 
				
			||||||
            BuildableIdentifier = "primary"
 | 
					            BuildableIdentifier = "primary"
 | 
				
			||||||
            BlueprintIdentifier = "33CC10EC2044A3C60003C045"
 | 
					            BlueprintIdentifier = "33CC10EC2044A3C60003C045"
 | 
				
			||||||
            BuildableName = "fast_network_navigation.app"
 | 
					            BuildableName = "anyway.app"
 | 
				
			||||||
            BlueprintName = "Runner"
 | 
					            BlueprintName = "Runner"
 | 
				
			||||||
            ReferencedContainer = "container:Runner.xcodeproj">
 | 
					            ReferencedContainer = "container:Runner.xcodeproj">
 | 
				
			||||||
         </BuildableReference>
 | 
					         </BuildableReference>
 | 
				
			||||||
@@ -82,7 +82,7 @@
 | 
				
			|||||||
         <BuildableReference
 | 
					         <BuildableReference
 | 
				
			||||||
            BuildableIdentifier = "primary"
 | 
					            BuildableIdentifier = "primary"
 | 
				
			||||||
            BlueprintIdentifier = "33CC10EC2044A3C60003C045"
 | 
					            BlueprintIdentifier = "33CC10EC2044A3C60003C045"
 | 
				
			||||||
            BuildableName = "fast_network_navigation.app"
 | 
					            BuildableName = "anyway.app"
 | 
				
			||||||
            BlueprintName = "Runner"
 | 
					            BlueprintName = "Runner"
 | 
				
			||||||
            ReferencedContainer = "container:Runner.xcodeproj">
 | 
					            ReferencedContainer = "container:Runner.xcodeproj">
 | 
				
			||||||
         </BuildableReference>
 | 
					         </BuildableReference>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,7 @@
 | 
				
			|||||||
// 'flutter create' template.
 | 
					// 'flutter create' template.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// The application's name. By default this is also the title of the Flutter window.
 | 
					// The application's name. By default this is also the title of the Flutter window.
 | 
				
			||||||
PRODUCT_NAME = fast_network_navigation
 | 
					PRODUCT_NAME = anyway
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// The application's bundle identifier
 | 
					// The application's bundle identifier
 | 
				
			||||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.fastNetworkNavigation
 | 
					PRODUCT_BUNDLE_IDENTIFIER = com.example.fastNetworkNavigation
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,14 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "2.11.0"
 | 
					    version: "2.11.0"
 | 
				
			||||||
 | 
					  auto_size_text:
 | 
				
			||||||
 | 
					    dependency: "direct main"
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: auto_size_text
 | 
				
			||||||
 | 
					      sha256: "3f5261cd3fb5f2a9ab4e2fc3fba84fd9fcaac8821f20a1d4e71f557521b22599"
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "3.0.0"
 | 
				
			||||||
  boolean_selector:
 | 
					  boolean_selector:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -17,6 +25,30 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "2.1.1"
 | 
					    version: "2.1.1"
 | 
				
			||||||
 | 
					  cached_network_image:
 | 
				
			||||||
 | 
					    dependency: "direct main"
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: cached_network_image
 | 
				
			||||||
 | 
					      sha256: "4a5d8d2c728b0f3d0245f69f921d7be90cae4c2fd5288f773088672c0893f819"
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "3.4.0"
 | 
				
			||||||
 | 
					  cached_network_image_platform_interface:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: cached_network_image_platform_interface
 | 
				
			||||||
 | 
					      sha256: ff0c949e323d2a1b52be73acce5b4a7b04063e61414c8ca542dbba47281630a7
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "4.1.0"
 | 
				
			||||||
 | 
					  cached_network_image_web:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: cached_network_image_web
 | 
				
			||||||
 | 
					      sha256: "6322dde7a5ad92202e64df659241104a43db20ed594c41ca18de1014598d7996"
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "1.3.0"
 | 
				
			||||||
  characters:
 | 
					  characters:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -41,6 +73,14 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "1.18.0"
 | 
					    version: "1.18.0"
 | 
				
			||||||
 | 
					  crypto:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: crypto
 | 
				
			||||||
 | 
					      sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "3.0.3"
 | 
				
			||||||
  csslib:
 | 
					  csslib:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -57,6 +97,22 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "1.0.8"
 | 
					    version: "1.0.8"
 | 
				
			||||||
 | 
					  dio:
 | 
				
			||||||
 | 
					    dependency: "direct main"
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: dio
 | 
				
			||||||
 | 
					      sha256: e17f6b3097b8c51b72c74c9f071a605c47bcc8893839bd66732457a5ebe73714
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "5.5.0+1"
 | 
				
			||||||
 | 
					  dio_web_adapter:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: dio_web_adapter
 | 
				
			||||||
 | 
					      sha256: "36c5b2d79eb17cdae41e974b7a8284fec631651d2a6f39a8a2ff22327e90aeac"
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "1.0.1"
 | 
				
			||||||
  fake_async:
 | 
					  fake_async:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -81,11 +137,27 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "7.0.0"
 | 
					    version: "7.0.0"
 | 
				
			||||||
 | 
					  fixnum:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: fixnum
 | 
				
			||||||
 | 
					      sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "1.1.0"
 | 
				
			||||||
  flutter:
 | 
					  flutter:
 | 
				
			||||||
    dependency: "direct main"
 | 
					    dependency: "direct main"
 | 
				
			||||||
    description: flutter
 | 
					    description: flutter
 | 
				
			||||||
    source: sdk
 | 
					    source: sdk
 | 
				
			||||||
    version: "0.0.0"
 | 
					    version: "0.0.0"
 | 
				
			||||||
 | 
					  flutter_cache_manager:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: flutter_cache_manager
 | 
				
			||||||
 | 
					      sha256: a77f77806a790eb9ba0118a5a3a936e81c4fea2b61533033b2b0c3d50bbde5ea
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "3.4.0"
 | 
				
			||||||
  flutter_lints:
 | 
					  flutter_lints:
 | 
				
			||||||
    dependency: "direct dev"
 | 
					    dependency: "direct dev"
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -98,10 +170,10 @@ packages:
 | 
				
			|||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: flutter_plugin_android_lifecycle
 | 
					      name: flutter_plugin_android_lifecycle
 | 
				
			||||||
      sha256: "8cf40eebf5dec866a6d1956ad7b4f7016e6c0cc69847ab946833b7d43743809f"
 | 
					      sha256: "9d98bd47ef9d34e803d438f17fd32b116d31009f534a6fa5ce3a1167f189a6de"
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "2.0.19"
 | 
					    version: "2.0.21"
 | 
				
			||||||
  flutter_test:
 | 
					  flutter_test:
 | 
				
			||||||
    dependency: "direct dev"
 | 
					    dependency: "direct dev"
 | 
				
			||||||
    description: flutter
 | 
					    description: flutter
 | 
				
			||||||
@@ -112,54 +184,86 @@ packages:
 | 
				
			|||||||
    description: flutter
 | 
					    description: flutter
 | 
				
			||||||
    source: sdk
 | 
					    source: sdk
 | 
				
			||||||
    version: "0.0.0"
 | 
					    version: "0.0.0"
 | 
				
			||||||
 | 
					  geocoding:
 | 
				
			||||||
 | 
					    dependency: "direct main"
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: geocoding
 | 
				
			||||||
 | 
					      sha256: d580c801cba9386b4fac5047c4c785a4e19554f46be42f4f5e5b7deacd088a66
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "3.0.0"
 | 
				
			||||||
 | 
					  geocoding_android:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: geocoding_android
 | 
				
			||||||
 | 
					      sha256: "1b13eca79b11c497c434678fed109c2be020b158cec7512c848c102bc7232603"
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "3.3.1"
 | 
				
			||||||
 | 
					  geocoding_ios:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: geocoding_ios
 | 
				
			||||||
 | 
					      sha256: "94ddba60387501bd1c11e18dca7c5a9e8c645d6e3da9c38b9762434941870c24"
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "3.0.1"
 | 
				
			||||||
 | 
					  geocoding_platform_interface:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: geocoding_platform_interface
 | 
				
			||||||
 | 
					      sha256: "8c2c8226e5c276594c2e18bfe88b19110ed770aeb7c1ab50ede570be8b92229b"
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "3.2.0"
 | 
				
			||||||
  google_maps:
 | 
					  google_maps:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: google_maps
 | 
					      name: google_maps
 | 
				
			||||||
      sha256: "47eef3836b49bb030d5cb3afc60b8451408bf34cf753e571b645d6529eb4251a"
 | 
					      sha256: "463b38e5a92a05cde41220a11fd5eef3847031fef3e8cf295ac76ec453246907"
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "7.1.0"
 | 
					    version: "8.0.0"
 | 
				
			||||||
  google_maps_flutter:
 | 
					  google_maps_flutter:
 | 
				
			||||||
    dependency: "direct main"
 | 
					    dependency: "direct main"
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: google_maps_flutter
 | 
					      name: google_maps_flutter
 | 
				
			||||||
      sha256: c1972cbad779bc5346c49045f26ae45550a0958b1cbca5b524dd3c8954995d28
 | 
					      sha256: acf0ec482d86b2ac55ade80597ce7f797a47971f5210ebfd030f0d58130e0a94
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "2.6.1"
 | 
					    version: "2.7.0"
 | 
				
			||||||
  google_maps_flutter_android:
 | 
					  google_maps_flutter_android:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: google_maps_flutter_android
 | 
					      name: google_maps_flutter_android
 | 
				
			||||||
      sha256: "0bcadb80eba39afda77dede89a6caafd3b68f2786b90491eceea4a01c3db181c"
 | 
					      sha256: "5d444f4135559488d7ea325eae710ae3284e6951b1b61729a0ac026456fe1548"
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "2.8.0"
 | 
					    version: "2.12.1"
 | 
				
			||||||
  google_maps_flutter_ios:
 | 
					  google_maps_flutter_ios:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: google_maps_flutter_ios
 | 
					      name: google_maps_flutter_ios
 | 
				
			||||||
      sha256: e5132d17f051600d90d79d9f574b177c24231da702453a036db2490f9ced4646
 | 
					      sha256: a6e3c6ecdda6c985053f944be13a0645ebb919da2ef0f5bc579c5e1670a5b2a8
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "2.6.0"
 | 
					    version: "2.10.0"
 | 
				
			||||||
  google_maps_flutter_platform_interface:
 | 
					  google_maps_flutter_platform_interface:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: google_maps_flutter_platform_interface
 | 
					      name: google_maps_flutter_platform_interface
 | 
				
			||||||
      sha256: "167af879da4d004cd58771f1469b91dcc3b9b0a2c5334cc6bf71fd41d4b35403"
 | 
					      sha256: bd60ca330e3c7763b95b477054adec338a522d982af73ecc520b232474063ac5
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "2.6.0"
 | 
					    version: "2.8.0"
 | 
				
			||||||
  google_maps_flutter_web:
 | 
					  google_maps_flutter_web:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: google_maps_flutter_web
 | 
					      name: google_maps_flutter_web
 | 
				
			||||||
      sha256: "0c0d5c723d94b295cf86dd1c45ff91d2ac1fff7c05ddca4f01bef9fa0a014690"
 | 
					      sha256: "8d5d0f58bfc4afac0bbe3d399f2018fcea691e3ea3d35254b7aae56df5827659"
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "0.5.7"
 | 
					    version: "0.5.9+1"
 | 
				
			||||||
  html:
 | 
					  html:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -172,10 +276,10 @@ packages:
 | 
				
			|||||||
    dependency: "direct main"
 | 
					    dependency: "direct main"
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: http
 | 
					      name: http
 | 
				
			||||||
      sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938"
 | 
					      sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "1.2.1"
 | 
					    version: "1.2.2"
 | 
				
			||||||
  http_parser:
 | 
					  http_parser:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -184,38 +288,22 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "4.0.2"
 | 
					    version: "4.0.2"
 | 
				
			||||||
  js:
 | 
					 | 
				
			||||||
    dependency: transitive
 | 
					 | 
				
			||||||
    description:
 | 
					 | 
				
			||||||
      name: js
 | 
					 | 
				
			||||||
      sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
 | 
					 | 
				
			||||||
      url: "https://pub.dev"
 | 
					 | 
				
			||||||
    source: hosted
 | 
					 | 
				
			||||||
    version: "0.6.7"
 | 
					 | 
				
			||||||
  js_wrapping:
 | 
					 | 
				
			||||||
    dependency: transitive
 | 
					 | 
				
			||||||
    description:
 | 
					 | 
				
			||||||
      name: js_wrapping
 | 
					 | 
				
			||||||
      sha256: e385980f7c76a8c1c9a560dfb623b890975841542471eade630b2871d243851c
 | 
					 | 
				
			||||||
      url: "https://pub.dev"
 | 
					 | 
				
			||||||
    source: hosted
 | 
					 | 
				
			||||||
    version: "0.7.4"
 | 
					 | 
				
			||||||
  leak_tracker:
 | 
					  leak_tracker:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: leak_tracker
 | 
					      name: leak_tracker
 | 
				
			||||||
      sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
 | 
					      sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "10.0.4"
 | 
					    version: "10.0.5"
 | 
				
			||||||
  leak_tracker_flutter_testing:
 | 
					  leak_tracker_flutter_testing:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: leak_tracker_flutter_testing
 | 
					      name: leak_tracker_flutter_testing
 | 
				
			||||||
      sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
 | 
					      sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "3.0.3"
 | 
					    version: "3.0.5"
 | 
				
			||||||
  leak_tracker_testing:
 | 
					  leak_tracker_testing:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -244,18 +332,34 @@ packages:
 | 
				
			|||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: material_color_utilities
 | 
					      name: material_color_utilities
 | 
				
			||||||
      sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
 | 
					      sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "0.8.0"
 | 
					    version: "0.11.1"
 | 
				
			||||||
  meta:
 | 
					  meta:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: meta
 | 
					      name: meta
 | 
				
			||||||
      sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
 | 
					      sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "1.12.0"
 | 
					    version: "1.15.0"
 | 
				
			||||||
 | 
					  nested:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: nested
 | 
				
			||||||
 | 
					      sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "1.0.0"
 | 
				
			||||||
 | 
					  octo_image:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: octo_image
 | 
				
			||||||
 | 
					      sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd"
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "2.1.0"
 | 
				
			||||||
  path:
 | 
					  path:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -264,6 +368,30 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "1.9.0"
 | 
					    version: "1.9.0"
 | 
				
			||||||
 | 
					  path_provider:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: path_provider
 | 
				
			||||||
 | 
					      sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "2.1.4"
 | 
				
			||||||
 | 
					  path_provider_android:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: path_provider_android
 | 
				
			||||||
 | 
					      sha256: "490539678396d4c3c0b06efdaab75ae60675c3e0c66f72bc04c2e2c1e0e2abeb"
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "2.2.9"
 | 
				
			||||||
 | 
					  path_provider_foundation:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: path_provider_foundation
 | 
				
			||||||
 | 
					      sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "2.4.0"
 | 
				
			||||||
  path_provider_linux:
 | 
					  path_provider_linux:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -284,18 +412,18 @@ packages:
 | 
				
			|||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: path_provider_windows
 | 
					      name: path_provider_windows
 | 
				
			||||||
      sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
 | 
					      sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "2.2.1"
 | 
					    version: "2.3.0"
 | 
				
			||||||
  platform:
 | 
					  platform:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: platform
 | 
					      name: platform
 | 
				
			||||||
      sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
 | 
					      sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "3.1.4"
 | 
					    version: "3.1.5"
 | 
				
			||||||
  plugin_platform_interface:
 | 
					  plugin_platform_interface:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -304,6 +432,22 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "2.1.8"
 | 
					    version: "2.1.8"
 | 
				
			||||||
 | 
					  provider:
 | 
				
			||||||
 | 
					    dependency: "direct main"
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: provider
 | 
				
			||||||
 | 
					      sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "6.1.2"
 | 
				
			||||||
 | 
					  rxdart:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: rxdart
 | 
				
			||||||
 | 
					      sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962"
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "0.28.0"
 | 
				
			||||||
  sanitize_html:
 | 
					  sanitize_html:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -316,58 +460,58 @@ packages:
 | 
				
			|||||||
    dependency: "direct main"
 | 
					    dependency: "direct main"
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: shared_preferences
 | 
					      name: shared_preferences
 | 
				
			||||||
      sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180
 | 
					      sha256: c3f888ba2d659f3e75f4686112cc1e71f46177f74452d40d8307edc332296ead
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "2.2.3"
 | 
					    version: "2.3.0"
 | 
				
			||||||
  shared_preferences_android:
 | 
					  shared_preferences_android:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: shared_preferences_android
 | 
					      name: shared_preferences_android
 | 
				
			||||||
      sha256: "1ee8bf911094a1b592de7ab29add6f826a7331fb854273d55918693d5364a1f2"
 | 
					      sha256: "041be4d9d2dc6079cf342bc8b761b03787e3b71192d658220a56cac9c04a0294"
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "2.2.2"
 | 
					    version: "2.3.0"
 | 
				
			||||||
  shared_preferences_foundation:
 | 
					  shared_preferences_foundation:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: shared_preferences_foundation
 | 
					      name: shared_preferences_foundation
 | 
				
			||||||
      sha256: "0a8a893bf4fd1152f93fec03a415d11c27c74454d96e2318a7ac38dd18683ab7"
 | 
					      sha256: "671e7a931f55a08aa45be2a13fe7247f2a41237897df434b30d2012388191833"
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "2.4.0"
 | 
					    version: "2.5.0"
 | 
				
			||||||
  shared_preferences_linux:
 | 
					  shared_preferences_linux:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: shared_preferences_linux
 | 
					      name: shared_preferences_linux
 | 
				
			||||||
      sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa"
 | 
					      sha256: "2ba0510d3017f91655b7543e9ee46d48619de2a2af38e5c790423f7007c7ccc1"
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "2.3.2"
 | 
					    version: "2.4.0"
 | 
				
			||||||
  shared_preferences_platform_interface:
 | 
					  shared_preferences_platform_interface:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: shared_preferences_platform_interface
 | 
					      name: shared_preferences_platform_interface
 | 
				
			||||||
      sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b"
 | 
					      sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "2.3.2"
 | 
					    version: "2.4.1"
 | 
				
			||||||
  shared_preferences_web:
 | 
					  shared_preferences_web:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: shared_preferences_web
 | 
					      name: shared_preferences_web
 | 
				
			||||||
      sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a"
 | 
					      sha256: "3a293170d4d9403c3254ee05b84e62e8a9b3c5808ebd17de6a33fe9ea6457936"
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "2.3.0"
 | 
					    version: "2.4.0"
 | 
				
			||||||
  shared_preferences_windows:
 | 
					  shared_preferences_windows:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: shared_preferences_windows
 | 
					      name: shared_preferences_windows
 | 
				
			||||||
      sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59"
 | 
					      sha256: "398084b47b7f92110683cac45c6dc4aae853db47e470e5ddcd52cab7f7196ab2"
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "2.3.2"
 | 
					    version: "2.4.0"
 | 
				
			||||||
  sky_engine:
 | 
					  sky_engine:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description: flutter
 | 
					    description: flutter
 | 
				
			||||||
@@ -389,6 +533,30 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "1.10.0"
 | 
					    version: "1.10.0"
 | 
				
			||||||
 | 
					  sprintf:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: sprintf
 | 
				
			||||||
 | 
					      sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "7.0.0"
 | 
				
			||||||
 | 
					  sqflite:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: sqflite
 | 
				
			||||||
 | 
					      sha256: a43e5a27235518c03ca238e7b4732cf35eabe863a369ceba6cbefa537a66f16d
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "2.3.3+1"
 | 
				
			||||||
 | 
					  sqflite_common:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: sqflite_common
 | 
				
			||||||
 | 
					      sha256: "3da423ce7baf868be70e2c0976c28a1bb2f73644268b7ffa7d2e08eab71f16a4"
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "2.5.4"
 | 
				
			||||||
  stack_trace:
 | 
					  stack_trace:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -421,6 +589,14 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "1.2.0"
 | 
					    version: "1.2.0"
 | 
				
			||||||
 | 
					  synchronized:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: synchronized
 | 
				
			||||||
 | 
					      sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558"
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "3.1.0+1"
 | 
				
			||||||
  term_glyph:
 | 
					  term_glyph:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -433,10 +609,10 @@ packages:
 | 
				
			|||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: test_api
 | 
					      name: test_api
 | 
				
			||||||
      sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
 | 
					      sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "0.7.0"
 | 
					    version: "0.7.2"
 | 
				
			||||||
  typed_data:
 | 
					  typed_data:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -445,6 +621,14 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "1.3.2"
 | 
					    version: "1.3.2"
 | 
				
			||||||
 | 
					  uuid:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: uuid
 | 
				
			||||||
 | 
					      sha256: "83d37c7ad7aaf9aa8e275490669535c8080377cfa7a7004c24dfac53afffaa90"
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "4.4.2"
 | 
				
			||||||
  vector_math:
 | 
					  vector_math:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -457,10 +641,10 @@ packages:
 | 
				
			|||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: vm_service
 | 
					      name: vm_service
 | 
				
			||||||
      sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
 | 
					      sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "14.2.1"
 | 
					    version: "14.2.4"
 | 
				
			||||||
  web:
 | 
					  web:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -469,14 +653,14 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "0.5.1"
 | 
					    version: "0.5.1"
 | 
				
			||||||
  win32:
 | 
					  widget_to_marker:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: "direct main"
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: win32
 | 
					      name: widget_to_marker
 | 
				
			||||||
      sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4
 | 
					      sha256: badc36f23c76f3ca9d43d7780058096be774adf0f661bdb6eb6f6b893f648ab9
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "5.5.1"
 | 
					    version: "1.0.6"
 | 
				
			||||||
  xdg_directories:
 | 
					  xdg_directories:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -487,4 +671,4 @@ packages:
 | 
				
			|||||||
    version: "1.0.4"
 | 
					    version: "1.0.4"
 | 
				
			||||||
sdks:
 | 
					sdks:
 | 
				
			||||||
  dart: ">=3.4.0 <4.0.0"
 | 
					  dart: ">=3.4.0 <4.0.0"
 | 
				
			||||||
  flutter: ">=3.19.0"
 | 
					  flutter: ">=3.22.0"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
name: "fast_network_navigation"
 | 
					name: "anyway"
 | 
				
			||||||
description: "An interactive city navigator."
 | 
					description: "A customizable, agile city navigator for your trips."
 | 
				
			||||||
# The following line prevents the package from being accidentally published to
 | 
					# The following line prevents the package from being accidentally published to
 | 
				
			||||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
 | 
					# pub.dev using `flutter pub publish`. This is preferred for private packages.
 | 
				
			||||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
 | 
					publish_to: 'none' # Remove this line if you wish to publish to pub.dev
 | 
				
			||||||
@@ -36,9 +36,15 @@ dependencies:
 | 
				
			|||||||
  # Use with the CupertinoIcons class for iOS style icons.
 | 
					  # Use with the CupertinoIcons class for iOS style icons.
 | 
				
			||||||
  cupertino_icons: ^1.0.6
 | 
					  cupertino_icons: ^1.0.6
 | 
				
			||||||
  sliding_up_panel: ^2.0.0+1
 | 
					  sliding_up_panel: ^2.0.0+1
 | 
				
			||||||
  google_maps_flutter: ^2.6.1
 | 
					 | 
				
			||||||
  http: ^1.2.1
 | 
					  http: ^1.2.1
 | 
				
			||||||
  shared_preferences: ^2.2.3
 | 
					  shared_preferences: ^2.2.3
 | 
				
			||||||
 | 
					  dio: ^5.5.0+1
 | 
				
			||||||
 | 
					  google_maps_flutter: ^2.7.0
 | 
				
			||||||
 | 
					  cached_network_image: ^3.4.0
 | 
				
			||||||
 | 
					  geocoding: ^3.0.0
 | 
				
			||||||
 | 
					  widget_to_marker: ^1.0.6
 | 
				
			||||||
 | 
					  provider: ^6.1.2
 | 
				
			||||||
 | 
					  auto_size_text: ^3.0.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dev_dependencies:
 | 
					dev_dependencies:
 | 
				
			||||||
  flutter_test:
 | 
					  flutter_test:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,8 +8,8 @@
 | 
				
			|||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
import 'package:flutter_test/flutter_test.dart';
 | 
					import 'package:flutter_test/flutter_test.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// import 'package:fast_network_navigation/main.dart';
 | 
					// import 'package:anyway/main.dart';
 | 
				
			||||||
import 'package:fast_network_navigation/layout.dart';
 | 
					import 'package:anyway/layout.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void main() {
 | 
					void main() {
 | 
				
			||||||
  testWidgets('Counter increments smoke test', (WidgetTester tester) async {
 | 
					  testWidgets('Counter increments smoke test', (WidgetTester tester) async {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,13 +24,13 @@
 | 
				
			|||||||
  <!-- iOS meta tags & icons -->
 | 
					  <!-- iOS meta tags & icons -->
 | 
				
			||||||
  <meta name="apple-mobile-web-app-capable" content="yes">
 | 
					  <meta name="apple-mobile-web-app-capable" content="yes">
 | 
				
			||||||
  <meta name="apple-mobile-web-app-status-bar-style" content="black">
 | 
					  <meta name="apple-mobile-web-app-status-bar-style" content="black">
 | 
				
			||||||
  <meta name="apple-mobile-web-app-title" content="fast_network_navigation">
 | 
					  <meta name="apple-mobile-web-app-title" content="anyway">
 | 
				
			||||||
  <link rel="apple-touch-icon" href="icons/Icon-192.png">
 | 
					  <link rel="apple-touch-icon" href="icons/Icon-192.png">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <!-- Favicon -->
 | 
					  <!-- Favicon -->
 | 
				
			||||||
  <link rel="icon" type="image/png" href="favicon.png"/>
 | 
					  <link rel="icon" type="image/png" href="favicon.png"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <title>fast_network_navigation</title>
 | 
					  <title>anyway</title>
 | 
				
			||||||
  <link rel="manifest" href="manifest.json">
 | 
					  <link rel="manifest" href="manifest.json">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <script>
 | 
					  <script>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    "name": "fast_network_navigation",
 | 
					    "name": "anyway",
 | 
				
			||||||
    "short_name": "fast_network_navigation",
 | 
					    "short_name": "anyway",
 | 
				
			||||||
    "start_url": ".",
 | 
					    "start_url": ".",
 | 
				
			||||||
    "display": "standalone",
 | 
					    "display": "standalone",
 | 
				
			||||||
    "background_color": "#0175C2",
 | 
					    "background_color": "#0175C2",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,10 @@
 | 
				
			|||||||
# Project-level configuration.
 | 
					# Project-level configuration.
 | 
				
			||||||
cmake_minimum_required(VERSION 3.14)
 | 
					cmake_minimum_required(VERSION 3.14)
 | 
				
			||||||
project(fast_network_navigation LANGUAGES CXX)
 | 
					project(anyway LANGUAGES CXX)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# The name of the executable created for the application. Change this to change
 | 
					# The name of the executable created for the application. Change this to change
 | 
				
			||||||
# the on-disk name of your application.
 | 
					# the on-disk name of your application.
 | 
				
			||||||
set(BINARY_NAME "fast_network_navigation")
 | 
					set(BINARY_NAME "anyway")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
 | 
					# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
 | 
				
			||||||
# versions of CMake.
 | 
					# versions of CMake.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -90,12 +90,12 @@ BEGIN
 | 
				
			|||||||
        BLOCK "040904e4"
 | 
					        BLOCK "040904e4"
 | 
				
			||||||
        BEGIN
 | 
					        BEGIN
 | 
				
			||||||
            VALUE "CompanyName", "com.example" "\0"
 | 
					            VALUE "CompanyName", "com.example" "\0"
 | 
				
			||||||
            VALUE "FileDescription", "fast_network_navigation" "\0"
 | 
					            VALUE "FileDescription", "anyway" "\0"
 | 
				
			||||||
            VALUE "FileVersion", VERSION_AS_STRING "\0"
 | 
					            VALUE "FileVersion", VERSION_AS_STRING "\0"
 | 
				
			||||||
            VALUE "InternalName", "fast_network_navigation" "\0"
 | 
					            VALUE "InternalName", "anyway" "\0"
 | 
				
			||||||
            VALUE "LegalCopyright", "Copyright (C) 2024 com.example. All rights reserved." "\0"
 | 
					            VALUE "LegalCopyright", "Copyright (C) 2024 com.example. All rights reserved." "\0"
 | 
				
			||||||
            VALUE "OriginalFilename", "fast_network_navigation.exe" "\0"
 | 
					            VALUE "OriginalFilename", "anyway.exe" "\0"
 | 
				
			||||||
            VALUE "ProductName", "fast_network_navigation" "\0"
 | 
					            VALUE "ProductName", "anyway" "\0"
 | 
				
			||||||
            VALUE "ProductVersion", VERSION_AS_STRING "\0"
 | 
					            VALUE "ProductVersion", VERSION_AS_STRING "\0"
 | 
				
			||||||
        END
 | 
					        END
 | 
				
			||||||
    END
 | 
					    END
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,7 +27,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
 | 
				
			|||||||
  FlutterWindow window(project);
 | 
					  FlutterWindow window(project);
 | 
				
			||||||
  Win32Window::Point origin(10, 10);
 | 
					  Win32Window::Point origin(10, 10);
 | 
				
			||||||
  Win32Window::Size size(1280, 720);
 | 
					  Win32Window::Size size(1280, 720);
 | 
				
			||||||
  if (!window.Create(L"fast_network_navigation", origin, size)) {
 | 
					  if (!window.Create(L"anyway", origin, size)) {
 | 
				
			||||||
    return EXIT_FAILURE;
 | 
					    return EXIT_FAILURE;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  window.SetQuitOnClose(true);
 | 
					  window.SetQuitOnClose(true);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +0,0 @@
 | 
				
			|||||||
{
 | 
					 | 
				
			||||||
  "$schema": "https://docs.renovatebot.com/renovate-schema.json"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
		Reference in New Issue
	
	Block a user