Skip to content
Snippets Groups Projects
Commit 180d203e authored by Benoît Harrault's avatar Benoît Harrault
Browse files

Normalize app architecture

parent 50edf648
No related branches found
No related tags found
1 merge request!18Resolve "Normalize game architecture"
Pipeline #5732 passed
Showing
with 543 additions and 51 deletions
......@@ -37,7 +37,7 @@ if (keystorePropertiesFile.exists()) {
}
android {
compileSdkVersion 33
compileSdkVersion 34
namespace "org.benoitharrault.plotter"
defaultConfig {
......
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
app.versionName=0.0.16
app.versionCode=16
app.versionName=0.0.17
app.versionCode=17
File added
File added
File added
File added
assets/icons/application.png

7.36 KiB

{
"app_name": "Plotter",
"settings_title": "Settings",
"settings_label_theme": "Theme mode",
"settings_label_api_host": "API host",
"about_title": "Informations",
"about_content": "Plotter",
"about_version": "Version: {version}",
"page_home": "Home",
"page_remote_control": "Control",
"": ""
}
{
"app_name": "Plotter",
"settings_title": "Réglages",
"settings_label_theme": "Thème de couleurs",
"settings_label_api_host": "URL de l'API",
"about_title": "Informations",
"about_content": "Plotter.",
"about_version": "Version : {version}",
"page_home": "Accueil",
"page_remote_control": "Télécommande",
"": ""
}
Improve/normalize app architecture.
Amélioration/normalisation de l'architecture de l'application.
#! /bin/bash
# Check dependencies
command -v inkscape >/dev/null 2>&1 || { echo >&2 "I require inkscape but it's not installed. Aborting."; exit 1; }
command -v scour >/dev/null 2>&1 || { echo >&2 "I require scour but it's not installed. Aborting."; exit 1; }
command -v optipng >/dev/null 2>&1 || { echo >&2 "I require optipng but it's not installed. Aborting."; exit 1; }
CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
BASE_DIR="$(dirname "${CURRENT_DIR}")"
SOURCE="${CURRENT_DIR}/icon.svg"
OPTIPNG_OPTIONS="-preserve -quiet -o7"
# optimize svg
cp ${SOURCE} ${SOURCE}.tmp
scour \
--remove-descriptive-elements \
--enable-id-stripping \
--enable-viewboxing \
--enable-comment-stripping \
--nindent=4 \
-i ${SOURCE}.tmp \
-o ${SOURCE}
rm ${SOURCE}.tmp
# build icons
function build_icon() {
ICON_SIZE="$1"
TARGET="$2"
TARGET_PNG="${TARGET}.png"
inkscape \
--export-width=${ICON_SIZE} \
--export-height=${ICON_SIZE} \
--export-filename=${TARGET_PNG} \
${SOURCE}
optipng ${OPTIPNG_OPTIONS} ${TARGET_PNG}
}
build_icon 72 ${BASE_DIR}/android/app/src/main/res/mipmap-hdpi/ic_launcher
build_icon 48 ${BASE_DIR}/android/app/src/main/res/mipmap-mdpi/ic_launcher
build_icon 96 ${BASE_DIR}/android/app/src/main/res/mipmap-xhdpi/ic_launcher
build_icon 144 ${BASE_DIR}/android/app/src/main/res/mipmap-xxhdpi/ic_launcher
build_icon 192 ${BASE_DIR}/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher
build_icon 512 ${BASE_DIR}/fastlane/metadata/android/en-US/images/icon
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:unicons/unicons.dart';
import 'package:plotter/ui/pages/remote_control.dart';
import 'package:plotter/ui/pages/home.dart';
class ActivityPageItem {
final Icon icon;
final Widget page;
final String code;
const ActivityPageItem({
required this.icon,
required this.page,
required this.code,
});
}
class ActivityPage {
static const indexHome = 0;
static const pageHome = ActivityPageItem(
icon: Icon(UniconsLine.home),
page: PageHome(),
code: 'page_home',
);
static const indexRemoteControl = 1;
static const pageRemoteControl = ActivityPageItem(
icon: Icon(UniconsLine.arrow),
page: PageRemoteControl(),
code: 'page_remote_control',
);
static Map<int, ActivityPageItem> items = {
indexHome: pageHome,
indexRemoteControl: pageRemoteControl,
};
static bool isIndexAllowed(int pageIndex) {
return items.keys.contains(pageIndex);
}
static ActivityPageItem getPageItem(int pageIndex) {
return items[pageIndex] ?? pageHome;
}
static Widget getPageWidget(int pageIndex) {
return items[pageIndex]?.page ?? pageHome.page;
}
static int itemsCount = ActivityPage.items.length;
}
import 'package:plotter/utils/tools.dart';
class DefaultGlobalSettings {
// available global parameters codes
static const String parameterCodeSkin = 'skin';
static const List<String> availableParameters = [
parameterCodeSkin,
];
// skin: available values
static const String skinValueDefault = 'default';
static const List<String> allowedSkinValues = [
skinValueDefault,
];
// skin: default value
static const String defaultSkinValue = skinValueDefault;
// available values from parameter code
static List<String> getAvailableValues(String parameterCode) {
switch (parameterCode) {
case parameterCodeSkin:
return DefaultGlobalSettings.allowedSkinValues;
}
printlog('Did not find any available value for global parameter "$parameterCode".');
return [];
}
// parameters displayed with assets (instead of painter)
static List<String> displayedWithAssets = [
//
];
}
import 'package:flutter/material.dart';
import 'package:unicons/unicons.dart';
import 'package:plotter/ui/screens/about.dart';
import 'package:plotter/ui/screens/activity.dart';
import 'package:plotter/ui/screens/settings.dart';
class ScreenItem {
final Icon icon;
final Widget screen;
final bool displayBottomNavBar;
const ScreenItem({
required this.icon,
required this.screen,
required this.displayBottomNavBar,
});
}
class Screen {
static const indexActivity = 0;
static const screenActivity = ScreenItem(
icon: Icon(UniconsLine.home),
screen: ScreenActivity(),
displayBottomNavBar: true,
);
static const indexSettings = 1;
static const screenSettings = ScreenItem(
icon: Icon(UniconsLine.setting),
screen: ScreenSettings(),
displayBottomNavBar: false,
);
static const indexAbout = 2;
static const screenAbout = ScreenItem(
icon: Icon(UniconsLine.info_circle),
screen: ScreenAbout(),
displayBottomNavBar: false,
);
static Map<int, ScreenItem> items = {
indexActivity: screenActivity,
indexSettings: screenSettings,
indexAbout: screenAbout,
};
static bool isIndexAllowed(int screenIndex) {
return items.keys.contains(screenIndex);
}
static Widget getWidget(int screenIndex) {
return items[screenIndex]?.screen ?? screenActivity.screen;
}
static bool displayBottomNavBar(int screenIndex) {
return items[screenIndex]?.displayBottomNavBar ?? screenActivity.displayBottomNavBar;
}
static int itemsCount = Screen.items.length;
}
import 'package:flutter/material.dart';
/// Colors from Tailwind CSS (v3.0) - June 2022
///
/// https://tailwindcss.com/docs/customizing-colors
const int _primaryColor = 0xFF6366F1;
const MaterialColor primarySwatch = MaterialColor(_primaryColor, <int, Color>{
50: Color(0xFFEEF2FF), // indigo-50
100: Color(0xFFE0E7FF), // indigo-100
200: Color(0xFFC7D2FE), // indigo-200
300: Color(0xFFA5B4FC), // indigo-300
400: Color(0xFF818CF8), // indigo-400
500: Color(_primaryColor), // indigo-500
600: Color(0xFF4F46E5), // indigo-600
700: Color(0xFF4338CA), // indigo-700
800: Color(0xFF3730A3), // indigo-800
900: Color(0xFF312E81), // indigo-900
});
const int _textColor = 0xFF64748B;
const MaterialColor textSwatch = MaterialColor(_textColor, <int, Color>{
50: Color(0xFFF8FAFC), // slate-50
100: Color(0xFFF1F5F9), // slate-100
200: Color(0xFFE2E8F0), // slate-200
300: Color(0xFFCBD5E1), // slate-300
400: Color(0xFF94A3B8), // slate-400
500: Color(_textColor), // slate-500
600: Color(0xFF475569), // slate-600
700: Color(0xFF334155), // slate-700
800: Color(0xFF1E293B), // slate-800
900: Color(0xFF0F172A), // slate-900
});
const Color errorColor = Color(0xFFDC2626); // red-600
final ColorScheme lightColorScheme = ColorScheme.light(
primary: primarySwatch.shade500,
secondary: primarySwatch.shade500,
onSecondary: Colors.white,
error: errorColor,
onSurface: textSwatch.shade500,
surface: textSwatch.shade50,
surfaceContainerHighest: Colors.white,
shadow: textSwatch.shade900.withOpacity(.1),
);
final ColorScheme darkColorScheme = ColorScheme.dark(
primary: primarySwatch.shade500,
secondary: primarySwatch.shade500,
onSecondary: Colors.white,
error: errorColor,
onSurface: textSwatch.shade300,
surface: const Color(0xFF262630),
surfaceContainerHighest: const Color(0xFF282832),
shadow: textSwatch.shade900.withOpacity(.2),
);
final ThemeData lightTheme = ThemeData(
colorScheme: lightColorScheme,
fontFamily: 'Nunito',
textTheme: TextTheme(
displayLarge: TextStyle(
color: textSwatch.shade700,
fontFamily: 'Nunito',
),
displayMedium: TextStyle(
color: textSwatch.shade600,
fontFamily: 'Nunito',
),
displaySmall: TextStyle(
color: textSwatch.shade500,
fontFamily: 'Nunito',
),
headlineLarge: TextStyle(
color: textSwatch.shade700,
fontFamily: 'Nunito',
),
headlineMedium: TextStyle(
color: textSwatch.shade600,
fontFamily: 'Nunito',
),
headlineSmall: TextStyle(
color: textSwatch.shade500,
fontFamily: 'Nunito',
),
titleLarge: TextStyle(
color: textSwatch.shade700,
fontFamily: 'Nunito',
),
titleMedium: TextStyle(
color: textSwatch.shade600,
fontFamily: 'Nunito',
),
titleSmall: TextStyle(
color: textSwatch.shade500,
fontFamily: 'Nunito',
),
bodyLarge: TextStyle(
color: textSwatch.shade700,
fontFamily: 'Nunito',
),
bodyMedium: TextStyle(
color: textSwatch.shade600,
fontFamily: 'Nunito',
),
bodySmall: TextStyle(
color: textSwatch.shade500,
fontFamily: 'Nunito',
),
labelLarge: TextStyle(
color: textSwatch.shade700,
fontFamily: 'Nunito',
),
labelMedium: TextStyle(
color: textSwatch.shade600,
fontFamily: 'Nunito',
),
labelSmall: TextStyle(
color: textSwatch.shade500,
fontFamily: 'Nunito',
),
),
);
final ThemeData darkTheme = lightTheme.copyWith(
colorScheme: darkColorScheme,
textTheme: TextTheme(
displayLarge: TextStyle(
color: textSwatch.shade200,
fontFamily: 'Nunito',
),
displayMedium: TextStyle(
color: textSwatch.shade300,
fontFamily: 'Nunito',
),
displaySmall: TextStyle(
color: textSwatch.shade400,
fontFamily: 'Nunito',
),
headlineLarge: TextStyle(
color: textSwatch.shade200,
fontFamily: 'Nunito',
),
headlineMedium: TextStyle(
color: textSwatch.shade300,
fontFamily: 'Nunito',
),
headlineSmall: TextStyle(
color: textSwatch.shade400,
fontFamily: 'Nunito',
),
titleLarge: TextStyle(
color: textSwatch.shade200,
fontFamily: 'Nunito',
),
titleMedium: TextStyle(
color: textSwatch.shade300,
fontFamily: 'Nunito',
),
titleSmall: TextStyle(
color: textSwatch.shade400,
fontFamily: 'Nunito',
),
bodyLarge: TextStyle(
color: textSwatch.shade200,
fontFamily: 'Nunito',
),
bodyMedium: TextStyle(
color: textSwatch.shade300,
fontFamily: 'Nunito',
),
bodySmall: TextStyle(
color: textSwatch.shade400,
fontFamily: 'Nunito',
),
labelLarge: TextStyle(
color: textSwatch.shade200,
fontFamily: 'Nunito',
),
labelMedium: TextStyle(
color: textSwatch.shade300,
fontFamily: 'Nunito',
),
labelSmall: TextStyle(
color: textSwatch.shade400,
fontFamily: 'Nunito',
),
),
);
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:plotter/models/activity/activity.dart';
import 'package:plotter/models/settings/settings_global.dart';
part 'activity_state.dart';
class ActivityCubit extends HydratedCubit<ActivityState> {
ActivityCubit()
: super(ActivityState(
currentActivity: Activity.createNull(),
));
void updateState(Activity activity) {
emit(ActivityState(
currentActivity: activity,
));
}
void refresh() {
final Activity activity = Activity(
// Settings
globalSettings: state.currentActivity.globalSettings,
// State
isRunning: state.currentActivity.isRunning,
// Base data
apiHost: state.currentActivity.apiHost,
// Activity data
apiStatus: state.currentActivity.apiStatus,
);
// activity.dump();
updateState(activity);
}
void startNewActivity({
required GlobalSettings globalSettings,
}) {
final Activity newActivity = Activity.createNew(
// Settings
globalSettings: globalSettings,
);
newActivity.dump();
updateState(newActivity);
refresh();
}
void quitActivity() {
state.currentActivity.isRunning = false;
refresh();
}
void resumeSavedActivity() {
state.currentActivity.isRunning = true;
refresh();
}
void deleteSavedActivity() {
state.currentActivity.isRunning = false;
refresh();
}
void setApiHost(String apiHost) {
state.currentActivity.apiHost = apiHost;
refresh();
}
void updateApiStatus(String apiStatus) {
state.currentActivity.apiStatus = apiStatus;
refresh();
}
@override
ActivityState? fromJson(Map<String, dynamic> json) {
final Activity currentActivity = json['currentActivity'] as Activity;
return ActivityState(
currentActivity: currentActivity,
);
}
@override
Map<String, dynamic>? toJson(ActivityState state) {
return <String, dynamic>{
'currentActivity': state.currentActivity.toJson(),
};
}
}
part of 'activity_cubit.dart';
@immutable
class ActivityState extends Equatable {
const ActivityState({
required this.currentActivity,
});
final Activity currentActivity;
@override
List<dynamic> get props => <dynamic>[
currentActivity,
];
}
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:plotter/config/activity_page.dart';
class NavCubitPage extends HydratedCubit<int> {
NavCubitPage() : super(0);
void updateIndex(int index) {
if (ActivityPage.isIndexAllowed(index)) {
emit(index);
} else {
emit(ActivityPage.indexHome);
}
}
@override
int fromJson(Map<String, dynamic> json) {
return ActivityPage.indexHome;
}
@override
Map<String, dynamic>? toJson(int state) {
return <String, int>{'pageIndex': state};
}
}
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:plotter/config/screen.dart';
class NavCubitScreen extends HydratedCubit<int> {
NavCubitScreen() : super(0);
void updateIndex(int index) {
if (Screen.isIndexAllowed(index)) {
emit(index);
} else {
goToActivityPage();
}
}
void goToActivityPage() {
emit(Screen.indexActivity);
}
void goToSettingsPage() {
emit(Screen.indexSettings);
}
void goToAboutPage() {
emit(Screen.indexAbout);
}
@override
int fromJson(Map<String, dynamic> json) {
return Screen.indexActivity;
}
@override
Map<String, dynamic>? toJson(int state) {
return <String, int>{'screenIndex': state};
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment