Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/build_app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ jobs:
--dart-define=THUNDERFOREST_API_KEY=${{ secrets.THUNDERFOREST_API_KEY }}
--dart-define=IS_RELEASE=true

# Required because F-Droid signs its apk with apksigner which produces a slightly different output than gradle signingConfig
# Required because F-Droid signs its apk with apksigner which produces a slightly different output than Gradle signingConfig
# see: https://f-droid.org/docs/Reproducible_Builds/#reproducible-signatures
- name: Sign with apksigner
run: >-
Expand Down Expand Up @@ -231,8 +231,8 @@ jobs:
--dart-define=THUNDERFOREST_API_KEY=${{ secrets.THUNDERFOREST_API_KEY }}
--dart-define=IS_RELEASE=true

# This step is mainly required because Google considers a link to a Github markdown file as "editable" which violates their policy.
# Therefore we convert the markdown file to HTML and publish it together with the App via Github pages here.
# This step is mainly required because Google considers a link to a GitHub Markdown file as "editable" which violates their policy.
# Therefore, we convert the Markdown file to HTML and publish it together with the App via GitHub pages here.
- name: Convert Privacy Policy to HTML
uses: BaileyJM02/markdown-to-pdf@v1.2.0
with:
Expand Down
20 changes: 11 additions & 9 deletions android/app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
plugins {
id("com.android.application")
id("kotlin-android")
id("dev.flutter.flutter-gradle-plugin")
}

Expand All @@ -27,17 +26,14 @@ if (keystorePropertiesFile.exists()) {

android {
namespace = "de.tu_chemnitz.etit.sse.openstop"
compileSdk = 36
compileSdk = 37
// Momentary fix. Alternatively the following line can also be commented out. See: https://github.com/flutter/flutter/issues/139427
ndkVersion = "27.0.12077973"
ndkVersion = "28.2.13676358"
//ndkVersion = flutter.ndkVersion

compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}

kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.toString()
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

sourceSets["main"].java.srcDirs("src/main/kotlin")
Expand Down Expand Up @@ -74,6 +70,12 @@ android {
}
}

kotlin {
compilerOptions {
jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17
}
}

flutter {
source = "../.."
}
3 changes: 2 additions & 1 deletion android/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true
android.newDsl=false
android.builtInKotlin=false
2 changes: 1 addition & 1 deletion android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-all.zip
4 changes: 2 additions & 2 deletions android/settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ pluginManagement {

plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.9.1" apply false
id("org.jetbrains.kotlin.android") version "2.1.0" apply false
id("com.android.application") version "9.0.1" apply false
id("org.jetbrains.kotlin.android") version "2.3.20" apply false
}

include(":app")
4 changes: 2 additions & 2 deletions docs/BUILD.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Prerequisites

Install and setup the [Flutter SDK](https://docs.flutter.dev/get-started/install).
Install and set up the [Flutter SDK](https://docs.flutter.dev/get-started/install).

## Build process

Expand All @@ -25,7 +25,7 @@ You can also omit the entire API key parameter. In this case the underlying map

## Additional notes

By default the app uses the [OpenStreetMap development server](https://master.apis.dev.openstreetmap.org) for login and upload. In order to use the live servers the custom parameter `--dart-define=IS_RELEASE=true` has to be set when running/building the app.
By default, the app uses the [OpenStreetMap development server](https://master.apis.dev.openstreetmap.org) for login and upload. In order to use the live servers the custom parameter `--dart-define=IS_RELEASE=true` has to be set when running/building the app.
**Note:** This has nothing to do with flutter's *release*, *profile* and *debug* mode.

Due to the usage of verified App Link or Universal Links respectively, login on the OpenStreetMap server won't work without signing the app. In order to enable login the files `app_config.dart`, `osm_config.dart`, `build.gradle` and `Runner.entitlements` need to be configured accordingly.
Expand Down
16 changes: 8 additions & 8 deletions docs/QUESTION_CATALOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ Displays a number wheel for each specified time unit. Possible time units are `d
The duration is always completely returned to the constructor, meaning no duration inputs are lost.
If for example the input is in hours, minutes and seconds, but only hours is marked as a return value, the hours value will include the minutes and seconds in its representation (potentially in the fractional part). Make sure that the `constructor` only generates the permitted values of the corresponding tag.

The `$input` variable will contain all duration values marked with `return: true` in the following order: `days`, `hours`, `minutes` and `seconds`. Therefore at least one duration value should have `return": true`.
The `$input` variable will contain all duration values marked with `return: true` in the following order: `days`, `hours`, `minutes` and `seconds`. Therefore, at least one duration value should have `return": true`.

```jsonc
"answer": {
Expand Down Expand Up @@ -337,8 +337,8 @@ output: `operator=_alu_`

### Answer examples

#### Multiple values using the semi-colon value separator
The example will write all selected values to the *cuisine* tag separated by semi-colon.
#### Multiple values using the semicolon value separator
The example will write all selected values to the *cuisine* tag separated by semicolon.
**Explanation:** The `$input` variable will contain all selected values, which will be concatenated by the `JOIN` expression.

```jsonc
Expand Down Expand Up @@ -377,7 +377,7 @@ The example will write all selected values to the *cuisine* tag separated by sem
```

#### Multiple values using multiple tags
The example will write the three tags *bus*, *tram* & *train*. For unselected options the values will fallback to *no*.
The example will write the three tags *bus*, *tram* & *train*. For unselected options the values will fall back to *no*.
**Explanation:** The `$input` variable will be empty for unselected options. Because `COALESCE` evaluates to the first value/argument it will output *no* in this case. If the fallback value is omitted then the expression will evaluate to `null` which means that the tag won't be written.
```jsonc
"answer": {
Expand Down Expand Up @@ -413,7 +413,7 @@ The example will write the three tags *bus*, *tram* & *train*. For unselected op
#### Using expressions to convert centimeters to meters
The example makes use of the 3 expressions PAD, INSERT and REPLACE to convert from centimeters to meters. The REPLACE expression is only used to remove any pending zeros (and potentially the decimal point). Note that this expression combination only works for positive integers (not for negative or decimal numbers) and does a conversion by exactly two decimal places to the left.

**Explanation:** The expressions evaluate from the inner most to the outer most as shown in the table below.
**Explanation:** The expressions evaluate from the innermost to the outermost as shown in the table below.

| $input | PAD | INSERT | REPLACE |
| ------ | ----- | ------ | ------- |
Expand Down Expand Up @@ -528,7 +528,7 @@ In order to match elements that have or don't have a certain key one can set the
- `"some_osm_key": true` means that the element must have a tag with the key `some_osm_key` while its **value can be anything**.
- `"some_osm_key": false` means that the element **must not** have a tag with the key `some_osm_key`.

To match against multiple values of the same key one could write multiple conditions. However this will often result in a lot of repetitive code wherefore a shorthand array notation exists:
To match against multiple values of the same key one could write multiple conditions. However, this will often result in a lot of repetitive code wherefore a shorthand array notation exists:
`highway": ["motorway ", "trunk", "primary"]` The previous example will match any elements that contain the key `highway` with a value of either `motorway`, `trunk` or `primary`. You can also use `true`, `false` and regular expressions in this notation.

For more complex tag matching scenarios **Regular expressions** can be used. They are written as normal strings enclosed by slashes (`/`) and use Dart's regular expression syntax and semantics, which is the same as for [JavaScript regular expressions](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Regular_Expressions).
Expand All @@ -537,7 +537,7 @@ Example use cases are:
- matching a tag that contains certain value `/INCLUDED_VALUE/`.
- matching a tag that contains a certain value between a separator string (e.g. semicolon) `/(^|^.+;)LIST_VALUE(;.+$|$)/`.

Currently setting regex flags is not supported. All of them are turned off except for the *match case sensitive* flag.
Currently, setting regex flags is not supported. All of them are turned off except for the *match case-sensitive* flag.

**Notation summary:**

Expand All @@ -562,7 +562,7 @@ This defines the element type or types the element must have in order to evaluat

Possible types are: `Node`, `OpenWay`, `ClosedWay` and `Relation`

In contrast to the standard OSM element types, way is split into two sub-types to distinguish between area/boundary and path/segment elements.
In contrast to the standard OSM element types, way is split into two subtypes to distinguish between area/boundary and path/segment elements.

### `child` condition

Expand Down
4 changes: 2 additions & 2 deletions docs/WORKING_PRINCIPLE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
# Working Principle

The app aims to contribute OSM data via surveys and shall be used on-site. It is designed to be simple and usable without much prior knowledge.
The app does **not** try to be a full featured editor for complex mapping scenarios like JOSM, ID or Vespucci by any means.
The app does **not** try to be a full-featured editor for complex mapping scenarios like JOSM, ID or Vespucci by any means.

## Core principles:
- The app only ever adds tags or elements and never deletes anything.
- Changes must be uploaded via a personal OSM user account and users authorize/login via OAuth2.
- Users never enter any tags directly, instead the app phrases questions and transforms the answers into OSM tags (similar to [StreetComplete](https://github.com/streetcomplete/StreetComplete)).

## Target Elements:
The app focuses on public transport related OSM elements. Therefore we first gather all nearby stops via the [Overpass API](https://wiki.openstreetmap.org/wiki/Overpass_API). Based on these stops we generate "stop areas", which are basically just circles enclosing one or more nearby stops with a minimal predefined radius. These make up the areas where we are looking for OSM elements via the [OSM API](https://wiki.openstreetmap.org/wiki/API_v0.6#Bounding_box_computation). Our main focus lies on elements with the key `public_transport=platform`. Some further relevant elements are: footways, steps, parking areas or toilets around the stop.
The app focuses on public transport related OSM elements. Therefore, we first gather all nearby stops via the [Overpass API](https://wiki.openstreetmap.org/wiki/Overpass_API). Based on these stops we generate "stop areas", which are basically just circles enclosing one or more nearby stops with a minimal predefined radius. These make up the areas where we are looking for OSM elements via the [OSM API](https://wiki.openstreetmap.org/wiki/API_v0.6#Bounding_box_computation). Our main focus lies on elements with the key `public_transport=platform`. Some further relevant elements are: footways, steps, parking areas or toilets around the stop.

## Matching:

Expand Down
2 changes: 0 additions & 2 deletions ios/Flutter/AppFrameworkInfo.plist
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,5 @@
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>13.0</string>
</dict>
</plist>
22 changes: 22 additions & 0 deletions ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; };
/* End PBXBuildFile section */

/* Begin PBXCopyFilesBuildPhase section */
Expand Down Expand Up @@ -50,13 +51,15 @@
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
C0C3EFFB315866C9D5691CD7 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
E92325C28327D7C47838E558 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */,
89C6123D110CC5300270504C /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand All @@ -75,6 +78,7 @@
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */,
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
Expand Down Expand Up @@ -133,6 +137,9 @@

/* Begin PBXNativeTarget section */
97C146ED1CF9000F007C117D /* Runner */ = {
packageProductDependencies = (
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */,
);
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
Expand All @@ -158,6 +165,9 @@

/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
packageReferences = (
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */,
);
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1510;
Expand Down Expand Up @@ -590,6 +600,18 @@
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
/* Begin XCLocalSwiftPackageReference section */
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = {
isa = XCLocalSwiftPackageReference;
relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage;
};
/* End XCLocalSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = {
isa = XCSwiftPackageProductDependency;
productName = FlutterGeneratedPluginSwiftPackage;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}
18 changes: 18 additions & 0 deletions ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,24 @@
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<PreActions>
<ExecutionAction
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
<ActionContent
title = "Run Prepare Flutter Framework Script"
scriptText = "/bin/sh &quot;$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh&quot; prepare&#10;">
<EnvironmentBuildable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</EnvironmentBuildable>
</ActionContent>
</ExecutionAction>
</PreActions>
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
Expand Down
1 change: 0 additions & 1 deletion lib/api/osm_authentication_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ class OSMAuthenticationAPI {
callbackUrlScheme: kAppCallbackUrlScheme,
options: const FlutterWebAuth2Options(
preferEphemeral: true,
intentFlags: ephemeralIntentFlags,
httpsHost: kAppCallbackUrlHost,
httpsPath: kAppCallbackUrlPath,
),
Expand Down
4 changes: 2 additions & 2 deletions lib/models/questionnaire.dart
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,8 @@ class QuestionnaireEntry<T extends Answer> {

QuestionnaireEntry copyWith({QuestionDefinition? question, T? answer}) {
return QuestionnaireEntry(
question = question ?? this.question,
answer = answer ?? this.answer,
question ?? this.question,
answer ?? this.answer,
);
}

Expand Down
9 changes: 6 additions & 3 deletions lib/view_models/home_view_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import 'package:animated_location_indicator/animated_location_indicator.dart';
import 'package:collection/collection.dart';
import 'package:dio/dio.dart';
import 'package:flutter/semantics.dart';
import 'package:flutter/widgets.dart' hide Action, ProxyElement, Notification;
import 'package:flutter/widgets.dart' hide Action, ProxyElement, Notification, View;
import 'package:flutter/widgets.dart' as widgets show View;
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_cache/flutter_map_cache.dart';
import 'package:flutter_mvvm_architecture/base.dart';
Expand Down Expand Up @@ -375,7 +376,8 @@ class HomeViewModel extends ViewModel
_appWorker.openQuestionnaire(element);
runInAction(() => _selectedElement.value = element);
// semantic notification
SemanticsService.announce(
SemanticsService.sendAnnouncement(
widgets.View.of(context),
appLocale.semanticsOpenQuestionnaireAnnounce,
Directionality.of(context),
);
Expand All @@ -392,7 +394,8 @@ class HomeViewModel extends ViewModel
// deselect element
runInAction(() => _selectedElement.value = null);
// semantic notification
SemanticsService.announce(
SemanticsService.sendAnnouncement(
widgets.View.of(context),
appLocale.semanticsCloseQuestionnaireAnnounce,
Directionality.of(context),
);
Expand Down
2 changes: 1 addition & 1 deletion lib/widgets/question_dialog/question_text_header.dart
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ class _QuestionTextHeaderState extends State<QuestionTextHeader>
if (hasAdditionalInfo)
ExcludeSemantics(
child: SizeTransition(
axisAlignment: -1,
alignment: AlignmentDirectional.centerStart,
sizeFactor: _sizeAnimation,
child: FadeTransition(
opacity: _fadeAnimation,
Expand Down
2 changes: 1 addition & 1 deletion lib/widgets/question_inputs/list_input.dart
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ class _ListInputItemState extends State<ListInputItem> with SingleTickerProvider
if (widget.description != null)
ExcludeSemantics(
child: SizeTransition(
axisAlignment: -1,
alignment: AlignmentDirectional.centerStart,
sizeFactor: _animation,
child: FadeTransition(
opacity: _animation,
Expand Down
Loading
Loading