Commit edd9d4cc authored by Jamili, Aria's avatar Jamili, Aria
Browse files

Merge branch 'dev' into 'master'

Last-Release

See merge request !46
parents 196d01d9 44e52f88
......@@ -28,7 +28,7 @@ The frontend consists of flutter framework using dart programming language
<details>
<summary>Home Screen</summary>
<img src="./readme_assets/home.png" alt="Home" width="300" height="600">
<img src="./assets/images/home.gif" alt="map" width="300" height="600">
</details>
</p>
......@@ -38,7 +38,16 @@ The frontend consists of flutter framework using dart programming language
<details>
<summary>Map Screen</summary>
<img src="./readme_assets/map.png" alt="map" width="300" height="600">
<img src="./assets/images/map.gif" alt="map" width="300" height="600">
</details>
</p>
<p>
<details>
<summary>Exhibitions Screen</summary>
<img src="./assets/images/exhibitions.gif" alt="map" width="300" height="600">
</details>
</p>
......@@ -47,7 +56,7 @@ The frontend consists of flutter framework using dart programming language
<details>
<summary>Audio-Guide Screen</summary>
<img src="./readme_assets/audio-guide.png" alt="audio" width="300" height="600">
<img src="./assets/images/audio-Guide.gif" alt="map" width="300" height="600">
</details>
</p>
......@@ -57,7 +66,7 @@ The frontend consists of flutter framework using dart programming language
<details>
<summary>Setting Screen</summary>
<img src="./readme_assets/setting.png" alt="setting" width="300" height="600">
<img src="./assets/images/setting.gif" alt="map" width="300" height="600">
</details>
</p>
......@@ -66,7 +75,7 @@ The frontend consists of flutter framework using dart programming language
<details>
<summary>Search Screen</summary>
<img src="./readme_assets/search.png" alt="search" width="300" height="700">
<img src="./assets/images/search.png" alt="search" width="300" height="700">
</details>
</p>
......
buildscript {
ext.kotlin_version = '1.5.20'
ext.kotlin_version = '1.5.21'
repositories {
google()
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.0.0'
classpath 'com.android.tools.build:gradle:7.0.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
......@@ -14,7 +14,7 @@ buildscript {
allprojects {
repositories {
google()
jcenter()
mavenCentral()
}
}
......
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:kunstforum_tu_darmstadt/models/app_info.dart';
//***********************************************
//***** Custom Imports *******
......@@ -61,7 +62,8 @@ class ApiConnection {
}
//print('Response body: ${response.body}');
return Artwork.fromJson(
jsonDecode(response.body.substring(1, response.body.length - 1)), apiLink);
jsonDecode(response.body.substring(1, response.body.length - 1)),
apiLink);
} catch (e, s) {
print("fetching artwork by id failed, error:");
print(e);
......@@ -81,7 +83,8 @@ class ApiConnection {
}
//print('Response body: ${response.body}');
return Artwork.fromJson(
jsonDecode(response.body.substring(1, response.body.length - 1)), apiLink);
jsonDecode(response.body.substring(1, response.body.length - 1)),
apiLink);
} catch (e, s) {
print("fetching artwork by name failed, error:");
print(e);
......@@ -109,4 +112,24 @@ class ApiConnection {
}
}
/// get App Info
Future<AppInfo?> fetchInfo() async {
var url = Uri.parse(apiLink + 'info');
try {
http.Response response = await http.get(url);
if ('${response.body}' == '') {
print("response body is empty");
return null;
}
//print('Response body: ${response.body}');
return AppInfo.fromJson(
jsonDecode(response.body.substring(0, response.body.length )),
apiLink);
} catch (e, s) {
print("fetching info failed, error:");
print(e);
print(s);
return null;
}
}
}
......@@ -6,6 +6,7 @@ import 'package:flutter/cupertino.dart';
import 'package:kunstforum_tu_darmstadt/api/api_connection.dart';
import 'package:kunstforum_tu_darmstadt/models/artwork.dart';
import 'package:kunstforum_tu_darmstadt/models/exhibition.dart';
import 'package:kunstforum_tu_darmstadt/models/app_info.dart';
///
/// A [ChangeNotifier] providing globally access to the API.
......@@ -30,6 +31,9 @@ class ArtworkService extends ChangeNotifier {
/// The list of [Artwork]s that are favourite
List<Artwork> _favouriteArtworkList = [];
/// The [AppInfo]
late AppInfo _appInfo;
//***************************************************
//****** class getters **********
//***************************************************
......@@ -46,6 +50,9 @@ class ArtworkService extends ChangeNotifier {
/// The list of [Artwork]s that are favourite
List<Artwork> get favouriteArtworkList => _favouriteArtworkList;
/// get app info
AppInfo get appInfo => _appInfo;
//***************************************************
//****** class setters **********
//***************************************************
......@@ -66,6 +73,7 @@ class ArtworkService extends ChangeNotifier {
print("fetching");
_artworkList = (await ApiConnection().fetchArtworks())!;
_exhibitionList = (await ApiConnection().fetchExhibitions())!;
_appInfo = (await ApiConnection().fetchInfo())!;
print(
"Got ${_artworkList.length} Artworks and ${_exhibitionList.length} Exhibitions");
......
......@@ -42,6 +42,9 @@ class AppSetting extends ChangeNotifier {
/// The preferred height of the custom app bar
Size _cardSize = Size(40, 40);
/// The preferred height of the IntroScreen dots
Size _dotSize = Size(10.0, 10.0);
/// The device typ indication display size
DeviceTyp? _deviceTyp;
......@@ -74,6 +77,12 @@ class AppSetting extends ChangeNotifier {
/// The preferred font size of image caption text
double _captionTextFontSize = 8;
/// The preferred font size of title in IntroScreen
double _introTileFontSize = 20;
/// The preferred font size of body in IntroScreen
double _introBodyFontSize = 16;
//***************************************************
//****** class getters **********
//***************************************************
......@@ -100,6 +109,9 @@ class AppSetting extends ChangeNotifier {
/// The preferred height of the custom app bar
Size get cardSize => _cardSize;
/// The preferred height of the IntroScreen dots
Size get dotSize => _dotSize;
/// The device typ indication display size
DeviceTyp? get deviceTyp => _deviceTyp;
......@@ -129,6 +141,12 @@ class AppSetting extends ChangeNotifier {
/// The preferred font size of Button text
double get buttonTextFontSize => _buttonTextFontSize;
/// The preferred font size of title in IntroScreen
double get introTileFontSize => _introTileFontSize;
/// The preferred font size of body in IntroScreen
double get introBodyFontSize => _introBodyFontSize;
//***************************************************
//****** class setters **********
//***************************************************
......@@ -246,10 +264,16 @@ class AppSetting extends ChangeNotifier {
mapMarkerIconSize = 40;
} else {
_deviceTyp = DeviceTyp.bigDisplay;
_appBarHeight = _mediaQuery!.size.height / 15;
_appBarHeight = _mediaQuery!.size.height / 12;
_iconSize = 40;
_cardSize = Size(100, 100);
mapMarkerIconSize = 50;
_dotSize = Size(15, 15);
_introBodyFontSize = 30;
_introTileFontSize = 40;
captionTextFontSize = 20;
_bodyTextFontSize = 20;
_bodyTitleFontSize = 25;
}
}
......@@ -263,7 +287,6 @@ class AppSetting extends ChangeNotifier {
} else {
_deviceTyp = DeviceTyp.bigDisplay;
}
int _primaryColorValue = json['_primaryColorValue'];
_primaryColor = MaterialColor(_primaryColorValue, {
50: Color.fromRGBO(json['_primaryColorRed'], json['_primaryColorGreen'],
......
......@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:kunstforum_tu_darmstadt/ui/screens/audio_guide_screen.dart';
import 'package:kunstforum_tu_darmstadt/ui/screens/exhibition_screen.dart';
import 'package:kunstforum_tu_darmstadt/ui/screens/exhibitions_screen.dart';
import 'package:kunstforum_tu_darmstadt/ui/screens/introduction_screen.dart';
import 'package:kunstforum_tu_darmstadt/ui/screens/loading_screen.dart';
//***********************************************
......@@ -19,8 +20,17 @@ class RouteGenerator {
/// A [String] representing route Name of audio guide screen
static const String audioGuideScreenRouteName = '/audio';
/// A [String] representing route Name of exhibition screen
static const String exhibitionScreenRouteName = '/exhibition';
/// A [String] representing route Name of exhibitions screen
static const String exhibitionsScreenRouteName = '/exhibitions';
/// A [String] representing route Name of loading screen
static const String loadingScreenRouteName = '/loading';
/// A [String] representing route Name of loading screen
static const String loadingScreenRouteName = '/';
static const String introScreenRouteName = '/intro';
/// A [String] representing route Name of settings screen
static const String settingsScreenRouteName = '/settings';
......@@ -31,12 +41,6 @@ class RouteGenerator {
/// A [String] representing route Name of map screen
static const String mapScreenRouteName = '/map';
/// A [String] representing route Name of exhibition screen
static const String exhibitionScreenRouteName = '/exhibition';
/// A [String] representing route Name of exhibitions screen
static const String exhibitionsScreenRouteName = '/exhibitions';
///
/// Returns a [MaterialPageRoute] for the give [RouteSettings].
///
......@@ -76,6 +80,8 @@ class RouteGenerator {
));
case exhibitionsScreenRouteName:
return MaterialPageRoute(builder: (context) => ExhibitionsScreen());
case introScreenRouteName:
return MaterialPageRoute(builder: (context) => IntroScreen());
default:
// If there is no such named route in the switch statement
return _errorRoute();
......
......@@ -31,5 +31,21 @@
"medium": "Medium",
"large": "Groß",
"scanQRCode": "QR-Code scannen",
"alertNoWifi": "Diese App benötigt eine Internetverbindung. Bitte verbinden Sie sich mit dem Internet und drücken Sie den unteren Button."
"alertNoWifi": "Diese App benötigt eine Internetverbindung. Bitte verbinden Sie sich mit dem Internet und drücken Sie den unteren Button.",
"showInMap": "In Karte öffnen",
"artworkNotFound":"Das gesuchte Kunstwerk wurde nicht gefunden!",
"introScreenTitle1": "Willkommen!",
"introScreenTitle2": "Home Screen",
"introScreenTitle3": "Audio-Guide",
"introScreenTitle4": "Karte",
"introScreenTitle5": "Ausstellungen",
"introScreenTitle6": "Einstellungen",
"introScreenSubTitle1": "Swipe nach links für eine Einführung zu den App-Features!",
"introScreenSubTitle2": "Hier werden Campus-Kunst und andere Ausstellungen angezeigt.",
"introScreenSubTitle3": "Doppelt antippen für die Beschreibung des Kunstwerks. Tippe auf die Pfeile, um zum nächsten Kunstwerk zu navigieren.",
"introScreenSubTitle4": "Tippe auf die Marker, um zum Kunstwerk zu gelangen. Der Button unten rechts zeigt deine aktuelle Position.",
"introScreenSubTitle5": "Hier werden alle aktuellen Ausstellungen des Kunstforums angezeigt.",
"introScreenSubTitle6": "Zum Anpassen der Farbe, Sprache oder Schriftgröße und Bearbeiten deiner Favoriten!",
"skip": "Fertig",
"done": "Fertig"
}
\ No newline at end of file
......@@ -130,5 +130,69 @@
"alertNoWifi": "This app requires internet connection. Please connect your device to internet and press the button below.",
"@alertNoWifi": {
"description": "The translation of Alert when no internet connection in loading screen"
},
"showInMap": "Show in map",
"@showInMap": {
"description": "The translation of show in map"
},
"artworkNotFound": "The searched artwork was not found!",
"@artworkNotFound": {
"description": "The translation of Alert when no corresponding Artworks to the QR was found."
},
"introScreenTitle1": "Welcome!",
"@introScreenTitle1": {
"description": "The title in 1. page of introduction screen"
},
"introScreenTitle2": "Home Screen",
"@introScreenTitle2": {
"description": "The title in 2. page of introduction screen"
},
"introScreenTitle3": "Audio Guide",
"@introScreenTitle3": {
"description": "The title in 3. page of introduction screen"
},
"introScreenTitle4": "Map Screen",
"@introScreenTitle4": {
"description": "The title in 4. page of introduction screen"
},
"introScreenTitle5": "Exhibition Screen",
"@introScreenTitle5": {
"description": "The title in 5. page of introduction screen"
},
"introScreenTitle6": "Setting Screen",
"@introScreenTitle6": {
"description": "The title in 6. page of introduction screen"
},
"introScreenSubTitle1": "Swipe left to see the features of this app!",
"@introScreenSubTitle1": {
"description": "The subtitle in 1. page of introduction screen"
},
"introScreenSubTitle2": "Campus-Kunst and other exhibitions are displayed here.",
"@introScreenSubTitle2": {
"description": "The subtitle in 2. page of introduction screen"
},
"introScreenSubTitle3": "Double-tap to read the artwork's description. Tap on the arrows to see the next artwork.",
"@introScreenSubTitle3": {
"description": "The subtitle in 3. page of introduction screen"
},
"introScreenSubTitle4": "Tap the markers to navigate to an artwork. The arrow on the bottom right shows your location.",
"@introScreenSubTitle4": {
"description": "The subtitle in 4. page of introduction screen"
},
"introScreenSubTitle5": "An overview of all current exhibitions of Kunstforum.",
"@introScreenSubTitle5": {
"description": "The subtitle in 5. page of introduction screen"
},
"introScreenSubTitle6": "Change the color, language or font size here and manage your favorite artworks!",
"@introScreenSubTitle6": {
"description": "The subtitle in 6. page of introduction screen"
},
"skip": "Skip",
"@skip": {
"description": "The translation of skip"
},
"done": "Done",
"@done": {
"description": "The translation of done"
}
}
\ No newline at end of file
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:kunstforum_tu_darmstadt/api/artwork_service.dart';
import 'package:provider/provider.dart';
import 'dart:ui' as ui;
import 'package:shared_preferences/shared_preferences.dart';
import 'config/route_generator.dart';
import 'lang/l10n.dart';
import 'lang/locale_provider.dart';
//***********************************************
//***** Custom Imports *******
//***********************************************
import 'package:kunstforum_tu_darmstadt/config/app_setting.dart';
import 'package:kunstforum_tu_darmstadt/ui/screens/loading_screen.dart';
import 'config/route_generator.dart';
import 'lang/l10n.dart';
import 'lang/locale_provider.dart';
import 'package:kunstforum_tu_darmstadt/api/artwork_service.dart';
import 'package:kunstforum_tu_darmstadt/ui/screens/introduction_screen.dart';
/// A flag indicating whether to show the [IntroScreen]
bool? showIntroScreen;
/// Runs the app by building the widget [KuTUD]
void main() {
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final prefs = await SharedPreferences.getInstance();
showIntroScreen = prefs.getBool("showIntroScreen") ?? true;
RenderErrorBox.backgroundColor = Colors.transparent;
RenderErrorBox.textStyle = ui.TextStyle(color: Colors.transparent);
runApp(MultiProvider(providers: [
ChangeNotifierProvider<LocaleProvider>(create: (_) => LocaleProvider()),
ChangeNotifierProvider<AppSetting>(create: (_) => AppSetting()),
......@@ -27,7 +40,6 @@ void main() {
/// A [StatefulWidget] building a [MaterialApp] which is this app's core.
///
class KuTUD extends StatelessWidget {
//***************************************************
//****** Main Widget *********
//***************************************************
......@@ -38,17 +50,13 @@ class KuTUD extends StatelessWidget {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.light));
final provider = Provider.of<LocaleProvider>(context);
final localProvider = Provider.of<LocaleProvider>(context);
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'KuTUD',
initialRoute: RouteGenerator.loadingScreenRouteName,
routes: {
RouteGenerator.loadingScreenRouteName: (context) =>
LoadingScreen(),
},
home: showIntroScreen! ? IntroScreen() : LoadingScreen(),
onGenerateRoute: RouteGenerator.generateRoute,
locale: provider.locale,
locale: localProvider.locale,
supportedLocales: L10n.all,
localizationsDelegates: [
AppLocalizations.delegate,
......
/// A class wrapping the information for [SettingScreen]
class AppInfo {
//***************************************************
//****** class Attributes **********
//***************************************************
/// about us section in the app
String aboutUs;
/// about artworks at the TU
String aboutTUArt;
/// Impressum
String imprint;
//***************************************************
//****** Constructor *********
//***************************************************
AppInfo(
{required this.aboutUs, required this.aboutTUArt, required this.imprint});
factory AppInfo.fromJson(Map<String, dynamic> json, String apiLink) {
return AppInfo(
aboutUs: json['aboutUs'] ?? "",
aboutTUArt: json['aboutTUArt'] ?? "",
imprint: json['imprint'] ?? "",
);
}
Map toJson() => {
'aboutUs': aboutUs,
'aboutTUArt': aboutTUArt,
'imprint': imprint,
};
}
......@@ -24,6 +24,9 @@ class Artwork {
/// A flag indicating whether this art is favourite or not
bool isFavourite;
/// the time of the Exhibition
String? timeOfExhibition;
/// the name of the work of art
final String name;
......@@ -37,7 +40,7 @@ class Artwork {
String? artistName;
/// artist's year of birth and death
String? yearOfBirthAndDead;
String? lebensDaten;
/// information about the material
String? material;
......@@ -60,6 +63,9 @@ class Artwork {
/// Y navigation coordinations
double? navY;
/// unique nr of Artwork
final int nr;
/// description
String? description;
......@@ -84,13 +90,15 @@ class Artwork {
Artwork(
{required this.id,
required this.nr,
this.timeOfExhibition,
required this.name,
this.hasValidAudio = false,
this.hasValidVideo = false,
this.year,
this.type,
this.artistName,
this.yearOfBirthAndDead,
this.lebensDaten,
this.material,
this.locationInfo,
this.address,
......@@ -126,9 +134,6 @@ class Artwork {
if (audioResponse.isNotEmpty) {
List<dynamic> audioLinksResponse = audioResponse['key'];
if (audioLinksResponse.isNotEmpty) {
//audioLinks = [
// "https://s3.amazonaws.com/scifri-episodes/scifri20181123-episode.mp3"
//];
audioLinks = audioLinksResponse.fold(<String>[],
(List<String>? previousValue, dynamic element) {
previousValue!.add(apiLink + element);
......@@ -156,13 +161,15 @@ class Artwork {
return Artwork(
id: json['_id'] ?? "",
nr: json['Nummerierung'] ?? -1,
timeOfExhibition: json['zeitpunktDerAusstellung'] ?? "",
name: json['kunstwerksname'] ?? "",
hasValidAudio: hasValidAudio,
hasValidVideo: hasValidVideo,
year: json['entstehungsjahr'] ?? -1,
type: json['kunstwerksart'] ?? "",
artistName: json['kuenstlerin'] ?? "",
yearOfBirthAndDead: json['GeburtsUndTodesjahr'] ?? "",
lebensDaten: json['lebensdaten'] ?? "",
material: json['materialinfo'] ?? "",
locationInfo: json['WieAnDieTU'] ?? "",
address: json['standortLageinfo'] ?? "",
......@@ -180,11 +187,13 @@ class Artwork {
Map toJson() => {
'id': id,
'Nummerierung': nr,
timeOfExhibition: timeOfExhibition,
'name': name,
'year': year,
'type': type,
'artistName': artistName,
'yearOfBirthAndDead': yearOfBirthAndDead,
'lebensDaten': lebensDaten,
'material': material,
'locationInfo': locationInfo,
'address': address,
......
......@@ -83,8 +83,6 @@ class Exhibition {
artworkList.sort((a, b) => a.year!.compareTo(b.year!));
}
// TODO: implement more sorting methods if needed
// returns a map of artworks grouped by exhibition (Ausstellung)
Map groupArtworksByExhibitions() {
var result = Map<String, List>();
......
......@@ -2,9 +2,6 @@ import 'package:audio_session/audio_session.dart';
import 'package:flutter/material.dart';
import 'package:just_audio/just_audio.dart';
import 'package:flutter/cupertino.dart';
import 'package:kunstforum_tu_darmstadt/api/artwork_service.dart';
import 'package:kunstforum_tu_darmstadt/models/artwork.dart';
import 'package:kunstforum_tu_darmstadt/models/exhibition.dart';
import 'package:provider/provider.dart';
import 'package:rxdart/rxdart.dart';
import 'package:auto_size_text/auto_size_text.dart';
......@@ -19,6 +16,9 @@ import 'package:kunstforum_tu_darmstadt/ui/widgets/custom_app_bar.dart';
import 'package:kunstforum_tu_darmstadt/ui/widgets/navigation_bar.dart';
import 'package:kunstforum_tu_darmstadt/config/route_generator.dart';
import 'package:kunstforum_tu_darmstadt/ui/widgets/artwork_flip_card.dart';
import 'package:kunstforum_tu_darmstadt/api/artwork_service.dart';
import 'package:kunstforum_tu_darmstadt/models/artwork.dart';
import 'package:kunstforum_tu_darmstadt/models/exhibition.dart';
///
/// A [StatefulWidget] that displays an audio player UI.
......@@ -58,7 +58,7 @@ class _AudioGuideScreenState extends State<AudioGuideScreen> {
/// The list of [AudioSource] which will be displayed.
late ConcatenatingAudioSource _playlist;
// TODO: Implement UX -> fetch starred from Artswork
/// If the [Artwork] is in the favorites list
bool isStarred = false;
//***************************************************
......@@ -115,7 +115,8 @@ class _AudioGuideScreenState extends State<AudioGuideScreen> {
builder: (context, snapshot) {
final state = snapshot.data;
if (state?.sequence.isEmpty ?? true) return SizedBox();
final Artwork artwork = exhibition.artworkList[getIndex()];//state!.currentSource!.tag;
final Artwork artwork =
exhibition.artworkList[getIndex()]; //state!.currentSource!.tag;
Size boxSize = Size(MediaQuery.of(context).size.width,