1073 lines
42 KiB
TeX
1073 lines
42 KiB
TeX
\begin{it}
|
|
\usepackage[italian]{babel}
|
|
\end{it}
|
|
\begin{en}
|
|
\usepackage[english]{babel}
|
|
\end{en}
|
|
|
|
\usepackage{blindtext}
|
|
\usepackage[T1]{fontenc}
|
|
\usepackage[utf8]{inputenc}
|
|
\usepackage[pdftex]{graphicx}
|
|
\usepackage{scrlayer-scrpage}
|
|
\usepackage{float}
|
|
\usepackage{hyperref}
|
|
\usepackage{tikz}
|
|
\usepackage[backend=biber]{biblatex}
|
|
\usepackage{pgfgantt}
|
|
\usepackage{pgffor}
|
|
\usepackage{listings}
|
|
\usepackage{lmodern}
|
|
|
|
\bibliography{bibliography}
|
|
|
|
% page margins
|
|
\usepackage[margin=3cm,head=15pt]{geometry}
|
|
|
|
% document metadata
|
|
\begin{it}
|
|
\def\mdtitle{Alternanza scuola-lavoro estesa presso \textit{Agomir S.p.A.}}
|
|
\def\mdsubtitle{Sviluppo di applicazioni multipiattaforma per dispositivi mobili}
|
|
\end{it}
|
|
\begin{en}
|
|
\def\mdtitle{Extended school internship at \textit{Agomir S.p.A.}}
|
|
\def\mdsubtitle{Multiplatform mobile application development}
|
|
\end{en}
|
|
\def\mdauthor{Claudio Maggioni}
|
|
\def\mddate{\today}
|
|
\def\mdrevision{6}
|
|
|
|
% header and footer style
|
|
\clearscrheadfoot
|
|
\pagestyle{scrheadings}
|
|
\makeatletter
|
|
\ohead[]{\mdauthor}
|
|
\ihead[]{\mdtitle}
|
|
\cfoot[\pagemark]{\pagemark}
|
|
|
|
% listings configuration
|
|
\begin{it}
|
|
\renewcommand{\lstlistingname}{Listato}
|
|
\renewcommand{\lstlistlistingname}{Elenco dei listati}
|
|
\end{it}
|
|
\lstset{
|
|
basicstyle=\small\ttfamily,
|
|
frame=shadowbox,
|
|
rulesepcolor=\color{black},
|
|
columns=fullflexible,
|
|
commentstyle=\color{gray},
|
|
keywordstyle=\bfseries,
|
|
escapeinside={\%*}{*)},
|
|
aboveskip=2em,
|
|
captionpos=b,
|
|
abovecaptionskip=1em,
|
|
belowcaptionskip=1em
|
|
}
|
|
|
|
\begin{document}
|
|
|
|
% first page
|
|
\begin{titlepage}
|
|
\pagenumbering{gobble}
|
|
\centering
|
|
\includegraphics[width=0.15\textwidth]{images/logo.jpg}\par\vspace{1cm}
|
|
{\scshape\LARGE Istituto di Istruzione Superiore \mbox{``A. Badoni''}\par}
|
|
\vspace{1cm}
|
|
{\scshape\Large Tesi di maturità\par}
|
|
\vspace{1.5cm}
|
|
{\huge\bfseries\mdtitle\par}
|
|
\vspace{0.75cm}
|
|
{\Large\bfseries\mdsubtitle\par}
|
|
\vspace{2cm}
|
|
{\Large\itshape\mdauthor\par}
|
|
\vfill
|
|
% Bottom of the page
|
|
{\large\mddate\par}
|
|
\begin{it}
|
|
\vspace{0.5cm}
|
|
{\large\textsc{Traduzione in italiano}\par}
|
|
\end{it}
|
|
\vspace{0.5cm}
|
|
{\large\color{red}\textsc{Revisione \mdrevision}\par}
|
|
\end{titlepage}
|
|
|
|
\newpage
|
|
|
|
\pagenumbering{Roman}
|
|
\tableofcontents
|
|
|
|
\newpage
|
|
|
|
\pagenumbering{arabic}
|
|
\begin{it}
|
|
\section{Informazioni generali sul lavoro svolto}
|
|
|
|
A partire dal giorno 19/10/2017, collaboro con l'azienda \textit{Agomir
|
|
S.p.A.}\cite{agomir:website} (produttrice di software, sistemi e servizi per
|
|
piccole e medie imprese) svolgendo un'attività di alternanza scuola-lavoro
|
|
estesa con cadenza settimanale (giovedì e venerdì pomeriggio). Mi occupo di
|
|
sviluppo software di tipo gestionale, nello specifico di applicazioni per
|
|
smartphone multipiattaforma (cioè compatibili sia con Android che con iOS)
|
|
utilizzando strumenti come \textit{Ionic Framework}\cite{ionic:website} e
|
|
\textit{Apache Cordova}\cite{cordova:website}.
|
|
|
|
Tale approccio al mondo \textit{mobile} facilita lo sviluppo, perché al
|
|
posto di usare API e meccanismi legati alla piattaforma \`e possibile
|
|
utilizzare tecnologie note e standard come HTML e Javascript. Naturalmente
|
|
tali applicazioni richiedono pi\`u risorse e tendono ad essere meno fluide,
|
|
ma questo aspetto è meno rilevante in contesti gestionali come quelli
|
|
affrontati da Agomir, non legati ad esempio al mondo dei videogiochi o
|
|
all'elaborazione real-time.
|
|
\end{it}
|
|
\begin{en}
|
|
\section{General information about the work done}
|
|
|
|
Since 19 october 2017, I work with \textit{Agomir S.p.A.}\cite{agomir:website}
|
|
(vendor and producer of software, networking solutions and services for small
|
|
and mid-size
|
|
industries) doing an extended school internship on a weekly basis (thursdays
|
|
and fridays, in the afternoon). I am involved in developing accounting
|
|
software, writing multiplatform smartphone applications (that work
|
|
with both Android and iOS) to be precise. In order to do this, I am using tools
|
|
such as \textit{Ionic Framework}\cite{ionic:website} and \textit{Apache
|
|
Cordova}\cite{cordova:website}.
|
|
|
|
This type of approach to the mobile world makes development more easy because
|
|
instead of using APIs and platform-dependent code it is possible to use
|
|
well-known and standardized technologies such as HTML and Javascript.
|
|
Naturally, these apps require more system resources and they generally are
|
|
less responsive than their native counterparts. However, this aspect is less
|
|
relevant in the world of accounting software that Agomir embraces, far from the
|
|
optimisation hungry sectors of videogames or real time applications.
|
|
\end{en}
|
|
|
|
\begin{it}
|
|
\section{Gestione dei progetti}
|
|
|
|
Non ho diretto controllo manageriale su ciò che sviluppo in azienda,
|
|
in quanto ho un ruolo simile a quello di un dipendente. L'incarico di gestire
|
|
l'andamento dei progetti spetta a Mario Goretti, A.D. dell'azienda e capo
|
|
del settore di sviluppo software gestionale (\textsc{SWG}), e ai suoi
|
|
collaboratori.
|
|
|
|
In generale sviluppo i progetti da solo. Collaboro con il collega Daniele
|
|
Crippa per l'interfacciamento con i software aziendali esistenti e per
|
|
l'organizzazione di nuovi progetti, nonché per consigli vari.
|
|
|
|
Nonostante non abbia controllo totale \`e comunque mia responsabilità fare
|
|
stime orarie sul lavoro da svolgere nonché definire passi e \textit{milestone}
|
|
per i vari progetti.
|
|
|
|
Per aumentare la forza lavoro per lo sviluppo di applicazioni \textit{mobile}
|
|
ho coordinato momenti di formazione ad alcuni dipendenti nei quali ho mostrato
|
|
il principale funzionamento delle tecnologie che uso.
|
|
\end{it}
|
|
\begin{en}
|
|
\section{Projects management}
|
|
|
|
I do not directly manage the projects on which I work, because my role is more
|
|
similar to that of an employee. The task of managing the status of the various
|
|
projects is given to Mario Goretti, CEO of the company and head of the
|
|
accounting software department (codenamed SWG), and to his collaborators.
|
|
|
|
In general, I work on the tasks assigned to me alone. I sometimes work with my
|
|
colleague Daniele Crippa in order to interface my projects' code with the
|
|
existing products from the company, to plan new projects, and for suggestions
|
|
in general.
|
|
|
|
In spite of the lack of control I have in regard to the projects I work on, I
|
|
am still responsible for defining development milestones and making
|
|
time estimates for the completion of these.
|
|
|
|
In order to increase the work force assigned to the development of mobile
|
|
apps, I have managed training sessions for some employees in order to show
|
|
them how the tools that I use work.
|
|
\end{en}
|
|
|
|
\begin{it}
|
|
\section{Il progetto principale: \textit{InteGRa Mobile}}
|
|
|
|
Questa applicazione, una volta completata, dovrebbe permettere ad utenti in
|
|
mobilità di interfacciarsi con alcune funzioni del prodotto ERP di punta di
|
|
Agomir: il gestionale \textit{InteGRa}\footnote{Sito internet di InteGRa ERP:
|
|
\url{https://integra.agomir.com/}}. Nel dettaglio, sarà possibile accedere
|
|
alle seguenti sezioni:
|
|
|
|
\begin{description}
|
|
\item[Ordini cliente] per registrare ordini di prodotti a clienti;
|
|
\item[Soggetti] per consultare informazioni anagrafiche di clienti e fornitori;
|
|
\item[Agenda] per consultare e aggiungere eventi nel calendario presente
|
|
nell'ERP, il quale si pu\`o integrare con \textit{Outlook};
|
|
\item[Magazzini] gestire e inventariare scorte in magazzino.
|
|
\end{description}
|
|
|
|
In aggiunta, sarà possibile anche registrare le ore per interventi in
|
|
trasferta, funzione gi\`a implementata nell'applicazione
|
|
\textit{InteGRa.Service}, sviluppata nei periodi di alternanza precedenti.
|
|
Tale lavoro non \`e direttamente implementabile in \textit{InteGRa Mobile} a
|
|
causa di differenze consistenti nelle architetture dei due prodotti.
|
|
|
|
Inoltre, l'applicazione sarà in grado di funzionare in modo limitato anche
|
|
senza connessione diretta ad \textit{InteGRa}, permettendo la sincronizzazione
|
|
dei dati modificati con il gestionale in un momento futuro.
|
|
\end{it}
|
|
\begin{en}
|
|
\section{The main project: \textit{InteGRa Mobile}}
|
|
|
|
This app, once completed, will enable moving users to interact with some
|
|
sections of the main ERP product from Agomir: the accounting software
|
|
\textit{InteGRa}\footnote{Website for InteGRa ERP (in italian):
|
|
\url{https://integra.agomir.com/}}. In detail, users will be able to work
|
|
within the following areas:
|
|
|
|
\begin{description}
|
|
\item[Customers' orders] for registering the products customers may order;
|
|
\item[Customers and suppliers] for looking up record data of customers and
|
|
suppliers;
|
|
\item[Calendar] for viewing and editing events on the ERP calendar, syncable
|
|
with \textit{Outlook};
|
|
\item[Warehouses] for managing warehouses and inventories.
|
|
\end{description}
|
|
|
|
In addition to what specified above, users will be able to register working
|
|
hours for business trips. This feature is already implemented in
|
|
\textit{InteGRa.Service}, an app developed in a previous school internship
|
|
period. The two apps can not be merged immediately due to relevant
|
|
differencies in their respective architectures.
|
|
|
|
Finally, the app will be able to work (with limited functionalities) without
|
|
a network link with \textit{InteGRa}, offering data syncronization when a
|
|
connection will be avaliable.
|
|
\end{en}
|
|
|
|
\begin{it}
|
|
\subsection{Architettura software}
|
|
\end{it}
|
|
\begin{en}
|
|
\subsection{Software architecture}
|
|
\end{en}
|
|
|
|
\begin{figure}[H]
|
|
\centering
|
|
\resizebox{\ifdim\width>\linewidth\linewidth\else\width\fi}{!}{\input{diagrams/itgmobile}}
|
|
\begin{it}
|
|
\caption{L'architettura di \textit{InteGRa Mobile} raffigurata con un diagramma}
|
|
\end{it}
|
|
\begin{en}
|
|
\caption{The architecture of \textit{InteGRa Mobile} as a diagram}
|
|
\end{en}
|
|
\label{fig:itgmobliearch}
|
|
\end{figure}
|
|
|
|
\begin{it}
|
|
Nella figura \ref{fig:itgmobliearch} si può notare come l'insieme dei
|
|
componenti software all'interno del progetto siano organizzati e comunichino
|
|
tra loro. Tale architettura è stata realizzata da me, basandosi
|
|
sull'esperienza acquisita con il progetto \textit{InteGRa.Service}. Segue
|
|
una spiegazione sintetica (organizzata in punti) del diagramma.
|
|
|
|
\begin{itemize}
|
|
|
|
\item L'applicazione non comunica direttamente con il gestionale, ma tramite
|
|
una serie di chiamate \textsc{REST}\cite{wiki:rest} ad un endpoint lato
|
|
server (chiamato \textit{InteGRaREST}) che svolge il ruolo di intermediario;
|
|
|
|
\item Tale componente è una Java WebApplication che utilizza una libreria
|
|
sviluppata internamente (chiamata \textit{restaurant}) per offrire le rotte
|
|
accessibili al client e per interagire con il database di
|
|
\textit{InteGRa}, una normale istanza di \textit{PostgreSQL};
|
|
|
|
\item Nella fase di comunicazione tramite HTTP, i dati in input vengono
|
|
trasmessi come \texttt{application/x-www-form-urlencoded} mentre
|
|
i dati in output vengono trasmessi come \textsc{JSON}\cite{wiki:json}, e le
|
|
sessioni vengono identificate con un token presente come parametro nella
|
|
query string di ciascuna richiesta;
|
|
|
|
\item \textit{InteGRaREST} effettua autonomamente query \textsc{SQL} al
|
|
database per recuperare velocemente informazioni aventi
|
|
struttura dati e logiche di memorizzazione semplici;
|
|
|
|
\item Nel caso sia necessario interagire con record complessi,
|
|
\textit{InteGRaREST} comunica con \textit{InteGRa} attraverso
|
|
chiamate \textsc{SOAP}\cite{wiki:soap};
|
|
|
|
\item \textit{InteGRa Mobile}, l'applicazione per dispositivi mobili, comunica
|
|
con \textit{InteGRaREST} grazie a una versione client di \textit{restaurant},
|
|
in grado di gestire (in modo limitato) operazioni eseguite in mancanza di
|
|
connessione alla rete.
|
|
|
|
\end{itemize}
|
|
\end{it}
|
|
\begin{en}
|
|
In the picture \ref{fig:itgmobliearch} is shown how the software components
|
|
in the app interact with each other. This architecture was entirely realized
|
|
by me, on the basis offered by the project \textit{InteGRa.Service}. Further
|
|
information is given in the following list.
|
|
|
|
\begin{itemize}
|
|
|
|
\item The smartphone app does not directly communicate with the accounting
|
|
software, but instead it interacts using \textsc{REST} calls\cite{wiki:rest}
|
|
with a middleware server application, named \textit{InteGRaREST};
|
|
|
|
\item The last component mentioned is a Java WebApplication,
|
|
implemented by using an internally developed library named \textit{restaurant},
|
|
that contains the boilerplate needed to generate the \textsc{REST} routes
|
|
and to access the \textit{InteGRa} database, a normal \textit{PostgreSQL}
|
|
instance;
|
|
|
|
\item When information is exchanged by HTTP, input data is encoded
|
|
as the MIME type \texttt{application/x-www-form-urlencoded}, while output data
|
|
is encoded as \textsc{JSON}\cite{wiki:json}. Sessions are identified by
|
|
a token that must be included in the query string of each call;
|
|
|
|
\item \textit{InteGRaREST} queries the database autonomously through
|
|
\textsc{SQL} in order to fetch and store data structures with simple
|
|
or absent logical constraints;
|
|
|
|
\item When interaction with data arranged in complex structures is needed,
|
|
\textit{InteGRaREST} communicates with \textit{InteGRa} through
|
|
\textsc{SOAP} calls\cite{wiki:soap};
|
|
|
|
\item \textit{InteGRa Mobile}, the smartphone app, interacts with
|
|
\textit{InteGRaREST} thanks to a client version of \textit{restaurant},
|
|
which is able to handle offline operations.
|
|
|
|
\end{itemize}
|
|
\end{en}
|
|
|
|
\begin{it}
|
|
\subsubsection{\textit{restaurant} -- lato server}
|
|
La libreria \textit{restaurant}\footnote{Il nome \textit{restaurant} è un
|
|
gioco di parole sull'acronimo REST.} viene importata
|
|
come una normale dipendenza Maven\cite{maven:website}. Essa dipende da:
|
|
\begin{description}
|
|
\item[Hibernate ORM\cite{hibernate:website}] un famoso Object/Relation
|
|
Mapper\cite{wiki:orm} per Java, utile ad interrogare il database
|
|
\textit{PostgreSQL} con facilità;
|
|
\item[GSON\cite{gson:website}] una libreria di Google per serializzare
|
|
e deserializzare oggetti Java in JSON;
|
|
\item[Jersey\cite{jersey:website}] un'implementazione dell'API JAX-RS
|
|
per realizzare gli endpoint REST. Ciò avviene senza servlet ma tramite
|
|
semplici metodi contenuti in classi senza ``padre'' dette \textit{controller},
|
|
opportunamente decorati con le annotazioni nel package
|
|
\texttt{javax.ws.rs}.
|
|
\end{description}
|
|
|
|
Le operazioni richieste per interrogare la base di dati sono ulteriormente
|
|
semplificate dal codice contenuto in \textit{restaurant} su due livelli:
|
|
\begin{itemize}
|
|
\item È possibile eseguire normali operazioni di CRUD\cite{wiki:crud}
|
|
nonchè costruire manualmente query tramite i metodi statici della classe
|
|
\textit{CRUDUtils}, che lavorano in input e in output con istanze delle
|
|
classi ``entità'';
|
|
\item Inoltre, è possibile creare nuovi \textit{controller} con facilità
|
|
ereditando dalle classi astratte \textit{JsonProducer} (per
|
|
implementare funzioni al di fuori di CRUD),
|
|
\textit{ReadableRESTController} (per implementare operazioni in sola
|
|
lettura, equivalenti alla ``Read'' in CRUD) e \textit{CRUDRESTController}
|
|
(per implementare CRUD completi), che offono
|
|
metodi in grado di generare autonomamente risposte in JSON, sia in caso
|
|
di successo che in errore.
|
|
\end{itemize}
|
|
|
|
Seguono le interfacce delle classi \textit{CRUDUtils}, \textit{JsonProducer},
|
|
\textit{ReadableRESTController} e \textit{CRUDRESTController}, complete di
|
|
Javadoc (in inglese, come presente nei sorgenti originali).
|
|
\end{it}
|
|
|
|
\begin{lstlisting}[caption=Interfaccia della classe \textit{JsonProducer}, label={lst:jsonprd-java}, language=Java]
|
|
/**
|
|
* Something that generates JSON responses
|
|
*/
|
|
public abstract class JsonProducer {
|
|
|
|
protected static final Logger log = %*\ldots*);
|
|
|
|
protected static final Gson GSON = %*\ldots*);
|
|
|
|
/**
|
|
* Generates a json response with status 200 containing an object
|
|
*
|
|
* @param o the contents of the response
|
|
* @return JSON response
|
|
*/
|
|
protected Response jsonResponse(Object o) { %*\ldots*) }
|
|
|
|
/**
|
|
* Generates a json response with custom status containing an object
|
|
*
|
|
* @param o the contents of the response
|
|
* @param status status of the response
|
|
* @return JSON response
|
|
*/
|
|
protected Response jsonResponse(Object o, int status) { %*\ldots*) }
|
|
|
|
/**
|
|
* Generates a json response containing a library standard error
|
|
*
|
|
* @param error the error
|
|
* @return JSON error response
|
|
*/
|
|
protected Response jsonErrResponse(Error error) { %*\ldots*) }
|
|
|
|
/**
|
|
* Generates a json response containing a custom error
|
|
*
|
|
* @param error the error
|
|
* @return JSON error response
|
|
*/
|
|
protected Response jsonErrResponse(ErrorDetails error) { %*\ldots*) }
|
|
|
|
/**
|
|
* Generates an error response if the parameter given as argument
|
|
* is null or an empty string
|
|
*
|
|
* @param s parameter
|
|
* @param propName parameter name, used in the error description
|
|
* @return JSON error response or null if the parameter is valid
|
|
*/
|
|
protected Response require(Object s, String propName) { %*\ldots*) }
|
|
}
|
|
\end{lstlisting}
|
|
\begin{it}
|
|
Per una migliore lettura del listato \ref{lst:jsonprd-java}, si precisa che la
|
|
classe \textit{Error} è una enumerazione Java contenente messaggi di errore
|
|
standard. La classe \textit{ErrorDetails}, invece, permette al codice esterno
|
|
di realizzare messaggi di errore personalizzati. Inoltre, l'oggetto statico
|
|
\texttt{GSON} di classe \textit{Gson} è il serializzatore a JSON offerto dalla
|
|
libreria omonima.
|
|
\end{it}
|
|
|
|
\begin{lstlisting}[caption=Interfaccia della classe \textit{ReadableRESTController}, label={lst:rdbrest-java}, language=Java]
|
|
/**
|
|
* A REST Controller only readable
|
|
* @param <T> The readable entity class
|
|
*/
|
|
public abstract class ReadableRESTController<T extends Readable,
|
|
U extends PrimaryKey<T>> extends JsonProducer {
|
|
|
|
/**
|
|
* Interface used to carry a function capable of filtering a list
|
|
* of entities after it has been retrieved from the database
|
|
*
|
|
* @param <T> The readable entity class
|
|
*/
|
|
public interface ListFilter<T extends Readable> {
|
|
List<T> filter(List<T> toFilter);
|
|
}
|
|
|
|
/**
|
|
* Returns the class of the readable entity. Used as parameter for
|
|
* CRUDUtils
|
|
*
|
|
* @return the class of the readable entity
|
|
*/
|
|
protected abstract Class<T> getBeanClass();
|
|
|
|
protected final Logger log = %*\ldots*);
|
|
|
|
/**
|
|
* Returns a response with a standard unfiltered, unpaginated list
|
|
* of entities
|
|
*
|
|
* @return JSON response, success or not
|
|
*/
|
|
protected Response list() { %*\ldots*) }
|
|
|
|
/**
|
|
* Returns a list of entities, customizable in many ways
|
|
*
|
|
* @param filter map of names of fields and their values which must
|
|
* be equal in the fetched entities (null accepted if
|
|
* not used)
|
|
* @param otherFilter list of additional filters (Hibernate Criterion
|
|
* objects, null accepted if not used)
|
|
* @param order order of the entities (Hibernate Order, null accepted
|
|
* if not used)
|
|
* @param afterDb implementation of interface containing a function
|
|
* capable of filtering a list of entities (null accepted
|
|
* if not used)
|
|
* @param page the page number to fetch (starts at 1, null accepted
|
|
* if not used)
|
|
* @return JSON response, success or not
|
|
*/
|
|
protected Response list(Map<String, Object> filter,
|
|
List<Criterion> otherFilter, Order order,
|
|
ListFilter<T> afterDb, Integer page) { %*\ldots*) }
|
|
|
|
/**
|
|
* Returns a response with a single entity given its primary key
|
|
*
|
|
* @param filterOne map of names of columns and their values containing
|
|
* the primary key
|
|
* @return JSON response, success or not
|
|
*/
|
|
protected Response get(Map<String, Object> filterOne) { %*\ldots*) }
|
|
|
|
/**
|
|
* Returns a response with a single entity given its primary key
|
|
*
|
|
* @param pk the primary key
|
|
* @return JSON response, success or not
|
|
*/
|
|
protected Response get(PrimaryKey<T> pk) { %*\ldots*) }
|
|
|
|
/**
|
|
* Returns the existing primary keys given a set of primary keys sent
|
|
* as a JSON array
|
|
*
|
|
* @param requestBody the request body with JSON data
|
|
* @return JSON array of the existing primary keys or error response
|
|
*/
|
|
protected Response existing(InputStream requestBody) { %*\ldots*) }
|
|
}
|
|
\end{lstlisting}
|
|
\begin{it}
|
|
Anche il listato \ref{lst:rdbrest-java} necessita di una breve spiegazione.
|
|
La classe astratta \textit{Readable}, vincolo sul primo parametro generico della classe,
|
|
è il padre di ogni classe ``entità'', mentre \textit{PrimaryKey<T>} è il padre
|
|
della classe contenente la chiave primaria di \textit{T}. Un'istanza della
|
|
classe figlia di \textit{PrimaryKey<T>} è contenuta come attributo nella
|
|
classe ``entità'' \textit{T}, che a sua volta estende \textit{Readable}.
|
|
\end{it}
|
|
|
|
\begin{lstlisting}[caption=Interfaccia della classe \textit{CRUDRESTController}, label={crudrest-java}, language=Java]
|
|
/**
|
|
* A REST Controller readable and writable
|
|
* @param <T> The readable and writable entity class
|
|
*/
|
|
public abstract class CRUDRESTController<T extends CRUDable, U extends
|
|
PrimaryKey<T>> extends ReadableRESTController<T, U> {
|
|
|
|
/**
|
|
* A standard edit route body. Edits an entity and returns a JSON
|
|
* response, both on success and on error.
|
|
*
|
|
* @param filterOne map containing columns and relative values of the
|
|
* primary key of the entity to edit
|
|
* @param form request data containing the fields to edit
|
|
* @return JSON response, positive or negative
|
|
*/
|
|
protected Response edit(Map<String, Object> filterOne,
|
|
MultivaluedMap<String, String> form) { %*\ldots*) }
|
|
|
|
/**
|
|
* A standard edit route body. Edits an entity and returns a JSON
|
|
* response, both on success and on error.
|
|
*
|
|
* @param pKey the entity primary key
|
|
* @param form request data containing the fields to edit
|
|
* @return JSON response, positive or negative
|
|
*/
|
|
protected Response edit(PrimaryKey<T> pKey,
|
|
MultivaluedMap<String, String> form) { %*\ldots*) }
|
|
|
|
/**
|
|
* A standard delete route body. Deletes an entity and returns a JSON
|
|
* response, both on success and on error.
|
|
*
|
|
* @param pKey the entity primary key
|
|
* @return JSON response, positive or negative
|
|
*/
|
|
protected Response delete(PrimaryKey<T> pKey) { %*\ldots*) }
|
|
|
|
/**
|
|
* A standard delete route body. Deletes an entity and returns a JSON
|
|
* response, both on success and on error.
|
|
*
|
|
* @param filterOne map containing columns and relative values of the
|
|
* primary key of the entity to delete
|
|
* @return JSON response, positive or negative
|
|
*/
|
|
protected Response delete(Map<String, Object> filterOne) { %*\ldots*) }
|
|
|
|
/**
|
|
* A standard create route body. Creates an entity and returns a JSON
|
|
* response, both on success and on error.
|
|
*
|
|
* @param form request data containing the entity column values
|
|
* @return JSON response, positive or negative
|
|
*/
|
|
protected Response create(MultivaluedMap<String, String> form) { %*\ldots*) }
|
|
|
|
%*\ldots*)
|
|
}
|
|
\end{lstlisting}
|
|
|
|
\begin{it}
|
|
Si precisa che la classe \textit{CRUDable} è un estensione della classe
|
|
\textit{Readable} che contiene metodi per l'inizializzazione dell'oggetto
|
|
sulla base di dati in input, contenuti in \textit{MultivaluedMap<String,
|
|
String>}. Di conseguenza, le classi ``entità'' che estendono CRUDable
|
|
possono essere usate per operazioni di scrittura, come quelle implementate
|
|
da \textit{CRUDRESTController}.
|
|
\end{it}
|
|
|
|
\begin{lstlisting}[caption=Interfaccia della classe \textit{CRUDUtils}, label={lst:crudutils-java}, language=Java]
|
|
|
|
/**
|
|
* Database access class. Access statically
|
|
*/
|
|
public abstract class CRUDUtils {
|
|
|
|
/**
|
|
* Interface containing a function able to generate an Hibernate Query
|
|
* object and execute it by getting a generic response given the
|
|
* session
|
|
*
|
|
* @param <T> the response type
|
|
*/
|
|
public interface HBMInteraction<T> {
|
|
T execute(Session s);
|
|
}
|
|
|
|
/**
|
|
* Interface containing a function able to generate an Hibernate Query
|
|
* object and execute it by getting a list of rows given the session
|
|
*/
|
|
public interface HBMQuery extends HBMInteraction<List<Object[]>> {
|
|
List<Object[]> execute(Session s);
|
|
}
|
|
|
|
/**
|
|
* Number of items on a single page. Used when pagination is requested
|
|
*/
|
|
public static final int ITEMSPERPAGE = %*\ldots*);
|
|
|
|
%*\ldots*)
|
|
|
|
private static final Logger log = %*\ldots*);
|
|
|
|
/**
|
|
* Save a writable entity on the database
|
|
*
|
|
* @param o the entity to save
|
|
* @param <T> type of the entity. Must be readable and writable
|
|
* @return boolean true = success, false = error
|
|
*/
|
|
public static <T extends CRUDable> boolean persist(final T o) { %*\ldots*) }
|
|
|
|
/**
|
|
* Delete a writable entity on the database
|
|
*
|
|
* @param toDelete the entity to delete
|
|
* @param <T> type of the entity. Must be readable and writable
|
|
* @return boolean true = success, false = error
|
|
*/
|
|
public static <T extends CRUDable> boolean delete(final T toDelete)
|
|
{ %*\ldots*) }
|
|
|
|
%*\ldots*)
|
|
|
|
/**
|
|
* Execute a query which returns data of a generic type
|
|
*
|
|
* @param query HBMInteraction implementation
|
|
* @param <T> The returning type
|
|
* @return the data
|
|
*/
|
|
public static <T> T executeHBMInteraction(HBMInteraction<T> query)
|
|
{ %*\ldots*) }
|
|
|
|
/**
|
|
* Get a list of entities
|
|
*
|
|
* @param tClass class of the entities
|
|
* @param restrictions map of names of fields and their values which
|
|
* must be equal in the fetched entities
|
|
* (null accepted if not used)
|
|
* @param criteria list of additional filters (Hibernate Criterion
|
|
* objects, null accepted if not used)
|
|
* @param <T> entity type. Must be readable
|
|
* @param o order of the entities (Hibernate Order, null accepted if
|
|
* not used)
|
|
* @param page the page number to fetch (starts at 1, null accepted if
|
|
* not used)
|
|
* @return the list of entities
|
|
*/
|
|
public static <T extends Readable> List<T> get(Class<T> tClass,
|
|
Map<String, Object> restrictions, List<Criterion> criteria,
|
|
Order[] o, Integer page) { %*\ldots*) }
|
|
|
|
/**
|
|
* Get a list of entities
|
|
*
|
|
* @param tClass class of the entities
|
|
* @param <T> entity type. Must be readable
|
|
* @return the list of entities
|
|
*/
|
|
public static <T extends Readable> List<T> get(Class<T> tClass) { %*\ldots*) }
|
|
|
|
|
|
/**
|
|
* Get a single entity
|
|
*
|
|
* @param tClass class of the entities
|
|
* @param restrictions map of names of fields and their values which
|
|
* must be equal in the fetched entities
|
|
* (null accepted if not used)
|
|
* @param <T> entity type. Must be readable
|
|
* @return the list of entities
|
|
*/
|
|
public static <T extends Readable> T getOne(Class<T> tClass,
|
|
Map<String, Object> restrictions) { %*\ldots*) }
|
|
|
|
/**
|
|
* Get a single entity
|
|
*
|
|
* @param tClass class of the entities
|
|
* @param pk the primary key object of the entity
|
|
* @param <T> entity type. Must be readable and contain a primary key
|
|
* object
|
|
* @return the list of entities
|
|
*/
|
|
public static <T extends Readable> T getOne(Class<T> tClass,
|
|
PrimaryKey<T> pk) { %*\ldots*) }
|
|
}
|
|
\end{lstlisting}
|
|
|
|
\begin{it}
|
|
Oltre a quanto mostrato, \textit{restaurant} contiene vari metodi utilità per
|
|
la gestione di file \texttt{.properties}, per la validazione dei dati in
|
|
input, per la registrazione della \textit{timestamp} di creazione o modifica
|
|
di un record, e altro ancora.
|
|
\end{it}
|
|
|
|
\begin{it}
|
|
\subsubsection{\textit{restaurant} -- lato client}
|
|
La parte client della libreria \textit{restaurant} è implementata con il
|
|
linguaggio \textit{TypeScript}, un'estensione di \textit{Javascript}
|
|
che aggiunge tipizzazione statica, i classici costrutti della programmazione
|
|
ad oggetti, nonchè il supporto alle generiche. Segue il listato dell'interfaccia della
|
|
classe \textit{RESTService}, la quale contiene metodi statici per comunicare
|
|
con il server asincronamente tramite istanze di classi-entità, cioè classi
|
|
figlie della classe astratta \textit{Table}.
|
|
\end{it}
|
|
\begin{lstlisting}[caption=Interfaccia della classe \textit{RESTService}, label={lst:restservice-ts}, language=Java]
|
|
export class RESTService {
|
|
|
|
...
|
|
|
|
/**
|
|
* Read all the pages of an entity in order to save them for offline
|
|
* readings.
|
|
*
|
|
* @param {{ new(): T }} - class of the table to read
|
|
* @param {any} - key data for the search. Must not contain filters
|
|
* or a field with key '_page'
|
|
* @return {Promise<boolean>} boolean promise that resolves when the
|
|
* reading has been done, true if succesful,
|
|
* false if not
|
|
*/
|
|
public bulkRead<T extends Table>(type: TableStatic, restrictions?: any):
|
|
Promise<boolean> { ... }
|
|
|
|
/**
|
|
* Retrieves a list of entities. When online and requested without
|
|
* filters, the list is saved in the offline storage. When offline,
|
|
* the data from the offline storage is read and filtered accordingly.
|
|
*
|
|
* Request criteria regarding directly properties of the entity MUST
|
|
* have the following format:
|
|
*
|
|
* 'attr-' + name_of_property[.nested] [+ '||' + other_property] +
|
|
* ('-like' | '-eq' | '-flag' | '-gt' | '-lt' | '-gte' | '-lte')
|
|
*
|
|
* Multiple field names separed by '||' state that the search must be
|
|
* done on multiple fields with a logic OR.
|
|
*
|
|
* The different suffixes denote different logical criteria. Look up
|
|
* the documentation of the meetsCriteriaFilter*(...) methods in
|
|
* order to get detailed info on each one.
|
|
*
|
|
* @param {{ new(): T }} type - type of the table to read
|
|
* @param {number} page - number of the page starting at 1 (in offline
|
|
* mode, a 20 entities slice; in online mode,
|
|
* as defined by the server). -1 is for no
|
|
* pagination
|
|
* @param {any} restrictions - restrictions required by the server,
|
|
* and filters.
|
|
* @param {boolean} offline - whether to fetch (true) or not (false)
|
|
* offline entities to sync. Defaults to true
|
|
* @return {Promise<T[]>} - promise of the expanded list, unsuccesful
|
|
* when the storage readings are unsuccesful
|
|
*/
|
|
public read<T extends Table>(type: TableStatic, page: number,
|
|
restrictions?: any, addOffline?: boolean): Promise<T[]> { ... }
|
|
|
|
/**
|
|
* Asks to the server which entity instances of the ones
|
|
* saved in the offline storage exist and deletes the
|
|
* ones that do not exist on the server.
|
|
*
|
|
* @param {TableStatic} type - the entity type
|
|
* @return {Promise<void>} promise rejecting only if an error occurs
|
|
*/
|
|
private async existing(type: TableStatic): Promise<void> { ... }
|
|
|
|
/**
|
|
* Reads a single entity, even in the offline storage if offline.
|
|
*
|
|
* @param {{ new(): T }} type - class of the entity to read
|
|
* @param {any} keyData - the primary key fields and their values
|
|
* in a JS Object, plus the 'id' field of
|
|
* type string used in the end bit of the
|
|
* request path.
|
|
* @return {Promise<T>} - promise of the element, unsuccesful if
|
|
* not found.
|
|
*/
|
|
public readOne<T extends Table>(type: TableStatic | string,
|
|
keyData: any): Promise<T> { ... }
|
|
|
|
/**
|
|
* Updates an entity on the server or, if offline, saves it in the
|
|
* offline sync queue.
|
|
*
|
|
* @param {{ new(): T }} type - class of the entity to be saved
|
|
* @param {T} value - the entity to save
|
|
* @param {any} keyData - the primary key fields and their values
|
|
* in a JS Object, plus the 'id' field of
|
|
* type string used in the end bit of the
|
|
* request path.
|
|
* @return {Promise<T>} - promise of the saved element.
|
|
*/
|
|
public update<T extends Table>(type: TableStatic | string, value: T,
|
|
keyData: any): Promise<T> { ... }
|
|
|
|
/**
|
|
* Creates an entity on the server or, if offline, saves it in the
|
|
* offline sync queue.
|
|
*
|
|
* @param {{ new(): T }} type - class of the entity to be deleted
|
|
* @param {string} id - string used in the end bit of the request path.
|
|
* @param {any} keyData - the primary key fields and their values in
|
|
* a JS Object
|
|
* @return {Promise<T>} - promise of the element deleted
|
|
*/
|
|
public create<T extends Table>(type: TableStatic | string, value: T):
|
|
Promise<T> { ... }
|
|
|
|
/**
|
|
* Deletes an entity on the server or, if offline, marks it for
|
|
* deletion in the offline sync queue.
|
|
*
|
|
* @param {{ new(): T }} type - class of the entity to be saved
|
|
* @param {any} keyData - the primary key fields and their values in
|
|
* a JS Object, plus the 'id' field of type string
|
|
* that goes in the end bit of the request
|
|
* path
|
|
* @return {Promise<T>} - promise of the element created.
|
|
*/
|
|
public delete<T extends Table>(type: TableStatic | string, keyData: any):
|
|
Promise<T> { ... }
|
|
}
|
|
\end{lstlisting}
|
|
|
|
\begin{it}
|
|
\subsection{Stato del progetto}
|
|
\end{it}
|
|
\begin{en}
|
|
\subsection{Project status}
|
|
\end{en}
|
|
|
|
\begin{figure}[H]
|
|
\centering
|
|
\resizebox{\ifdim\width>\linewidth\linewidth\else\width\fi}{!}{
|
|
\begin{ganttchart}{1}{49}
|
|
\gantttitle{2017}{28}
|
|
\gantttitle{2018}{21} \\
|
|
\gantttitle{6}{2}
|
|
\gantttitle{7}{4}
|
|
\gantttitle{8}{5}
|
|
\gantttitle{9}{4}
|
|
\gantttitle{10}{4}
|
|
\gantttitle{11}{5}
|
|
\gantttitle{12}{4}
|
|
\gantttitle{1}{5}
|
|
\gantttitle{2}{4}
|
|
\gantttitle{3}{4}
|
|
\gantttitle{4}{4}
|
|
\gantttitle{5}{4} \\
|
|
\gantttitlelist{25,...,52}{1}
|
|
\gantttitlelist{1,...,21}{1} \\
|
|
\ganttbar[name=prg]{Progettazione}{1}{1} \\
|
|
\ganttlinkedbar[name=ir]{Impl. \textit{restaurant}}{2}{37} \\
|
|
\ganttgroup[name=funz]{Impl. sezioni}{2}{13} \\
|
|
\ganttbar{Inv. di magazzino}{2}{4} \\
|
|
\ganttbar{Agenda}{5}{6} \\
|
|
\ganttbar{Soggetti}{7}{8} \\
|
|
\ganttbar{Ordini cliente}{11}{13} \\
|
|
\ganttbar[name=tf]{Test per sezioni}{14}{16} \\
|
|
\ganttlinkedgroup[name=tr]{Rifinim. \textit{restaurant}}{19}{46} \\
|
|
\ganttbar{Adattam. architettura}{19}{37} \\
|
|
\ganttbar[name=str]{Test}{38}{46} \\
|
|
\ganttbar[name=ril]{Finalizzazione}{47}{49}
|
|
\ganttlink{funz}{tf}
|
|
\ganttlink{prg}{funz}
|
|
\ganttlink{ir}{str}
|
|
\ganttlink{tr}{ril}
|
|
\end{ganttchart}
|
|
}
|
|
\begin{it}
|
|
\caption{Il diagramma di Gantt del progetto
|
|
\textit{InteGRa Mobile}}
|
|
\end{it}
|
|
\begin{en}
|
|
\caption{\textit{InteGRa Mobile} Gantt diagram}
|
|
\end{en}
|
|
\label{fig:ganttitgmob}
|
|
\end{figure}
|
|
|
|
\begin{it}
|
|
Nella lettura del diagramma di Gantt in figura \ref{fig:ganttitgmob} \`e necessario tenere a mente che il completamento dell'intera applicazione non \`e certo per
|
|
la fine di maggio. Ci\`o che dovr\`a essere necessariamente completato \`e la parte pi\`u importante del progetto:
|
|
\textit{restaurant}, il motore di sincronizzazione online/offline e libreria generica che permette di implementare
|
|
velocemente nuove sezioni dell'applicazione.
|
|
\end{it}
|
|
\begin{en}
|
|
\blindtext
|
|
\end{en}
|
|
\begin{it}
|
|
\subsection{Fasi del progetto}
|
|
|
|
Le fasi del progetto, di cui date di inizio e fine sono state specificate nel diagramma in figura \ref{fig:ganttitgmob}, sono:
|
|
|
|
\begin{description}
|
|
|
|
\item[Progettazione] delineazione delle funzionalità da implementare
|
|
nell'applicazione e prime bozze dell'interfaccia utente;
|
|
|
|
\item[Implementazione \textit{restaurant}] implementazione della libreria, su
|
|
cui si basano tutte le sezioni del programma. Vista l'importanza di questo
|
|
componente, gli sviluppi vengono continuati in parallelo con
|
|
l'implementazione delle sezioni;
|
|
|
|
\item[Implementazione sezioni] creazione di interfaccia e logica per le
|
|
funzionalit\'a previste, cio\'e:
|
|
\begin{itemize}
|
|
\item Magazzini;
|
|
\item Agenda;
|
|
\item Soggetti;
|
|
\item Ordini cliente;
|
|
\end{itemize}
|
|
|
|
\item[Test per le sezioni] messa alla prova degli algoritmi e della UI per
|
|
efficacia ed efficienza, nonch\'e eventuali correzioni;
|
|
|
|
\item[Rifinimento \textit{restaurant}] messa alla prova delle scelte
|
|
architetturali e algoritmi usati nella libreria e rifinimento di essi;
|
|
Fase divisa in:
|
|
\begin{description}
|
|
\item[Miglioramento architettura] analisi del protocollo per individuare
|
|
punti di debolezza e possibili ottimizzazioni;
|
|
\item[Test per \textit{restaurant}] messa alla prova dei cambiamenti fatti;
|
|
\end{description}
|
|
|
|
\item[Finalizzazione] task finali del progetto, tra cui branding, operazioni
|
|
pre-rilascio, e gestione di eventuali personalizzazioni richieste dai clienti.
|
|
|
|
\end{description}
|
|
\end{it}
|
|
\begin{en}
|
|
\subsection{Project phases}
|
|
|
|
\blindtext
|
|
\end{en}
|
|
|
|
\section{Il progetto secondario: \textit{Guac Remote}}
|
|
Questo progetto \`e destinato ad un'importante azienda del territorio, produttrice di macchine equilibratrici. Tale applicazione dovrebbe fungere da client di
|
|
desktop remoto (come \textit{TeamViewer}\footnote{TeamViewer: \url{https://www.teamviewer.com/it/}}) per il PC presente nel loro prodotto di punta, un sistema di calibrazione delle
|
|
ruote di autoveicoli funzionante mediante telecamere. Il programma dovrebbe permettere all'operatore di tale prodotto di interagire con il software presente nel macchinario, senza scendere dal veicolo.
|
|
|
|
A causa di urgenza del committente, lo sviluppo di questo progetto ha interrotto e attualmente interrompe gli sviluppi
|
|
per \textit{InteGRa Mobile}.
|
|
|
|
\subsection{Architettura software}
|
|
|
|
\begin{figure}[H]
|
|
\centering
|
|
\resizebox{\ifdim\width>\linewidth\linewidth\else\width\fi}{!}{\input{diagrams/guacremote}}
|
|
\caption{L'architettura di \textit{Guac Remote}}
|
|
\label{fig:argosarch}
|
|
\end{figure}
|
|
|
|
\textit{Guac Remote} è basata su un'applicativo e libreria per la connessione a computer remoto chiamato \textit{Apache Guacamole}\cite{guacamole:website}.
|
|
Tale software è costituito da due parti: \cite{guacdoc:arch}
|
|
\begin{description}
|
|
\item[\textit{guacamole-server} (o \textit{guacd})] un servizio scritto in C che funge da adattatore tra il protocollo \textit{guacamole} e i protocolli VNC, RDP o SSH, utilizzando questi ultimi per stabilire connessioni con gli host remoti;
|
|
\item[\textit{guacamole-client}] una WebApplication scritta tramite \textit{Java servlet} che fornisce un'interfaccia web per interagire con \textit{guacamole-server} e connettersi ai PC remoti.
|
|
\end{description}
|
|
Data la natura open-source del progetto, sia \textit{guacamole-server} che \textit{guacamole-client} possono essere usati come libreria per la realizzazione di software derivati. In particolare, \textit{guacamole-client} può essere scomposto nelle librerie \textit{guacamole-common-js}, che contiene il codice Javascript per il client, e \textit{guacamole-common}, che fornisce classi Java per la connessione con \textit{guacamole-server}. \cite{guacdoc:api}
|
|
|
|
In \textit{Guac Remote}, \textit{Apache Guacamole} è usato per fornire accesso remoto al PC presente nel macchinario, che contiene l'applicativo per la calibrazione, all'applicazione installata sul tablet. Nel dettaglio, \textit{guacamole-common-js} è usato nell'applicazione \textit{mobile} fornire un'interfaccia touchscreen per interagire con l'host remoto, mentre \textit{guacamole-common}, tramite una piccola WebApplication, assieme a \textit{guacamole-server} sono installati sul PC.
|
|
|
|
In aggiunta, tablet e PC possono comunicare informazioni aggiuntive tramite una WebSocket creata al momento della connessione, necessaria per alcune estensioni al protocollo richieste dal cliente.
|
|
|
|
\subsection{Stato del progetto}
|
|
|
|
\begin{figure}[H]
|
|
\centering
|
|
\resizebox{\ifdim\width>\linewidth\linewidth\else\width\fi}{!}{
|
|
\begin{ganttchart}{1}{22}
|
|
\gantttitle{2018}{22} \\
|
|
\gantttitle{2}{6}
|
|
\gantttitle{3}{8}
|
|
\gantttitle{4}{8} \\
|
|
\gantttitlelist{7,...,17}{2} \\
|
|
\foreach \n in {7,...,17}{
|
|
\gantttitle{G}{1}
|
|
\gantttitle{V}{1}
|
|
} \\
|
|
\ganttbar[name=prg]{Incontro con il cliente}{1}{1} \\
|
|
\ganttlinkedbar{Realizzazione primo prototipo}{2}{4} \\
|
|
\ganttlinkedbar{Prima demo al cliente}{5}{5} \\
|
|
\ganttlinkedbar{Stime orarie per sviluppi futuri}{6}{6} \\
|
|
\ganttlinkedgroup[name=impl]{Implementazione richieste \ldots}{8}{12} \\
|
|
\ganttbar{Apertura tastiera e zoom \ldots}{8}{9} \\
|
|
\ganttbar{Input tramite tastiera nativa \ldots}{10}{10} \\
|
|
\ganttbar{Scroll a due dita}{11}{11} \\
|
|
\ganttbar{Interfaccia di configurazione}{12}{12} \\
|
|
\ganttbar[name=demodef]{Demo al cliente}{13}{13} \\
|
|
\ganttlinkedbar{Implementazione richieste agg\ldots}{14}{20} \\
|
|
\ganttlinkedbar{Consegna}{21}{22}
|
|
\ganttlink{impl}{demodef}
|
|
\end{ganttchart}
|
|
}
|
|
\caption{Il diagramma di Gantt del progetto \textit{Guac Remote}}
|
|
\label{fig:ganttargos}
|
|
\end{figure}
|
|
|
|
% TODO: cambiare quando diventa tesina vera e propria
|
|
Questo progetto, al giorno 06/04, non ha ritardi. L'applicazione \'e stata mostrata in demo il giorno 29/03, ed il cliente si ritiene soddisfatto del lavoro fatto fino ad ora.
|
|
|
|
\subsection{Fasi del progetto}
|
|
|
|
Le fasi del progetto, di cui le date di inizio e di fine sono indicate nel diagramma della figura \ref{fig:ganttargos}, sono:
|
|
|
|
\begin{description}
|
|
\item[Incontro con il cliente] primo scambio di informazioni per capire gli obiettivi del progetto;
|
|
\item[Realizzazione primo prototipo] dimostrazione dell'efficacia del protocollo \textit{guacamole} tramite un prototipo del prodotto;
|
|
\item[Prima demo al cliente] demo del prototipo al cliente;
|
|
\item[Stime orarie per sviluppi futuri] delineazione della tabella di marcia per gli sviluppi futuri;
|
|
\item[Implementazione richieste del cliente] sviluppo delle estensioni al protocollo richieste. Nel dettaglio, esse sono:
|
|
\begin{description}
|
|
\item[Apertura tastiera e zoom al ``focus'' di un input] alla pressione di un campo di testo, l'applicazione \textit{mobile} deve automaticamente ingrandire l'area selezionata e mostrare una tastiera;
|
|
\item[Input tramite tastiera nativa Android o iOS] sostituzione della tastiera su schermo di \textit{guacamole-common-js} con quella nativa;
|
|
\item[Scroll a due dita] supporto della gesture di scroll verticale a due dita;
|
|
\item[Interfaccia di configurazione] creazione di una piccola finestra di configurazione, in cui inserire IP e porta del PC;
|
|
\end{description}
|
|
\item[Demo al cliente] dimostrazione degli sviluppi fatti al cliente e eventuale definizione di richieste aggiuntive;
|
|
\item[Implementazione delle richieste aggiuntive del cliente]
|
|
\item[Consegna] operazioni finali del progetto, tra cui branding e compilazione per rilascio.
|
|
\end{description}
|
|
|
|
\newpage
|
|
|
|
\pagenumbering{gobble}
|
|
|
|
\listoffigures
|
|
\lstlistoflistings
|
|
\printbibliography
|
|
|
|
\end{document}
|