[apps/home] Apps placement defined by country

Instead of relying on the order in which the apps are passed at compile
time, the look of the Home app is defined in a config file, and depends
on the country chosen.

Change-Id: If7f56a284e0001d6d75ece1e7efdf5fd1d0a9978
This commit is contained in:
Gabriel Ozouf
2020-07-22 16:43:25 +02:00
committed by Émilie Feral
parent fad375c11c
commit b2f81db4a9
7 changed files with 136 additions and 15 deletions

View File

@@ -1,11 +1,11 @@
CountryCode,CountryPreferences::AvailableExamModes,CountryPreferences::MethodForQuartiles,Poincare::Preferences::UnitFormat
CA,StandardOnly,MedianOfSublist,Metric
DE,StandardOnly,MedianOfSublist,Metric
ES,StandardOnly,MedianOfSublist,Metric
FR,StandardOnly,CumulatedFrequency,Metric
GB,StandardOnly,MedianOfSublist,Metric
IT,StandardOnly,CumulatedFrequency,Metric
NL,All,MedianOfSublist,Metric
PT,StandardOnly,MedianOfSublist,Metric
US,StandardOnly,MedianOfSublist,Imperial
WW,StandardOnly,MedianOfSublist,Metric
CountryCode,CountryPreferences::AvailableExamModes,CountryPreferences::MethodForQuartiles,Poincare::Preferences::UnitFormat,CountryPreferences::HomeAppsLayout
CA,StandardOnly,MedianOfSublist,Metric,Default
DE,StandardOnly,MedianOfSublist,Metric,Default
ES,StandardOnly,MedianOfSublist,Metric,Default
FR,StandardOnly,CumulatedFrequency,Metric,Default
GB,StandardOnly,MedianOfSublist,Metric,Default
IT,StandardOnly,CumulatedFrequency,Metric,Default
NL,All,MedianOfSublist,Metric,Default
PT,StandardOnly,MedianOfSublist,Metric,Default
US,StandardOnly,MedianOfSublist,Imperial,HidePython
WW,StandardOnly,MedianOfSublist,Metric,Default
1 CountryCode CountryPreferences::AvailableExamModes CountryPreferences::MethodForQuartiles Poincare::Preferences::UnitFormat CountryPreferences::HomeAppsLayout
2 CA StandardOnly MedianOfSublist Metric Default
3 DE StandardOnly MedianOfSublist Metric Default
4 ES StandardOnly MedianOfSublist Metric Default
5 FR StandardOnly CumulatedFrequency Metric Default
6 GB StandardOnly MedianOfSublist Metric Default
7 IT StandardOnly CumulatedFrequency Metric Default
8 NL All MedianOfSublist Metric Default
9 PT StandardOnly MedianOfSublist Metric Default
10 US StandardOnly MedianOfSublist Imperial HidePython
11 WW StandardOnly MedianOfSublist Metric Default

View File

@@ -15,20 +15,28 @@ public:
CumulatedFrequency
};
constexpr CountryPreferences(AvailableExamModes availableExamModes, MethodForQuartiles methodForQuartiles, Poincare::Preferences::UnitFormat unitFormat) :
enum class HomeAppsLayout : uint8_t {
Default,
HidePython,
};
constexpr CountryPreferences(AvailableExamModes availableExamModes, MethodForQuartiles methodForQuartiles, Poincare::Preferences::UnitFormat unitFormat, HomeAppsLayout homeAppsLayout) :
m_availableExamModes(availableExamModes),
m_methodForQuartiles(methodForQuartiles),
m_unitFormat(unitFormat)
m_unitFormat(unitFormat),
m_homeAppsLayout(homeAppsLayout)
{}
constexpr AvailableExamModes availableExamModes() const { return m_availableExamModes; }
constexpr MethodForQuartiles methodForQuartiles() const { return m_methodForQuartiles; }
constexpr Poincare::Preferences::UnitFormat unitFormat() const { return m_unitFormat; }
constexpr HomeAppsLayout homeAppsLayout() const { return m_homeAppsLayout; }
private:
const AvailableExamModes m_availableExamModes;
const MethodForQuartiles m_methodForQuartiles;
const Poincare::Preferences::UnitFormat m_unitFormat;
const HomeAppsLayout m_homeAppsLayout;
};
#endif

View File

@@ -19,6 +19,7 @@ public:
CountryPreferences::AvailableExamModes availableExamModes() const { return I18n::CountryPreferencesArray[static_cast<uint8_t>(m_country)].availableExamModes(); }
CountryPreferences::MethodForQuartiles methodForQuartiles() const { return I18n::CountryPreferencesArray[static_cast<uint8_t>(m_country)].methodForQuartiles(); }
Poincare::Preferences::UnitFormat unitFormat() const { return I18n::CountryPreferencesArray[static_cast<uint8_t>(m_country)].unitFormat(); }
CountryPreferences::HomeAppsLayout homeAppsLayout() const { return I18n::CountryPreferencesArray[static_cast<uint8_t>(m_country)].homeAppsLayout(); }
bool isInExamMode() const { return (int8_t)examMode() > 0; }
ExamMode examMode() const;
void setExamMode(ExamMode examMode);

View File

@@ -1,9 +1,28 @@
app_home_src = $(addprefix apps/home/,\
app.cpp \
app_cell.cpp \
apps_layout.py \
controller.cpp \
)
apps_src += $(app_home_src)
i18n_files += $(call i18n_without_universal_for,home/base)
# Apps layout file generation
# The header is refered to as <apps/home/apps_layout.h> so make sure it's
# findable this way
SFLAGS += -I$(BUILD_DIR)
apps_layout = apps/home/apps_layout.csv
$(eval $(call rule_for, \
APPS_LAYOUT, \
apps/home/apps_layout.cpp, \
, \
$$(PYTHON) apps/home/apps_layout.py --layouts $(apps_layout) --header $$(subst .cpp,.h,$$@) --implementation $$@ --apps $$(EPSILON_APPS), \
global \
))
$(BUILD_DIR)/apps/home/apps_layout.h: $(BUILD_DIR)/apps/home/apps_layout.cpp

View File

@@ -0,0 +1,2 @@
Default,calculation,graph,code,statistics,probability,solver,sequence,regression,settings
HidePython,calculation,graph,solver,statistics,probability,regression,sequence,code,settings
1 Default calculation graph code statistics probability solver sequence regression settings
2 HidePython calculation graph solver statistics probability regression sequence code settings

90
apps/home/apps_layout.py Normal file
View File

@@ -0,0 +1,90 @@
# -*- coding: utf-8 -*-
# This script builds the .h/.cpp files for managing the placement of
# applications on the home menu, from the apps_layout.csv file.
import argparse
import csv
import io
parser = argparse.ArgumentParser(description="Build tools for the placement of applications on the home menu.")
parser.add_argument('--header', help='the .h file to generate')
parser.add_argument('--implementation', help='the .cpp file to generate')
parser.add_argument('--apps', nargs='+', help='apps that are actually compiled')
parser.add_argument('--layouts', help='the apps_layout.csv file')
args = parser.parse_args()
def build_permutation(apps, appsOrdered):
res = [0] * len(apps)
i = 0
for app in appsOrdered:
if app in apps:
res[i] = apps.index(app) + 1
i += 1
return res
def parse_config_file(layouts, apps):
res = {'styles':[], 'permutations':[]}
with io.open(layouts, "r", encoding="utf-8") as f:
csvreader = csv.reader(f, delimiter=',')
for row in csvreader:
res['styles'].append(row[0])
res['permutations'].append(build_permutation(apps, row[1:]))
return res
data = parse_config_file(args.layouts, args.apps)
def print_header(header, data):
f = open(header, "w")
f.write("#ifndef HOME_APPS_LAYOUT_H\n")
f.write("#define HOME_APPS_LAYOUT_H\n")
f.write("// This file is auto-generated by apps_layout.py\n\n")
f.write("namespace Home {\n\n")
f.write("int PermutedAppSnapshotIndex(int index);\n\n")
f.write("}\n\n")
f.write("#endif")
f.close()
def print_implementation(implementation, data):
f = open(implementation, "w")
f.write("// This file is auto-generated by apps_layout.py\n\n")
f.write('#include "apps_layout.h"\n')
f.write("#include <apps/country_preferences.h>\n")
f.write("#include <apps/global_preferences.h>\n")
f.write("#include <assert.h>\n\n")
f.write("namespace Home {\n\n")
styles = data['styles']
permutations = data['permutations']
f.write("/* Permutations are built so that Permutation[n] is the index of the snapshot\n")
f.write(" * for the nth app in the Home menu. */\n\n")
for i in range(len(styles)):
f.write("static constexpr int " + styles[i] + "AppsPermutation[] = {\n")
f.write(" 0,\n")
for j in permutations[i]:
f.write(" " + str(j) + ",\n")
f.write("};\n")
f.write('static_assert(' + styles[i] + 'AppsPermutation[0] == 0, "The Home apps must always be at index 0");\n\n')
f.write("int PermutedAppSnapshotIndex(int index) {\n")
f.write(" CountryPreferences::HomeAppsLayout currentLayout = GlobalPreferences::sharedGlobalPreferences()->homeAppsLayout();\n")
for i in range(len(styles)):
f.write(" if (currentLayout == CountryPreferences::HomeAppsLayout::" + styles[i] + ") {\n")
f.write(" return " + styles[i] + "AppsPermutation[index];")
f.write(" }\n")
f.write(" assert(false);\n")
f.write(" return -1;\n")
f.write("}\n\n")
f.write("}\n\n")
f.close()
if args.header:
print_header(args.header, data)
if args.implementation:
print_implementation(args.implementation, data)

View File

@@ -1,5 +1,6 @@
#include "controller.h"
#include "app.h"
#include <apps/home/apps_layout.h>
#include "../apps_container.h"
#include "../global_preferences.h"
#include "../exam_mode_configuration.h"
@@ -60,7 +61,7 @@ Controller::Controller(Responder * parentResponder, SelectableTableViewDataSourc
bool Controller::handleEvent(Ion::Events::Event event) {
if (event == Ion::Events::OK || event == Ion::Events::EXE) {
AppsContainer * container = AppsContainer::sharedAppsContainer();
::App::Snapshot * selectedSnapshot = container->appSnapshotAtIndex(selectionDataSource()->selectedRow() * k_numberOfColumns + selectionDataSource()->selectedColumn() + 1);
::App::Snapshot * selectedSnapshot = container->appSnapshotAtIndex(PermutedAppSnapshotIndex(selectionDataSource()->selectedRow() * k_numberOfColumns + selectionDataSource()->selectedColumn() + 1));
if (ExamModeConfiguration::appIsForbiddenInExamMode(selectedSnapshot->descriptor()->name(), GlobalPreferences::sharedGlobalPreferences()->examMode())) {
App::app()->displayWarning(I18n::Message::ForbidenAppInExamMode1, I18n::Message::ForbidenAppInExamMode2);
} else {
@@ -128,7 +129,7 @@ void Controller::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) {
appCell->setVisible(false);
} else {
appCell->setVisible(true);
::App::Descriptor * descriptor = container->appSnapshotAtIndex(appIndex)->descriptor();
::App::Descriptor * descriptor = container->appSnapshotAtIndex(PermutedAppSnapshotIndex(appIndex))->descriptor();
appCell->setAppDescriptor(descriptor);
}
}