Quando abbiamo un’idea per un’App, ci sono tante domande che dobbiamo porci: E’ già presente sul mercato? Potrebbe funzionare? Quanto ci costa svilupparla? Quanto possiamo guadagnarci? Come facciamo a monetizzare da questa idea? In questo articolo voglio parlarvi di come monetizzare. Ci sono tante modalità e tanti strumenti che ci permettono di monetizzare, voglio riportare qui la mia esperienza, i miei errori e i suggerimenti su come utilizzare al meglio questi strumenti. Quando ho cominciato, non avevo per niente le idee chiare. Il mio obiettivo principale non era quello di guadagnarci ma di mettere in piedi un servizio auto-sostenibile, che non avesse costi o comunque fossero molto vicini allo zero, in modo da poter permettermi di vendere il servizio ad un prezzo molto basso. Primo step La prima cosa in assoluto da fare è individuare il nostro pubblico potenziale, per farlo vi consiglio di utilizzare Google adWords oppure facebook Ads. Questi strumenti sono ottimi per capire quale potrebbe essere il nostro pubblico potenziale. Per farlo dobbiamo inserire le parole chiavi riguardo le “specialità” della nostra App, selezionare il range di età e i paesi coinvolti. Il risultato che otteniamo da questa analisi è il numero dei potenziali utilizzatori totali e il paese con più percentuale di utilizzo. Quest’analisi potrebbe aiutarci ad individuare i nostri concorrenti, se esistono, e capire il loro pubblico e cosa ci distingue. Strumenti di guadagno Stabiliamo la modalità di monetizzazione. Ce ne sono davvero di svariate e dipende anche molto dal nostro tipo di business, però le principali più utilizzate e più adattabili nel mondo delle app credo possano essere le seguenti: Pubblicità Prodotti e Abbonamenti in app Vendita e analisi dati Pubblicità: Sebbene Google, uno dei più grandi colossi web al mondo, guadagni solo o quasi con la pubblicità, quest’ultima non è secondo me un ottimo strumento da utilizzare in tutti i contesti in quanto presenta i seguenti problemi: non è adeguata nei momenti iniziali Il guadagno è molto basso a meno che non si posseggano davvero tanti utenti Il prodotto deve attrarre e coinvolgere l’utente per un tempo medio lungo Rischia di essere troppo fastidiosa e potrebbe rovinare l’esperienza utente La pubblicità più semplice da implementare con Flutter guarda caso è la stessa di google, quindi Google AdMob. Vendita di prodotti: se decidi di mettere in vendita la tua app, hai a disposizione attraverso la Google play console diverse possibilità di vendita tra cui: Prezzo download App Ci permette di stabilire un prezzo per il download dell’app. Questa è la cosa più semplice da implementare, in quanto basta impostare il prezzo dell’app all’interno della console. Una volta fatto, abbiamo terminato! a meno che non si voglia mettere un ulteriore controllo all’interno dell’app, per evitare che vengano create delle copie pirate della nostra app e non la compri più nessuno. Secondo me questa modalità di vendita è ormai superata, la gente non si fida a comprare al buio, sebbene può sempre richiedere il rimborso ;credo comunque che imporre un prezzo sin dall’inizio sia un ostacolo. Sicuramente per alcune app ha senso, spesso viene utilizzato per la versione pro dell’app. Prodotti in App questa tipologia di prodotto dà accesso all’applicazione in versione gratuita e mette a disposizione alcuni prodotti all’interno dell’app stessa. In questo caso però oltre a definire i vari prodotti, scegliere e impostare i vari prezzi, dobbiamo anche sviluppare un pezzo nella nostra app per recuperare i prodotti da google play store per poter mostrare i prodotti. Anche se sono a disposizione librerie e plugin, prevede comunque un nostro sviluppo per poterlo utilizzare. Abbonamenti in App Gli abbonamenti sono molto simili ai prodotti con la differenza sostanziale che hanno una scadenza quindi hanno una validità temporale. Differenze tra Prodotti e Abbonamenti Il prodotto in App può essere utilizzato solo una volta, consumato subito, oppure potrebbe essere utilizzato per sempre, dipende dal valore che noi decidiamo di attribuirgli nella nostra app. Una volta comprato sblocca qualcosa. L’abbonamento invece ha una scadenza specifica, quindi comprende lo stesso comportamento del prodotto base ma ha una durata che può essere settimanale, mensile, trimestrale e annuale. Quanto davvero si guadagna dalla vendita su google play store? Questa è una cosa che all’inizio non è nota a tutti. Come nella vita normale, quando ci sono di mezzo i soldi, tutti ci devono guadagnare. Per tutti in questo caso intendo Google e lo stato. Google mette a disposizione: una vetrina uno store gestione rimborsi gestione prezzi normalizzazione valute tasse in tutti i Paesi del mondo. e tante altre cose Sebbene questo servizio sia per il 95% automatico, s’ha da pagare. Ma la cosa interessante e curiosa è che la percentuale che prende Google è di circa il 30% del prezzo della nostra App. la percentuale invece dovuta allo stato dipende dal Paese di riferimento dell’acquisto. Esempio: se vendiamo un’App dal costo di 2 euro, il nostro guadagno netto effettivo sarà di 1 euro circa. Invece. nel caso di Abbonamento, la percentuale spettante a Google, guarda caso cambia e scende al 15%. Ma perchè Google ci invoglia a vendere abbonamenti? Non ci vuole tanto ad arrivarci, il prodotto una volta venduto ha terminato il suo guadagno. L’abbonamento invece è per sempre. Stabiliamo il prezzo Non è semplice stabilire il prezzo giusto. E’ molto importante sceglierlo adeguatamente. Io ho fatto tanti errori in fase di scelta e credo che sia ancora sbagliato. Però l’errore principale che ho commesso, e credo commettano in molti, è il fatto che sottovalutiamo il prodotto che abbiamo sviluppato. Lavorandoci da tanto e conoscendolo bene, tendiamo a sottovalutare. Dalla mia esperienza ho capito che il modo giusto per definirlo è moltiplicare per 3 o per 4 il prezzo che io stesso sarei disposto a pagare. Google play console permette di inserire diversi prezzi per ogni paese, sebbene alcuni paesi siano più ricchi e più disposti a pagare per un’App, io ho deciso di mettere per tutti lo stesso prezzo, nel mio caso credo sia giusto. Anche perché si tratta di un prezzo molto basso. Inoltre l’app send to kindle è destinata ai possessori di un kindle che quindi, avendo comprato un kindle, credo possano essere disposti a pagare qualche euro per una comoda funzionalità. Definiamo la strategia Questo è il punto più complicato e dipende molto dal tipo di app o servizio che abbiamo implementato. Io per definirla in send to kindle, ho fatto questo ragionamento: Posso guadagnare solo con la pubblicità? Sebbene il mio pubblico stia crescendo e può diventare molto ampio, per adesso la risposta è no e credo lo sarà per molto. Perchè? Ho deciso di rendere l’app maledettamente veloce e quindi l’interazione utente è troppo breve. L’utente entra, invia il contenuto al kindle e ha finito. Non ha altro da fare nella mia app. Quindi le scelte che avrei potuto seguire erano le seguenti: lavorare per proporre qualcos’altro legato al contesto utilizzare uno strumento diverso rispetto alla pubblicità Alla fine ho deciso di non usare la pubblicità come guadagno principale. Ma allora, perchè l’ho usata lo stesso? Ho usato la pubblicità come elemento di fastidio e noia. Sì, avete capito bene noia, ho creato un sistema in modo che ad ogni tre invii si è costretti a guardare un video, per poter proseguire. La gente può continuare a guardare video, ma prima o poi si annoierà così tanto di guardare video pubblicitari che deciderà di valutare la rimozione della pubblicità. Oltre alla conversione degli articoli, send to kindle converte anche pdf e diversi formati di ebook. Di recente ho deciso anche di introdurre un abbonamento per questo che consente di effettuare un numero infinito di conversioni per un anno. Ho deciso di mettere un abbonamento perché la conversione utilizza un container in cloud ed ho dei costi per il servizio cloud da pagare ogni mese. In questo modo se i miei utenti dovessero sottoscrivere l’abbonamento, riceverò qualcosa ogni anno che spero possa essere utile ai fini di pagare il servizio in cloud e guadagnarci anche qualcosa. Finchè troverete l’app nello store vuol dire che sta funzionando ;) Considerazioni Oggigiorno quasi tutti cercano di proporre un servizio e contestualmente un abbonamento. Questo perchè a lungo andare è il più profittevole. Secondo me ogni app e ogni sistema necessita delle giuste considerazioni e scelte. Credo che tutti oggi possano comprendere e sono più propensi ad acquistare un oggetto fatto e finito anziché sottoscrivere un abbonamento. L’abbonamento non è visto molto bene da tutti, inoltre va motivato bene e bisogna ragionare bene su come venderlo e far comprendere tutti i vantaggi che si ottengono. Comunque non vi fasciate troppo la testa, il prezzo non è per sempre. Google play console, permette di cambiare il prezzo in ogni momento. Se uno lavora bene e fa crescere il suo servizio/app, il valore di quest’ultima cresce quindi è giusto aumentare il prezzo e aggiungere nuove funzionalità che aprono le porte a nuovi guadagni.
When we have an idea for an App, there are many questions that come to our mind, the first ones are: Is this app already on the market? Can this idea work? How much does it cost to develop it? How much can we earn? How to monetize this idea? In this article I want to talk about how to monetize. There are many ways and many tools that allow us to monetize, I want to report here my experience, my mistakes and suggestions on how to use these tools at the best. When I started, I had no clear idea. My main goal was not to earn money but to set up a self-sustainable service, which had no costs or costs very close to zero, so that I could afford to sell the service at a low price. First step The first thing to do is to identify our potential audience, to do this I recommend using Google adWords or Facebook Ads. These tools are great to understand what our potential audience might be. To do this we must enter the keywords of our App services, select the age range and the countries involved. The result we get from this analysis is the total number of potential users and the country with the highest percentage of use. It could help us identify our competitors and understand what is their audience like and what sets us apart. Earning tools It is time to establish our monetization method; there are really many different ways and choosing one of them depends on our type of business. However, the most used ones and the most adaptable in the world of Mobile Apps are the following: Advertising Products and Subscriptions in the app Sales and data analysis Advertising: Although one of the largest web giants in the world Google earns only or almost with advertising, the latter is not, in my opinion, a good tool to be used in all contexts, I believe it has the following problems: It is not appropriate at the beginning The profit is very low unless you have many users The product must attract and involve the user for some time. There is the risk of being too annoying and could ruin the user experience. The simplest advertising to implement with Flutter is the same used in google, therefore Google AdMob. Sale of products: if you decide to put your App on sale, you have various sales options available through the Google play console, including: **The price related to the App download ** This allows us to set a price for the App download. This is the simplest thing to implement, as we only have to just set the price of the app inside the console. Once done, we can be over with it unless we want to put a further check inside the app to prevent people from creating pirated copies of our app and from buying it. In my opinion setting a price from the beginning is now outdated, people do not trust buying in the dark. Although they can always ask for a refund, I think it is still an obstacle to impose a price from the beginning. It certainly makes sense for some apps, it is often used for the pro version of the app. Products in the App This type of product gives access to a free version of the application and makes the products available within the app itself. In this case, however, in addition to defining the various products, choosing and setting the various prices, we must also develop a piece in our app to retrieve the products from the Google Play Store in order to be able to show the products. Although libraries and plugins are available, we still have to develop it in order to use it. Subscriptions in the App Subscriptions are very similar to products with the difference that they have an expiring date and therefore have a temporal validity. Differences between Products and Subscriptions The product in the App can be used only once, expended immediately, or it could be used forever: it actually depends on the value that we decide to assign in our app. Once bought, it unlocks something. The subscription instead has a specific deadline, therefore it includes the same behavior as the base product but has a duration that can be weekly, monthly, three-monthly and annual. How much do you really earn from selling on google play store? This is something that is not known at first. As in normal life, when money is involved, everyone has to earn it. By “everyone"in this case I mean Google and the state. Google offers: a showcase a store refund management price management currency normalization taxes in all countries of the world. And lot of other stuff Although the 95% of this service is completely automatic, it has to be paid. But the interesting thing is that the percentage that Google takes is about 30% of the price of our app. the percentage due to the State instead depends on the country of reference of the purchase. Example: if we sell an app with a cost of 2 euros, our actual net profit will be around 1 Euro. Instead if we subscribe the percentage due to Google changes and drops to 15%. But why does Google encourages us to sell subscriptions? It does not take long to get there, once we have sold the product, we won’t have any further earnings. The subscription instead is forever. We establish the price It is not easy to establish the right price but it is very important to choose it properly. The main mistake I made, and I believe many people commit, is the fact that we underestimate the product we have developed. Since we have been working on it for a long time and know it well, we tend to underestimate it. From my experience, I understood that the right way to define it is to multiply by 3 or 4 the price that I would be willing to pay. Google play console allows you to enter different prices for each country, although some countries are richer and more willing to pay for an App, I decided to put the same price for everyone, in my case I think it’s right. Also because it is a very low price. In addition, the App send to kindle is intended for the owners of a kindle who therefore can be willing to pay a few euros for a convenient feature. We define the strategy This I think is the most complicated part and depends a lot on the type of app or service that we have implemented. Consider send to kindle I can ask myself this question: Can I earn only with advertising? Although my audience is growing and can become very large for now, the answer is no and I believe it will be no also in the future. Why? I decided to make the app bloody fast and therefore the user interaction is too short. The user enters, sends the content to the kindle and he is done. He has nothing else to do in my app. So the choices I could follow were the following: Suggest something else related to the context Use a different tool rather than advertising. In the end I decided not to use advertising as the main income. So why did I use it anyway? I used advertising as an element of annoyance and boredom. Yes you got it right! boredom, I created a system in a way that every three submissions, you are forced to watch an advert video, in order to continue. People can continue watching videos, but sooner or later they will get so bored with watching advertising videos that they will decide to consider removing the advertisements. In addition to converting articles, send to kindle also converts pdfs and various ebook formats. I recently decided to introduce a subscription for this. It allows you to perform an infinite number of conversions for a year. I decided to put a subscription because the conversion uses a container in the cloud and I have costs for the cloud service to be paid every month. In this way, if my users had to subscribe, I would receive something every year which, I hope, will be useful in order to pay for the cloud service and also earn something. As long as you’ll find it in the app store means it’s working;) Considerations In these days almost everyone is trying to offer a service using a subscription. This is because in the long run it is the most profitable. In my opinion, every app and every system need the right considerations and choices. I think a lot of people today can understand and are more likely to buy a product on time rather than signing up for a subscription. People don’t like the subscription, moreover it must be well motivated and you have to think about how to sell it and make everyone understand the advantages that they get. However, do not bandage your head too much, the price is not forever. Google play console, allows you to change the price at any time. If the person works well and the app grows, the value of the latter grows so it is correct to increase the price and add new features that open new doors to new earnings.
Quando abbiamo un’idea per un’App, ci sono tante domande che dobbiamo porci: E’ già presente sul mercato? Potrebbe funzionare? Quanto ci costa svilupparla? Quanto possiamo guadagnarci? Come facciamo a monetizzare da questa idea? In questo articolo voglio parlarvi di come monetizzare. Ci sono tante modalità e tanti strumenti che ci permettono di monetizzare, voglio riportare qui la mia esperienza, i miei errori e i suggerimenti su come utilizzare al meglio questi strumenti. Quando ho cominciato, non avevo per niente le idee chiare. Il mio obiettivo principale non era quello di guadagnarci ma di mettere in piedi un servizio auto-sostenibile, che non avesse costi o comunque fossero molto vicini allo zero, in modo da poter permettermi di vendere il servizio ad un prezzo molto basso. Primo step La prima cosa in assoluto da fare è individuare il nostro pubblico potenziale, per farlo vi consiglio di utilizzare Google adWords oppure facebook Ads. Questi strumenti sono ottimi per capire quale potrebbe essere il nostro pubblico potenziale. Per farlo dobbiamo inserire le parole chiavi riguardo le “specialità” della nostra App, selezionare il range di età e i paesi coinvolti. Il risultato che otteniamo da questa analisi è il numero dei potenziali utilizzatori totali e il paese con più percentuale di utilizzo. Quest’analisi potrebbe aiutarci ad individuare i nostri concorrenti, se esistono, e capire il loro pubblico e cosa ci distingue. Strumenti di guadagno Stabiliamo la modalità di monetizzazione. Ce ne sono davvero di svariate e dipende anche molto dal nostro tipo di business, però le principali più utilizzate e più adattabili nel mondo delle app credo possano essere le seguenti: Pubblicità Prodotti e Abbonamenti in app Vendita e analisi dati Pubblicità: Sebbene Google, uno dei più grandi colossi web al mondo, guadagni solo o quasi con la pubblicità, quest’ultima non è secondo me un ottimo strumento da utilizzare in tutti i contesti in quanto presenta i seguenti problemi: non è adeguata nei momenti iniziali Il guadagno è molto basso a meno che non si posseggano davvero tanti utenti Il prodotto deve attrarre e coinvolgere l’utente per un tempo medio lungo Rischia di essere troppo fastidiosa e potrebbe rovinare l’esperienza utente La pubblicità più semplice da implementare con Flutter guarda caso è la stessa di google, quindi Google AdMob. Vendita di prodotti: se decidi di mettere in vendita la tua app, hai a disposizione attraverso la Google play console diverse possibilità di vendita tra cui: Prezzo download App Ci permette di stabilire un prezzo per il download dell’app. Questa è la cosa più semplice da implementare, in quanto basta impostare il prezzo dell’app all’interno della console. Una volta fatto, abbiamo terminato! a meno che non si voglia mettere un ulteriore controllo all’interno dell’app, per evitare che vengano create delle copie pirate della nostra app e non la compri più nessuno. Secondo me questa modalità di vendita è ormai superata, la gente non si fida a comprare al buio, sebbene può sempre richiedere il rimborso ;credo comunque che imporre un prezzo sin dall’inizio sia un ostacolo. Sicuramente per alcune app ha senso, spesso viene utilizzato per la versione pro dell’app. Prodotti in App questa tipologia di prodotto dà accesso all’applicazione in versione gratuita e mette a disposizione alcuni prodotti all’interno dell’app stessa. In questo caso però oltre a definire i vari prodotti, scegliere e impostare i vari prezzi, dobbiamo anche sviluppare un pezzo nella nostra app per recuperare i prodotti da google play store per poter mostrare i prodotti. Anche se sono a disposizione librerie e plugin, prevede comunque un nostro sviluppo per poterlo utilizzare. Abbonamenti in App Gli abbonamenti sono molto simili ai prodotti con la differenza sostanziale che hanno una scadenza quindi hanno una validità temporale. Differenze tra Prodotti e Abbonamenti Il prodotto in App può essere utilizzato solo una volta, consumato subito, oppure potrebbe essere utilizzato per sempre, dipende dal valore che noi decidiamo di attribuirgli nella nostra app. Una volta comprato sblocca qualcosa. L’abbonamento invece ha una scadenza specifica, quindi comprende lo stesso comportamento del prodotto base ma ha una durata che può essere settimanale, mensile, trimestrale e annuale. Quanto davvero si guadagna dalla vendita su google play store? Questa è una cosa che all’inizio non è nota a tutti. Come nella vita normale, quando ci sono di mezzo i soldi, tutti ci devono guadagnare. Per tutti in questo caso intendo Google e lo stato. Google mette a disposizione: una vetrina uno store gestione rimborsi gestione prezzi normalizzazione valute tasse in tutti i Paesi del mondo. e tante altre cose Sebbene questo servizio sia per il 95% automatico, s’ha da pagare. Ma la cosa interessante e curiosa è che la percentuale che prende Google è di circa il 30% del prezzo della nostra App. la percentuale invece dovuta allo stato dipende dal Paese di riferimento dell’acquisto. Esempio: se vendiamo un’App dal costo di 2 euro, il nostro guadagno netto effettivo sarà di 1 euro circa. Invece. nel caso di Abbonamento, la percentuale spettante a Google, guarda caso cambia e scende al 15%. Ma perchè Google ci invoglia a vendere abbonamenti? Non ci vuole tanto ad arrivarci, il prodotto una volta venduto ha terminato il suo guadagno. L’abbonamento invece è per sempre. Stabiliamo il prezzo Non è semplice stabilire il prezzo giusto. E’ molto importante sceglierlo adeguatamente. Io ho fatto tanti errori in fase di scelta e credo che sia ancora sbagliato. Però l’errore principale che ho commesso, e credo commettano in molti, è il fatto che sottovalutiamo il prodotto che abbiamo sviluppato. Lavorandoci da tanto e conoscendolo bene, tendiamo a sottovalutare. Dalla mia esperienza ho capito che il modo giusto per definirlo è moltiplicare per 3 o per 4 il prezzo che io stesso sarei disposto a pagare. Google play console permette di inserire diversi prezzi per ogni paese, sebbene alcuni paesi siano più ricchi e più disposti a pagare per un’App, io ho deciso di mettere per tutti lo stesso prezzo, nel mio caso credo sia giusto. Anche perché si tratta di un prezzo molto basso. Inoltre l’app send to kindle è destinata ai possessori di un kindle che quindi, avendo comprato un kindle, credo possano essere disposti a pagare qualche euro per una comoda funzionalità. Definiamo la strategia Questo è il punto più complicato e dipende molto dal tipo di app o servizio che abbiamo implementato. Io per definirla in send to kindle, ho fatto questo ragionamento: Posso guadagnare solo con la pubblicità? Sebbene il mio pubblico stia crescendo e può diventare molto ampio, per adesso la risposta è no e credo lo sarà per molto. Perchè? Ho deciso di rendere l’app maledettamente veloce e quindi l’interazione utente è troppo breve. L’utente entra, invia il contenuto al kindle e ha finito. Non ha altro da fare nella mia app. Quindi le scelte che avrei potuto seguire erano le seguenti: lavorare per proporre qualcos’altro legato al contesto utilizzare uno strumento diverso rispetto alla pubblicità Alla fine ho deciso di non usare la pubblicità come guadagno principale. Ma allora, perchè l’ho usata lo stesso? Ho usato la pubblicità come elemento di fastidio e noia. Sì, avete capito bene noia, ho creato un sistema in modo che ad ogni tre invii si è costretti a guardare un video, per poter proseguire. La gente può continuare a guardare video, ma prima o poi si annoierà così tanto di guardare video pubblicitari che deciderà di valutare la rimozione della pubblicità. Oltre alla conversione degli articoli, send to kindle converte anche pdf e diversi formati di ebook. Di recente ho deciso anche di introdurre un abbonamento per questo che consente di effettuare un numero infinito di conversioni per un anno. Ho deciso di mettere un abbonamento perché la conversione utilizza un container in cloud ed ho dei costi per il servizio cloud da pagare ogni mese. In questo modo se i miei utenti dovessero sottoscrivere l’abbonamento, riceverò qualcosa ogni anno che spero possa essere utile ai fini di pagare il servizio in cloud e guadagnarci anche qualcosa. Finchè troverete l’app nello store vuol dire che sta funzionando ;) Considerazioni Oggigiorno quasi tutti cercano di proporre un servizio e contestualmente un abbonamento. Questo perchè a lungo andare è il più profittevole. Secondo me ogni app e ogni sistema necessita delle giuste considerazioni e scelte. Credo che tutti oggi possano comprendere e sono più propensi ad acquistare un oggetto fatto e finito anziché sottoscrivere un abbonamento. L’abbonamento non è visto molto bene da tutti, inoltre va motivato bene e bisogna ragionare bene su come venderlo e far comprendere tutti i vantaggi che si ottengono. Comunque non vi fasciate troppo la testa, il prezzo non è per sempre. Google play console, permette di cambiare il prezzo in ogni momento. Se uno lavora bene e fa crescere il suo servizio/app, il valore di quest’ultima cresce quindi è giusto aumentare il prezzo e aggiungere nuove funzionalità che aprono le porte a nuovi guadagni.
When we have an idea for an App, there are many questions that come to our mind, the first ones are: Is this app already on the market? Can this idea work? How much does it cost to develop it? How much can we earn? How to monetize this idea? In this article I want to talk about how to monetize. There are many ways and many tools that allow us to monetize, I want to report here my experience, my mistakes and suggestions on how to use these tools at the best. When I started, I had no clear idea. My main goal was not to earn money but to set up a self-sustainable service, which had no costs or costs very close to zero, so that I could afford to sell the service at a low price. First step The first thing to do is to identify our potential audience, to do this I recommend using Google adWords or Facebook Ads. These tools are great to understand what our potential audience might be. To do this we must enter the keywords of our App services, select the age range and the countries involved. The result we get from this analysis is the total number of potential users and the country with the highest percentage of use. It could help us identify our competitors and understand what is their audience like and what sets us apart. Earning tools It is time to establish our monetization method; there are really many different ways and choosing one of them depends on our type of business. However, the most used ones and the most adaptable in the world of Mobile Apps are the following: Advertising Products and Subscriptions in the app Sales and data analysis Advertising: Although one of the largest web giants in the world Google earns only or almost with advertising, the latter is not, in my opinion, a good tool to be used in all contexts, I believe it has the following problems: It is not appropriate at the beginning The profit is very low unless you have many users The product must attract and involve the user for some time. There is the risk of being too annoying and could ruin the user experience. The simplest advertising to implement with Flutter is the same used in google, therefore Google AdMob. Sale of products: if you decide to put your App on sale, you have various sales options available through the Google play console, including: **The price related to the App download ** This allows us to set a price for the App download. This is the simplest thing to implement, as we only have to just set the price of the app inside the console. Once done, we can be over with it unless we want to put a further check inside the app to prevent people from creating pirated copies of our app and from buying it. In my opinion setting a price from the beginning is now outdated, people do not trust buying in the dark. Although they can always ask for a refund, I think it is still an obstacle to impose a price from the beginning. It certainly makes sense for some apps, it is often used for the pro version of the app. Products in the App This type of product gives access to a free version of the application and makes the products available within the app itself. In this case, however, in addition to defining the various products, choosing and setting the various prices, we must also develop a piece in our app to retrieve the products from the Google Play Store in order to be able to show the products. Although libraries and plugins are available, we still have to develop it in order to use it. Subscriptions in the App Subscriptions are very similar to products with the difference that they have an expiring date and therefore have a temporal validity. Differences between Products and Subscriptions The product in the App can be used only once, expended immediately, or it could be used forever: it actually depends on the value that we decide to assign in our app. Once bought, it unlocks something. The subscription instead has a specific deadline, therefore it includes the same behavior as the base product but has a duration that can be weekly, monthly, three-monthly and annual. How much do you really earn from selling on google play store? This is something that is not known at first. As in normal life, when money is involved, everyone has to earn it. By “everyone"in this case I mean Google and the state. Google offers: a showcase a store refund management price management currency normalization taxes in all countries of the world. And lot of other stuff Although the 95% of this service is completely automatic, it has to be paid. But the interesting thing is that the percentage that Google takes is about 30% of the price of our app. the percentage due to the State instead depends on the country of reference of the purchase. Example: if we sell an app with a cost of 2 euros, our actual net profit will be around 1 Euro. Instead if we subscribe the percentage due to Google changes and drops to 15%. But why does Google encourages us to sell subscriptions? It does not take long to get there, once we have sold the product, we won’t have any further earnings. The subscription instead is forever. We establish the price It is not easy to establish the right price but it is very important to choose it properly. The main mistake I made, and I believe many people commit, is the fact that we underestimate the product we have developed. Since we have been working on it for a long time and know it well, we tend to underestimate it. From my experience, I understood that the right way to define it is to multiply by 3 or 4 the price that I would be willing to pay. Google play console allows you to enter different prices for each country, although some countries are richer and more willing to pay for an App, I decided to put the same price for everyone, in my case I think it’s right. Also because it is a very low price. In addition, the App send to kindle is intended for the owners of a kindle who therefore can be willing to pay a few euros for a convenient feature. We define the strategy This I think is the most complicated part and depends a lot on the type of app or service that we have implemented. Consider send to kindle I can ask myself this question: Can I earn only with advertising? Although my audience is growing and can become very large for now, the answer is no and I believe it will be no also in the future. Why? I decided to make the app bloody fast and therefore the user interaction is too short. The user enters, sends the content to the kindle and he is done. He has nothing else to do in my app. So the choices I could follow were the following: Suggest something else related to the context Use a different tool rather than advertising. In the end I decided not to use advertising as the main income. So why did I use it anyway? I used advertising as an element of annoyance and boredom. Yes you got it right! boredom, I created a system in a way that every three submissions, you are forced to watch an advert video, in order to continue. People can continue watching videos, but sooner or later they will get so bored with watching advertising videos that they will decide to consider removing the advertisements. In addition to converting articles, send to kindle also converts pdfs and various ebook formats. I recently decided to introduce a subscription for this. It allows you to perform an infinite number of conversions for a year. I decided to put a subscription because the conversion uses a container in the cloud and I have costs for the cloud service to be paid every month. In this way, if my users had to subscribe, I would receive something every year which, I hope, will be useful in order to pay for the cloud service and also earn something. As long as you’ll find it in the app store means it’s working;) Considerations In these days almost everyone is trying to offer a service using a subscription. This is because in the long run it is the most profitable. In my opinion, every app and every system need the right considerations and choices. I think a lot of people today can understand and are more likely to buy a product on time rather than signing up for a subscription. People don’t like the subscription, moreover it must be well motivated and you have to think about how to sell it and make everyone understand the advantages that they get. However, do not bandage your head too much, the price is not forever. Google play console, allows you to change the price at any time. If the person works well and the app grows, the value of the latter grows so it is correct to increase the price and add new features that open new doors to new earnings.
Oggi voglio raccontarvi una storia, la storia di una mia creazione e del progetto che ad oggi mi ha portato più successo e soddisfazioni. Partiamo dagli albori Io sono nato e soprattutto cresciuto nell’era digitale e sono sempre stato a contatto con la tecnologia, purtroppo non ho mai avuto un ottimo rapporto invece con i libri, credo e spero di poter recuperare in futuro. Questo mio mal rapporto con i libri però, in un certo senso, mi ha portato al successo di cui sto per raccontarvi. E’ strano in effetti ma è proprio così. Tanto tempo fa, essendo frustrato dal fatto che non leggevo abbastanza, ho deciso di comprare un Kindle pensando che mi avrebbe dato nuove opportunità di lettura. Il Kindle in effetti mi ha dato diverse soddisfazioni, ho letto diversi libri ma il mio utilizzo principale è sempre stato quello di leggere articoli online recuperati dal web. Articoli di programmazione, tecnologia, scienza e matematica. Sì, come avete già potuto capire, non sono un letterato e men che meno un filosofo, scrivo perchè mi piace ma non credo di saperlo fare bene non avendo studiato materie adeguate. Vabbè… mi sà che sono troppo prolisso adesso. Veniamo al dunque… ! perchè vi ho raccontato questa storia? Perché il Kindle è stato il punto cardine del mio successo? Avendone apprezzato tanto le sue capacità, ho deciso di consigliarlo a molti miei amici e parenti. L’ho consigliato anche a un mio caro collega di lavoro con il quale ci siamo spesso dati suggerimenti a vicenda su come ottenere il massimo dal kindle. L’app che più utilizzavamo era un’ app per smartphone che permetteva di inviare articoli al kindle. Un giorno quest’ app smette di funzionare e sparisce dallo store in maniera misteriosa e ahimè! non se ne sa più nulla. Nello store erano presenti altre app per fare questa azione, ma non funzionavano benissimo o alcune di queste erano a pagamento. Io e il mio collega eravamo un po’ frustrati da questa cosa. Il mio collega Un giorno il mio collega mi dice: ma perchè non la fai tu l’app per inviare contenuti al kindle? Io ci ho pensato un attimo, non sapevo bene da dove cominciare, ma poi mi son detto: lo sai che c’è? sono programmatore, ho le basi posso farcela! E’ così è nato Send To Kindle. Oggi devo molto al mio caro collega e alla sua richiesta. Quando ho rilasciato la prima versione dell’app, era molto spartana, l’ho implementata per me, i miei amici/parenti e soprattutto per il mio collega, non credevo e non potevo mai immaginare che ad oggi, dopo un anno dalla sua creazione, ho raggiunto più di 70.000 installazioni in tutto il mondo e questo numero cresce giornalmente. Cosa Abbiamo imparato? Quest’app mi ha fatto crescere in poco tempo e mi ha fatto capire tante cose che voglio riassumere e condividere con voi: Se non ci provi non ci riesci A volte ci pensiamo troppo, oppure attendiamo la perfezione per poter rilasciare qualcosa. Invece, in molti casi è meglio buttarla lì e poi se funziona siamo sempre in tempo per aggiornarla ed aggiungere nuove feature. Più fai e più possibilità hai di riuscita Se ci riesci devi correre come un matto Anche se hai pensato e preparato tutto ai minimi dettagli, una volta che la tua app o il tuo sito iniziano ad ingranare e si popolano di utenti, cominciano i problemi! Ed è li che devi fare il possibile in modo da perdere meno utenti possibili. Impossibile accontentare tutti Ogni utente ha le sue necessità e non è possibile fare un prodotto generico che accontenti tutti. Non seguire troppo e valuta bene le richieste degli utenti L’ho provato sulla mia pelle, gli utenti potrebbero non accontentarsi mai! Un esempio: Send To Kindle nel primo periodo inviava solo articoli. Tutte le recensioni negative dicevano che non inviava documenti/file. Allora ho implementato l’invio dei documenti che mi è costato tempo e denaro. Adesso secondo voi cosa dicono gli utenti? Alcuni sono soddisfatti ma molti altri mettono sempre una stella e dicono: non invia file superiori a 25 mb. Secondo voi, se mai aumentassi questo limite cosa diranno? Esegui tanti rilasci frequenti Anche se un giorno tutto va bene pensa sempre che il giorno dopo andrà tutto di ma* Presta attenziona alle risposte date ai clienti Aggiornati sempre sulle mosse dei tuoi concorrenti Continua ad aggiornare ed innovare Considerazioni finali Sono conscio del fatto che Send To Kindle non sia un’invenzione fuori dal normale o un’ innovazione. In effetti, non ho inventato qualcosa di completamente nuovo; ho reso migliore, più efficiente e integrato qualcosa che c’era già con tecnologie nuove. Credo che il mio punto di forza sia il fatto di aver reso fruibile il servizio da mobile. A volte basta poco per ottenere successo, basta essere meglio dei propri concorrenti. Sicuramente però per farlo sono necessari tanti sforzi e tanto lavoro. Se siete arrivati a leggere fino a qui complimenti!!! Se non l’avete ancora fatto scaricate Send To Kindle, e inviate questo articolo o altri articoli al kindle! Assaporate una lettura più rilassante!.
Today I want to tell you a story, the story of my creation and the project that has brought me more success and satisfaction to date. Let’s start from the dawn I was born and especially raised in the digital age and I have always been in contact with technology. Unfortunately I have never had an excellent relationship with books, I believe and I hope to be able to catch up in the future. This bad relationship with books, however, has led me to the success I’m about to tell you. A long time ago, being frustrated by the fact that I wasn’t reading enough, I decided to buy a Kindle thinking that would give me new reading opportunities. The Kindle indeed gave me different satisfactions, I have read several books but my main use has always been to read online articles retrieved from the web. I used to read programming, technology, science and math articles. Yes, as you have already understood, I am not a writer and least of all a philosopher, I write because I like it but I don’t think I can do it well as I haven’t studied subjects related to writing. Ok, I’m too long-winded now! But why did I tell you this story? Why was the Kindle the cornerstone of my success? Since I appreciated its skills so much, I decided to recommend it to many of my friends and relatives. I also recommended it to a close colleague of mine, with whom we often given each other suggestions on how to get the most out of the kindle. The app that we used most was a smartphone app that allowed us to send items to the kindle. One day this app stopped working and disappeared from the store in a mysterious and unknown way. There were other apps in the store that did this, but they didn’t work very well and some of them weren’t free. My colleague and I were somewhat frustrated by this. My colleague One day my colleague tells me: why don’t you make the app to send content to the kindle? I thought about it for a moment, I wasn’t sure from where to start, but then I said to myself: I’m a programmer and I have the basics … I can do it! This is how Send To Kindle was born. Today I owe a lot to my dear colleague and to his question. When I released the first version of the app, it was very spartan, I implemented it for myself, my friends/relatives and especially for my colleague. I did not believe and I could never imagine that today, one year after its creation, I would have reached more than 70,000 installations worldwide and this number grows daily. What have we learned? This app made me grow in a short time and made me understand many things that I want to summarize and share with you: If don’t try, you don’t succeed Sometimes we think about it too much or we wait for perfection before releasing something. Instead, in many cases, it is better to throw it there and then if it works we are always in time to update it and add new features. The more you do/work on it and the more chances you have to succeed If you succeed you have to run like crazy It is Impossible to satisfy everyone Even if you have thought and worked to the smallest details, once your app or your site begin to mesh and populates with users, your problems will begin and that’s where you have to do your best in order not to lose your users. It is Impossible to satisfy everyone Every user has his own needs and it is not possible to make a generic product that will satisfy everyone. Don’t follow too much and evaluate user requests well I tried it on my skin, users may never be satisfied! An example, Send To Kindle in the first period only used to send articles. All the negative reviews said: the app doesn’t send documents/files. So I implemented the sending of documents which cost me time and money. Now, what do you think users say? Some are satisfied but many others always put a star and say: it doesn’t send files larger than 25 mb. In your opinion, if I ever increase this limit what will they say? Do many frequent releases Even if one day everything goes well, always think that the next day everything will be st** Pay attention to the answers given to customers Always be updated with the moves of your competitors Continue to update and innovate Final considerations I am aware of the fact that Send To Kindle is not an unusual invention or an innovation. In fact, I did not invent something completely new, I improved something that was already there with new technologies and I made it more efficient. I think my strong point is that I made the mobile service accessible. Sometimes it takes little to be successful, you just need to be better than your competitors. Certainly, a lot of effort and a lot of work are needed to do it. Congratulations if you’ve read this far!!! if you haven’t done it yet, download Send To Kindle, and send this article or other articles to you kindle! Have a better reading and relaxing experience!
Sei pronto per iniziare una nuova esperienza di lettura? Finalmente puoi dire addio alle scomode letture notturne da smartphone. Leggere articoli lunghi su uno schermo di uno smartphone può essere scomodo, specialmente se l’articolo è molto lungo e sono presenti molti annunci. Leggere su un Kindle è molto più semplice per gli occhi e si può avere anche un maggiore controllo sulla dimensione e sul carattere usato. Trova il tuo indirizzo Kindle con 3 passaggi 1. Recupera la email del tuo kindle, puoi trovarlo accedendo al seguente link: https://www.amazon.it/hz/mycd/myx#/home/settings/ ( assicurati che il .it sia l’Amazon utilizzato nel tuo paese, quella in cui hai associato il tuo kindle) 2. Scorri verso il basso fino alle impostazioni del documento personale e fai clic su di esso. Modifica l’indirizzo del kindle che ti servirà per accedere all’app Invia a Kindle. 3. Scorri verso il basso, nella sezione mail approvate, aggiungi l’indirizzo e-mail che usi tutti i giorni o quello da cui desideri inviare i documenti, verifica che l’archiviazione sia abilitata. Adesso rilassati e divertiti a inviare documenti con Send to Kindle. Send to Kindle La migliore app per farlo è Send To Kindle. Questa app non è la stessa di Invia a Kindle fornita da Amazon. Invia a Kindle ti consente di: inviare articoli Web al tuo Kindle tramite la funzione Condividi sul tuo browser Web mobile. Invia documento pdf / epub / azw convertendolo in mobi. Articolo Web Invia a Kindle Scarica l’app Invia a Kindle dal Play Store Send To Kindle. Individua l’articolo che desideri inviare al tuo Kindle su qualsiasi browser o app di notizie. Apri le impostazioni del tuo browser e cerca tre punti nell’angolo in alto a destra. Fai clic su Condividi. Scegli Send To Kindle come app di condivisione. Attendi che l’app converta il tuo articolo nel formato corretto. Fare clic sul pulsante Invia con la freccia gialla. Se è la prima volta che usi l’app, dovrai impostare il tuo indirizzo e-mail Kindle. Tocca il messaggio per farlo. Digita la tua e-mail Kindle e premi Salva. Scegli il tuo client di posta elettronica. Invia l’e-mail. Controlla il tuo Kindle per l’articolo. Invia a Kindle PDF, EPUB o AZW Fai clic sull’icona più nella barra di ricerca Seleziona il file pdf da inviare Fai clic su Convert Invia la mail utilizzando il tuo client di posta preferito
Are you ready to start a new better reading experience? You can finally say goodbye to the uncomfortable night readings from your smartphone. Reading long articles on a mobile screen can be difficult, especially if the site has lots ads. Reading on a Kindle is much easier on the eyes, and you have more control over the size and the font you use. Find Your Kindle Address with 3 step configuration 1. Recover the email of your kindle, you can find it by accessing to the following link: https://www.amazon.com/hz/mycd/myx#/home/settings/ (make sure that the .com is the Amazon used in your country, the one where you have associated your kindle) 2. Scroll down to personal document settings and click on it. Edit the kindle address that you need in order to enter the Send to Kindle app. 3. Scroll down, in the section approved mails, add the e-mail address you use every day or the one you want to send the documents from, please verify that archiving is enabled. Now, relax and enjoy sending documents from Send to Kindle. Send to Kindle The best app for doing so is Send to Kindle app. This app is not the same as the Send to Kindle provided by Amazon. Send to Kindle allows you: send web articles to your Kindle by Share feature on your mobile web browser. Send pdf/epub/azw document by converting it to mobi. Send to Kindle Web Article Download Send to Kindle app from the Play Store Send to Kindle. Locate the article you want to send to your Kindle on any browser or news app. Open the settings for your browser and look for three dots in the top-right corner. Click Share. Choose Send to Kindle as your sharing app. Wait for the app to convert your article to the correct format. Click the yellow-arrow Send button. If this is the first time you have used the app, you will need to set up your Kindle email address. Tap the message to do so. Type in your Kindle email and press Save. Choose your email client. Send the email. Check your Kindle for the article. Send to Kindle per PDF, EPUB o AZW Click on plus icon in search bar Select your pdf file Click convert Send the mail
Si cominciava ad intravedere qualche raggio di sole, che splendeva sui ciottoli bianchi della spiaggia. I piccoli vetrini creavano un riflesso ancora più forte, quasi abbagliante. In lontananza si intravedevano delle barchette che dondolavano in mare. Quella mattina l’acqua era molto calma, piano piano le piccole barchette si avvicinavano verso la riva e divennero pescherecci di media grandezza. Sul lungomare, una coda di lape (moto ape) in attesa. Gino sta ancora dormendo, ieri ha fatto consegne fino a tardi. Oggi u mari ie chinu i pisci! 1 si sente urlare in lontananza. La prima barchetta di un anziano pescatore attracca, un pescivendolo si avvicina e con il suo garzone comincia a scaricare cassette piene di pesce. Le trasportano sulla loro ape, di colore giallo sbiadito, ne aveva fatta di strada e ne aveva preso di sole quell’ape. Riempiono il cassone con le cassette piene di pesce fresco e ci misero sopra pezzi di ghiaccio per mantenerlo. Chi bellu pisci, pisci friscu!! pisci spada, sciabulata, aguglia, curstadelle io iaiu! 2 sentì in lontananza Gino, stava ancora dormendo e voleva tanto continuare a dormire ma oggi era martedì e come tutti i martedì puntuale passava Francu u pisciaru. Mu fai un chilu i curstadelle!! 3 sentì Gino urlare dalla casa accanto. il pescivendolo si ferma, scende e si avvicina alla bilancia, bilancia bellissima, enorme, con due piatti e un bilanciere dorato. Mette due pesi da mezzo chilo sopra un piatto della bilancia, sull’altro, mette un foglio e ci butta sopra a pugni le costardelle fintanto che i due vassoi non siano in equilibrio. Signura c’ha regalu! sulu sta junnata 5 euru o chilo! 4 Sempre urlando, in modo che anche le massaie vicine potessero sentire. Un cestino di vimini (u panaru) cala giù con una carrucola dal balcone. Il garzone prende i soldi dal cestino e ci mette le corstadelle, ci aggiunge due limoni e un pò di prezzemolo. Fortunatamente per Gino, quella mattina nessun altro tra i suoi vicini rispose all’offerta di Francu. Allora sentì il forte rumore dell’ape che piano, piano si allontanava. Il rumore stava per terminare e finalmente Gino avrebbe potuto riprendere a dormire. Banane, mulinciane, pumadoru, kiwi io iaio. Culi voli l’ovaaa! 5 il sonno di Gino fu presto interrotto da Pippo u viduraru. Viniti,viniti,accattati,accattati, tutta roba frisca iè! 6 Alla fine Gino come tutti i martedì, decise di alzarsi, anche se ancora presto e ancora assonnato, spalancò la finestra, un forte sole e tanto calore illuminò la sua giornata. Audio Siciliani Oggi u mari ie chinu i pisci! Your browser does not support the audio element. ↩︎ Chi bellu pisci, pisci friscu!! pisci spada, sciabulata, aguglia, curstadelle io iaiu! Your browser does not support the audio element. ↩︎ Mu fai un chilu i curstadelle!! Your browser does not support the audio element. ↩︎ Signura c’ha regalu! sulu sta junnata 5 euru o chilo! Your browser does not support the audio element. ↩︎ Banane, mulinciane, pumadoru, kiwi io iaio. Culi voli l’ovaaa! Your browser does not support the audio element. ↩︎ Viniti,viniti,accattati,accattati, tutta roba frisca iè! Your browser does not support the audio element. ↩︎
Erano le 4.30 e Pierluigi aveva ancora gli occhi chiusi. Era sommerso in un sogno profondo. il sogno di un gioco, guardie e ladri. Pierluigi, era alla ricerca di un posto posizionato in alto, un luogo dove avrebbe potuto godere di una buona visione con multiple vie di fuga. Scelse la terrazza di Floride, una terrazza enorme che offriva una vista sulla piazza centrale del paese. Da lì poteva vedere tutta la piazza e seguire le guardie che avevano appena finito di contare e stavano per andare alla ricerca dei ladri. Un sobbalzo lo svegliò aprì leggermente gli occhi e vide a bettula 1 del nonno. L’aveva usata come cuscino e gli aveva biascicato addosso mentre dormiva. Il sobbalzo era stato causato da Stella la cavalla del nonno che si stava destreggiando nella dura salita che portava alla campagna. Cooooocoocode sentì, un colpo di ali e la bettola iniziò a muoversi. Pierluigi, aveva sonno e non si fece domande sul perché la bettola si muoveva ed emetteva versi di gallina, chiuse gli occhi e riprese a dormire. Appena arrivarono in campagna, si fermarono di fronte all’orto. Scesero dal cavallo, il nonno disse Sta Iddina non è chiu bona a nenti. Non fa qiu ovae non è bona mancu più fari u brodu, c’amo a tagghiari a testa. 2 allora il nonno si girò verso Pierluigi e gli diede un coltello dal manico di legno molto affilato, l’aveva affilato la mattina con tanta cura. Pigghia stu cuteddu, cià fari nun tagghiu nte cannarozzi. Te ca. 3 Pierluigi aveva solo 9 anni era ingenuo e perplesso non capiva il perché di questa azione e non si spiegava perché doveva essere proprio lui a farla. Era molto impaurito dalla gallina. Il nonno la prese tenendola per le ali e tenendogli la testa e gli disse fozza tagghia! allora Pierluigi si avvicinò e avvicinò il coltello alla gola della galliana, la sfiorò, non appena vide il sangue, rabbrividì e si fermò. Il nonno cercò di riprendere il coltello per terminare la gallina. Ad un tratto la gallina gli sfuggì di mano e cominciò ad emettere versi smorzati. Si mise a correre sguazzando sangue di qua e di la. Il sangue schizzò su Pierluigi e sul nonno e sul prato, Sembrava una scena splatter come nei film di Tarantino. Cercarono di acchiappare la gallina ma gli sfuggì, dopo un pò la gallina si stancò e cominciò a barcollare qua e la. Il nonno in un balzo l’afferrò e con un colpo secco gli recise la gola, mentre lo fece, si sentì un ultimo stridulo coccodè che mise fine alle sofferenze della povera gallina. Allora la prese e la appese ad un palo vicino all’orto e disse ora voggiu a vidiri si tornunu staceddi da mala sorti! 4 tutta questa crudeltà per far scappare gli uccellini che in quei giorni mangiavano il raccolto. Il nonno aveva fatto uno spaventapasseri ma non era stato sufficiente. Ci teneva molto alla sua campagna aveva fatto tanti sacrifici e avrebbe fatto qualsiasi cosa per raccoglierne i frutti. Pierluigi era un po scandalizzato dalla scena a cui aveva assistito. Un attimo dopo, si mise al lavoro, doveva mbvirare 5, spostando la suca di rasola a rasola. Mise la suca sotto un grande albero di pesche e si sedette ad aspettare. Ad un tratto, gli calò la palpebra e si trovò di fronte le guardie che stavano salendo dalle scale principali. Allora Pierluigi, scappò verso l’altra uscita della terrazza ma un’altra guardia arrivò velocemente su dalle stesse scale. Una guardia disse ormai sei un trappola, arrenditi. Pierluigi, ci teneva a non essere catturato e avrebbe fatto di tutto, allora si sporse dalla terrazza mentre i due si avvicinavano. Fu lì che capì che poteva farcela, col cuore in gola e la guardia ad un soffio, flush si tuffó dalla terrazza. Durante il volo emise un gemito di follia, ammortizzó la caduta facendo delle capriole sul prato morbido che lo aiutò ad attenuare la caduta. Si chiuse sì tanto in se stesso che successe una cosa insolita, emesse una flatulenza e subito dopo sentì qualcosa che spinse nelle mutande proprio in prossimità del sedere. Non era paura ma causato dalla contrazione che fece il suo corpo. Era piccolo e non sentì alcun dolore, si alzò e continuò a correre fino a casa verso il bagno per finire ciò che era cominciato. Ad un tratto mentre si lavó le mani, sentì l’acqua, la sentì fino ai piedi si svegliò e vide una pozza enorme, si era scordato di spostare la suca e il pesco era pieno colmo d’acqua. Allora prese la suca e di corsa la sposto nella rasula sotto. Dopo un po' arrivo il nonno, Pierluigi era stanco e aveva fame chiese al nonno che ore sono? Il nonno alzò lo sguardo e guardò il sole e disse iè menzionnu ora ni calamu e annamu a manciari. Iai fami? Te docu manciati na pessica. 6 Pierluigi accenno un sospiro di sollievo, domandava spesso che ore erano al nonno, era affascinato da come sapesse sempre l’ora esatta senza l’uso dell’orologio. Il nonno preparò con le foglie dei fichi d’india ei limoni marci u pastrozzu 7 un secchio di mangime che diede alle pecore, andavano pazze per u pastrozzu. Il nonno prese il cavallo e si incamminarono verso casa. Mentre scendevano Pierluigi guardò la campagna ed in lontananza vide la gallina che stava lì, illuminata dal sole, riusciva a vedere chiazze di sangue rosso, qualche goccia che colava piano piano giù. Si sentiva un po' in colpa per la gallina, ma il pensiero svanì un attimo dopo arrivati a casa di fronte a una gran tavola bandita con tutta la famiglia lì pronti a pranzare. Bértula o bisaccia è un manufatto tessile comunemente diffuso su tutto il territorio regionale. Il suo uso ha origini antichissime ed è fortemente legata alla vita quotidiana. In ambito contadino, viene utilizzato per trasportare cibo come frutta, verdura o latte e uova. Oppure anche per utensili. ↩︎ Questa gallina non produce più niente. Non fa più le uova e non è più buona neanche per fare il brodo, dobbiamo tagliarle la testa. ↩︎ Prendi questo coltello e coltello, devi fare un taglio in gola. Tieni ↩︎ Ora voglio vedere se tornano ancora questi uccelli che portano solo guai ↩︎ Mbvirare annaffiare le piante, utilizzato sia con l’utilizzo della suca (canna, pompa) oppure anche tramite canalette a scorrimento d’acqua. ↩︎ E’ mezzogiorno, tra poco torniamo a casa e andiamo a mangiare. Hai Fame? Tieni mangiati una pesca. ↩︎ U pastrozzu composto di limoni e aranci, lasciati sotto un telone e sotto al sole, rimangono lì per giorni, settimane a macerare, in modo da comporre questa melma da un odore molto distinguibile e forte. Viene utilizzato come mangime per gli animali, ha ottimi valori nutrizionali. ↩︎
In questi giorni ho voluto sperimentare e approfondire le conoscenze in ambito di chatbot, ho fatto questa scelta perchè, in questo momento, questa tecnologia è molto utilizzata e molto in crescita. Un pò di teoria I chatbot esistono da tempo, davvero da tanto tempo, risalgono ai primi anni 60/70 con ELIZA e PARRY. Questi due progetti sono nati con lo scopo di creare una conversazione simulata con una macchina. In molti casi, questi due chatbot sono riusciti ad ingannare le persono coinvolte, utilizzando però risposte molto vaghe. Tuttavia, questi due progetti, nonostante fossero classificati come chatbot, risultavano essere molto stupidi, difatto non hanno la stessa concezione del chatbot odierno. Il software che c’era dietro ai vecchi chatbot era molto semplice, possiamo vederlo come una mappatura uno a uno ( domanda -> risposta). Non era presente alcuna interpretazione della domanda. La maggior parte dei Chatbot moderni invece utilizzano il linguaggio naturale che possiamo immaginare con la composizione di tre elementi principali: analisi lessicale (scomposizione frase in token) analisi grammaticale (associazione parti del discorso) analisi sintattica (arrangiamento token in una struttura ad albero) analisi semantica (assegnazione di un significato) Così descritto sembra semplice e noi umani siamo in grado di farlo senza sapere che esistono tutti questi step. Insegnare ad una macchina l’elaborazione degli step sopra elencati è davvero difficile. In particolar modo l’ultimo punto dell’analisi semantica. Oggigiorno I Chatbot più comuni che utilizziamo senza quasi rendercene conto sono Ok Google, Alexa, Siri, Cortona e ogni giorno ne sono presenti sempre di più e di nuovi. Gli ambiti in crescita dove vengono maggiormente utilizzati sono banche, servizio clienti e e-commerce. Ma perchè i chatbot si chiamano con nomi propri di persona? Non ho trovato questa risposta online però credo che la risposta possa essere semplice! Dovendo interagire con qualcosa dalle sembianze umane, quale potrebbe essere il miglior modo per interagire se non quello di attribuire un nome con cui possiamo chiamarlo? Quasi tutte le interazioni con altre persone iniziano con il nome proprio della persona, serve per richiedere la sua attenzione. Cosa c’è dietro al cofano? Ma cosa c’è dietro questi Chatbot? Nel caso di Ok Google, il servizio nelle retroscene è proprio DialogFlow, Questo servizio messo a disposizione di tutti, permette a tutti in modo agile di creare un un vero e proprio chatbot in pochi step e senza alcuna conoscenza di programmazione. Il concetto è molto facile, si basa sulla creazione di Intenti e risposte. Vi spiegherò in questo articolo come utilizzare DialogFlow e come implementarlo usando Flutter. Perchè Flutter? Perchè credo sia molto in crescita al momento. Il progetto sta crescendo così tanto da essere compatibile con tante piattaforme. In particolar modo funziona in maniera nativa su Android e IOS e funziona davvero alla grande. Secondo i test fatti, ha delle prestazioni quasi pari ai linguaggi nativi (Java/Swift/c++). Nonostante sia ancora in Beta, la funzione in modalità web va alla grande e funziona anche per ambienti desktop (linux e Windows). Ma cosa manca? ah già! Tv e Smartwatch! credo però che arriverà a breve anche li il supporto, se ne comincia a parlare online! Mi fai vedere il codice? Non voglio entrare nel dettaglio della configurazione del progetto di dialog flow in quanto ci sono davvero tanti tutorial ben fatti. Do per scontato che abbiate installato Flutter. Se non l’avete ancora fatto, potete partire da questa guida. Dopo aver installato Flutter, visto che la sua concezione è Mobile, se volete compilare anche per web dobbiamo eseguire i seguenti comandi: //abilito la verisone web di flutter flutter channel master flutter upgrade flutter config --enable-web //creo e faccio il run di un nuovo progetto cd into project directory flutter create . flutter run -d chrome Una volta abilitata la versione web di flutter e creato il progetto, possiamo procedere con la modifica del file pubspec.yaml. Questo file contiene le configurazioni e dipendenze ai plugin che utilizziamo nel nostro progetto. Dobbiamo quindi aggiungere le dipendenze a DialogFlow, la versione compatibile sia per web che per mobile è la seguente: flutter_dialogflow: ^0.1.3 assets: - assets/yourapifile.json Dobbiamo anche aggiungere negli asset il file json scaricato da google cloud relativo al service account che ha accesso al progetto di DialogFlow. l’implementazione di dialog Flow richiede davvero poco, giusto qualche riga di codice per l’inizializzazione: dialogFlow.AuthGoogle authGoogle = await dialogFlow.AuthGoogle(fileJson:"assets/yourapifile.json").build(); dialogflow = dialogFlow.Dialogflow(authGoogle: authGoogle); Infine, chiediamo a DialogFlow l’elaborazione dell’intent in modo da ricevere una risposta, con la seguente riga di codice: dialogFlow.AIResponse response = await dialogflow.detectIntent(query); print(response.getMessage()); Potete trovare l’intera implementazione funzionante al seguente link Github. Per poterlo utilizzare, dovrete solo aggiungere la vostra chiave Json del vostro account di servizio, che dovrete creare su google cloud e associare a DialogFlow. schermata del risultato finale:
In these days, I wanted to experiment and deepen my knowledge in the field of chatbot, I made this choice because at this moment, this technology is widely used and really growing. A little theory Chatbots have been around for a long time, really for a long time, they date back to the early 60s70s with ELIZA and PARRY. These two projects were born for the purpose of creating a simulated conversation with a machine. In many cases, these two chatbots have managed to deceive the people involved, but using very vague but intelligent answers. These two projects, however, despite being classified as chatbots, were very stupid, they do not have the same conception of today’s chatbot. The software behind the old chatbots was very simple, we can see it as a one-to-one mapping (question -> answer). There was no interpretation of the question. Most modern Chatbots, on the other hand, use natural language that we can imagine with the composition of three main elements: lexical analysis (decomposition of sentence into token) grammatical analysis (association of parts of speech) syntactic analysis (token arrangement in a tree structure) analysis semantics (assigning a meaning) Thus described it seems simple and we as humans are able to do it without knowing the existence of all these steps. Teaching a machine how to process the steps listed above is really difficult. Especially the last point of the semantic analysis. Nowadays the most common Chatbots that we use without realizing it are Ok Google, Alexa, Siri, Cortona and every day there are more and more new ones. The growing areas where they are most used are banks, customer service and e-commerce. But why are chatbots called by personal names? I have not found this answer online but I believe the answer is simple. As we have to interact with something with human features, what could be the best way to do it, if not to give a name with which we can call it? Almost all interactions with other people begin with the person’s first name, it is needed to request his/her attention. What’s behind the hood? But what’s behind these Chatbots? In the case of Ok Google, the service in the background is just DialogFlow, This service made available to everyone, allows everyone in an agile way to create a real chatbot in a few steps and without any programming knowledge, the concept is very easy, it is basically based on the creation of intents and answers. I will explain in this article how to use DialogFlow and how to implement it using Flutter. Why Flutter? Because I think it is really growing at the moment. The project is growing so much that it is compatible with many platforms. In particular, it works natively on Android and IOS and works really well, according to the tests made, it has performances almost equal to the native languages (Java / Swift / c ++). Although still in Beta, the Web function is going great, and it also works for desktop environments (linux and Windows). What is missing? Just TV and Smartwatch, I believe that the support will arrive shortly! Can you show me the code? I don’t want to go in detail in DialogFlow configuration, there are really many good tutorials that explain the setup and how to use it. I assume that you have Flutter installed. If you haven’t, you can start from this guide. After installing Flutter, since its conception is Mobile, if you want to compile also for web you must execute the following commands: //enable web flutter versione flutter channel master flutter upgrade flutter config --enable-web //new web project and run in chrome cd into project directory flutter create . flutter run -d chrome Once the web version of flutter has been enabled and the project has been created, we can proceed by update the pubspec.yaml file. This file contains the configurations and dependencies to the plugins that we use in our project. We must therefore add the dependencies to DialogFlow, the compatible version for both web and mobile is as follows: flutter_dialogflow: ^0.1.3 assets: - assets/yourapifile.json We must also add in the assets the json file downloaded from google cloud relating to the service account that has access to the DialogFlow project. The implementation of dialog Flow requires very little, just a few lines of initialization code: dialogFlow.AuthGoogle authGoogle = await dialogFlow.AuthGoogle(fileJson:"assets/yourapifile.json").build(); dialogflow = dialogFlow.Dialogflow(authGoogle: authGoogle); Finally we have to ask DialogFlow to process the intent in order to receive a response, do this with the following line of code: dialogFlow.AIResponse response = await dialogflow.detectIntent(query); print(response.getMessage()); The whole working implementation can be found at the following link Github. In order to use it, you only need to add your service account Json key, which you will have to create on Google Cloud and associate it with DialogFlow. final result screen:
There are many ways to secure our functions. This topic is not clear when you start using the Cloud Function and it is also constantly changing. I decided to implement my own method that uses the basic and simplest authentication, the Basic Auth. It certainly is not the safest method but it was enough for my purpose. The only flaw is that the function has to be added and implemented in every Cloud function that we make.This implementation then gives us the possibility to call our function from anywhere we want to by simply adding as a parameter of the request the user and the password encrypted in the Basic Auth. To simplify the verification, I created a function that I place in each Cloud Function. This function does the parsing of the request data, verifies if there is a basic auth and, if present, it checks whether the user and password data match. Here is the code I used: var compare = require('tsscmp') const checkAuth = async() => { // parse login and password from headers const b64auth = (req.headers.authorization || '').split(' ')[1] || '' const strauth = new Buffer(b64auth, 'base64').toString() const splitIndex = strauth.indexOf(':') const login = strauth.substring(0, splitIndex) const password = strauth.substring(splitIndex + 1) //function to validate credentials using https://www.npmjs.com/package/tsscmp //Prevents timing attacks using Brad Hill's Double HMAC pattern to perform secure string comparison function check (name, pass) { var valid = true // Simple method to prevent short-circut and use timing-safe compare valid = compare(name, 'user') && valid valid = compare(pass,'password') && valid return valid } if (!check(login, password)) { res.statusCode = 401 res.setHeader('WWW-Authenticate', 'Basic realm="example"') res.end('Access denied') } } Then we can use the function within the method called by our Cloud Function in the following way: exports.helloWorld = async (req, res) => { await checkAuth(); let message = req.query.message || req.body.message || 'Hello World!'; res.status(200).send(message); }; And that’s it, our functions are now safe! Not much but it’s a starting point. So in the end if our system is breached,it is not a big problem, in this function we only return Hello World! How do we instead call our function with Flutter? We simply have to create the Basic Auth with user and password and add it to the request as follows: var basicAuth = 'Basic '+base64Encode(utf8.encode(user:password')); http.Response response = await http.get(apiUrl, headers: <String, String>{'authorization': basicAuth}); print(response.statusCode); //200 print(response.body ); //Hello world! This method is not one of the best, however, it is very fast to implement and also gives us an advantage because it can be used in the same way from all Cloud providers. Finally, we can recall it from anywhere using any language, and it does not force us to use json keys that could be displayed. The only problem I had was having the username and password inside the code that I didn’t really like. I solved it by putting these parameters within the firebase configurations. If you are interested in this, you can read this article Flutter 2 indispensable plugins with great potential.
Ci sono tanti modi per poter mettere al sicuro le funzioni. Questo argomento non è chiarissimo dal primo momento in cui si utilizzano le funzioni in Cloud ed è inoltre in continuo cambiamento. Io ho deciso di implementare un mio metodo che utilizza la blasonata e più semplice autenticazione: la Basic Auth. Sicuramente non è la più sicura ma bastava al mio scopo. Unica pecca è che la funzione deve essere aggiunta e implementata in ogni funzione Cloud che andiamo a creare. In questo modo, possiamo chiamare la funzione implementata da dove vogliamo semplicemente aggiungendo come parametro della request user e password criptati nella Basic Auth. Per semplificare la verifica, ho creato, una funzione che inserisco all’interno di ogni Cloud Function. Questa funzione si occupa di fare il parsing dei dati della request, verificare se presente una basic auth e se presente verifica se i dati di user e password corrispondono. Di seguito il codice utilizzato: var compare = require('tsscmp') const checkAuth = async() => { // parse login and password from headers const b64auth = (req.headers.authorization || '').split(' ')[1] || '' const strauth = new Buffer(b64auth, 'base64').toString() const splitIndex = strauth.indexOf(':') const login = strauth.substring(0, splitIndex) const password = strauth.substring(splitIndex + 1) //function to validate credentials using https://www.npmjs.com/package/tsscmp //Prevents timing attacks using Brad Hill's Double HMAC pattern to perform secure string comparison function check (name, pass) { var valid = true // Simple method to prevent short-circut and use timing-safe compare valid = compare(name, 'user') && valid valid = compare(pass,'password') && valid return valid } if (!check(login, password)) { res.statusCode = 401 res.setHeader('WWW-Authenticate', 'Basic realm="example"') res.end('Access denied') } } Successivamente possiamo utilizzare la funzione all’interno del metodo chiamato della nostra Cloud Function nel seguente modo: exports.helloWorld = async (req, res) => { await checkAuth(); let message = req.query.message || req.body.message || 'Hello World!'; res.status(200).send(message); }; Ed il gioco è fatto, le nostre funzioni adesso sono al sicuro. Mica tanto! ma è un inizio. Tanto alla fine se dovessero superare la nostra autenticazione, non sarebbe un grosso problema. In questa funzione restituiamo solo Hello World! Come facciamo invece a richiamare la nostra funzione con Flutter? Dobbiamo semplicemente creare la Basic Auth con user e password e aggiungerla alla request come segue: var basicAuth = 'Basic '+base64Encode(utf8.encode(user:password')); http.Response response = await http.get(apiUrl, headers: <String, String>{'authorization': basicAuth}); print(response.statusCode); //200 print(response.body ); //Hello world! Questo metodo non è uno dei migliori però è molto rapido implementarlo e inoltre ci dà un vantaggio perché può essere utilizzato allo stesso modo per tutti i provider cloud. Infine, possiamo richiamarlo dappertutto utilizzando qualsiasi linguaggio, e non costringe a doversi portare dietro chiavi json che potrebbero essere esposte. L’unico problema che ho avuto è stato quello di avere nel codice scolpiti user e password che non trovavo gradevoli. Ho risolto questo problema mettendo questi parametri all’interno delle configurazioni di firebase. Se siete interessati potete leggere questo articolo Flutter 2 plugin indispensabili dalle grandi potenzialità
Gino apri gli occhi, quella mattina si svegliò un po’ turbato di ciò che era avvenuto la sera prima ma soprattutto con un brutto pensiero, forse aveva fatto un incubo. Sollevò lo sguardo, guardò l’orologio e la sua faccia si scurì immediatamente. In un attimo aveva realizzato che era giovedì e doveva fare una commissione. Non aveva la minima voglia di fare questa commissione e rabbrividì al solo pensiero. Si girò d’altro lato sperando di addormentarsi nuovamente e di svegliarsi il giorno dopo. Chiuse a malapena una palpebra quando sentì una voce dal profondo e non era un incubo, purtroppo, era sua mamma che urlava: Gino svigghiati chi iè taddu. Ti ricoddi chi m’ha spicciari da cosa a posta? U sai chi io non ci pozz’ annari! 1 Quindi, era tutto vero. Sua mamma aveva bisogno di lui, ma la commissione alla posta Gino non l’aveva mai mandata giù, la faceva controvoglia e lo metteva di malumore. Allora risponde alla mamma: mamma fammi dommiri, fammi dommiri autri 10 minuti! Poi mi siddiu e sugnu stancu e u sai che na suppottu Naiu Valia! 2 la mamma gli urlò subito contro insistentemente fin tanto che Gino decise di alzarsi. Scese le scale con i capelli e la faccia stropicciata, prese il caffè e lì accanto trovò le carte, carte che doveva portare alla posta. Le guardò, poi si mise a giocare con la tazzina di caffè mettendola vicino agli occhi in modo da fare sparire le carte dal suo sguardo. Alla fine si rassegnò, si alzò, prese le carte e uscì. Attraversò il vialetto e si ritrovò di fronte alla piazza del paese. In piazza a distanza vide due donne, già da lontano riconobbe Cammela a cuttighiara 3 che parlava con una certa veemenza che la caratterizzava. Stava raccontando alle vicine la disavventura proprio di Gino del giorno prima “Scinni Papà”. Carmela non appena lo vide arrivare, cambiò subito discorso, tanto sapeva tutto di tutti e aveva tanti cuttigghi a disposizione da raccontare. Gino si sentì un po’ osservato mentre passava e percepì in sottofondo un po’ di sghignazzi. Una volta in piazza, vide Don Gianni che stava lì seduto ad aspettare che il tempo passasse. Don Gianni salutò Gino con molto rispetto e un bel sorriso affettuoso, erano molto amici i due. Gino ricambiò. Poco più avanti Ciccio invece gli disse: Ma tò patri a unni u lassasti? Aoggi nun scinniu? 4 chiaramente una provocazione, una presa in giro poco velata da parte di Ciccio. A quel punto Gino capì che si riferiva al giorno precedente e allora rispose a tono: Ciccio Birra ma picchì non ti bivi na birra e ti cucchi? 5 Proseguì però senza fermarsi, non voleva problemi quella mattina, già ne aveva tanti, la sua missione principale era la commissione in posta. Superata la piazza, si ritrova di fronte alla posta. Già in lontananza poteva sentire la puzza di sigaretta misto profumo d’arancia e di cocco di Naiu Valia e già gli veniva un senso di vomito. Non appena arrivò all’ingresso la vide. Naiu Valia era lì, sicca sicca, con i capelli scuri, la faccia scura, vestita di nero, come sempre, a non fare nulla. Aveva quello sguardo rilassato ma anche minaccioso e incavolato. Allora Gino entra in silenzio e lei subito a tono: cu non mori si rividi! Ma cu ti porta? C’ha fari? 6 Gino rispose dicendo che doveva fare due raccomandate, prendere un pacchetto…. non lo fece neanche finire di parlare che rispose Ma chi ti sbigghiasti ora? tutto oggi a fari? iè tardu, su i 10, no sai chi io a fari a pausa? Si voi mi poi spettari ca, poi quannu tornu videmu chi putemu fari. 7 Gino non aveva voglia di problemi quella mattina allora rispose che andava bene e si sedette. Aspettò lì seduto sino alle 11 ma niente, di Naiu Valia nessuna traccia. Allora si fece una passeggiata e andò a bere un po’ d’acqua al lavatoio (posto utilizzato una volta per lavare i panni). Una volta ritornato vide due vecchiette dentro che dovevano ritirare la pensione e Naiu Valia che era appena tornata. Gino allora si avvicinò e Naiu Valia lo richiamò subito Gino ma che fai? non vedi che c’è gente. Devi aspettare il tuo turno e prendi il numerino. Mica siamo al mercato qui! allora Gino stava per innervosirsi ma sapeva che non era il caso e non poteva perchè se tornava a casa senza aver sbrigato le commissioni poi sentila la mamma. Allora disse: va bene aspetto e prese il numerino. Naiu Valia, cominciò a parlare con le vecchiette di tutto, sembrava di essere davvero al mercato. Cominciarono dal l’uncinetto per finire con le ricette di cucina siciliane. Gino non ne potè più, era stanco e si addormentò. Non appena aprì gli occhi vide che non c’era più nessuno ed era finalmente arrivato il suo turno. Allora si avvicinò al bancone, lì di fronte a Naiu Valia, lì pronto con tutte le sue carte e le sue cose da sbrigare, dice: è il mio turno adesso giusto? Naiu valia rispose: Ma beddu chi ti sbigghiasti ora! Non vidi chi ie menzionnu. Io annari a manciari. Tranquillu….rilassati i poi fari dumani! tantu ca fari? 8 Se siete arrivati a leggere fin qui avete capito perchè l’addetta della poste aveva come soprannome Naiu Valia. Questa storia ovviamente è un’ esasperazione. Non vuole essere provocatoria verso gli uffici postali né verso gli addetti postali né contro il sud o il nord, è solo una storia, prendetela così com’è e state tranquilli e rilassatevi! Traduzioni e audio: Gino svegliati che è tardi, ti ricordi che mi devi sbrigare quelle commissioni all’ufficio postale? Lo sai che io non posso andarci! Your browser does not support the audio element. ↩︎ Mamma fammi dormire! fammi dormire altri 10 minuti! Poi mi scoccio e sono stanco e lo sai che non sopporto l’addetta della posta! Your browser does not support the audio element. ↩︎ cuttighiara persona che ama sparlare di altre persone Your browser does not support the audio element. ↩︎ *Ma dove hai lasciato tuo papà? Oggi non è sceso con te? *Your browser does not support the audio element. ↩︎ Francesco detto Ciccio Birra perchè non ti bevi una birra e vai a letto? Your browser does not support the audio element. ↩︎ Chi non muore si rivede! Ma chi ti ha consigliato di venire? Che cosa devi fare? Your browser does not support the audio element. ↩︎ Ma ti sei svegliato ora? Devi fare tutto oggi? è tardi, sono le 10, non lo sai che io alle 10 devo faccio la pausa? Se vuoi puoi aspettarmi qui. Quando trono vediamo che cosa riusciamo a sbrigare. Your browser does not support the audio element. ↩︎ Ma bello, ti sei appena svegliato! Non vedi che è mezzogiorno. Io devo andare a pranzare. Stai tranquillo e rilassati, le puoi sbrigare domani le commissioni. Tanto che cosa hai da fare? Your browser does not support the audio element. ↩︎
Giuseppe te ca 1 euro, vammi a cattari na buttigghia i pommarola chi stamatina ma scuddai ma pigghiu a putia. Cu restu a cattiti chiddu chi voi, basta chi non ti catti vularie. Si proprio te na cattari, a cattiti i carruba chi su boni. 1 Questa frase sembra molto normale oggi, va be in sicilia ovviamente, ma un un tempo, non molto lontano, quando la globalizzazione, la tecnologia e commercializzazione dei prodotti non era così forte come ai giorni odierni, la frase non reggeva. Non tutti avevano la possibilità di comprare una bottiglia di pomodoro. E allora come si faceva? non si mangiava la famosissima e ricorrente pasta alla norma? Magari all’epoca non era così ricorrente era un piatto della domenica. No non se ne faceva a meno e per non farlo mancare a tavola, quasi tutti si adoperavano a fare le bottiglie in casa, in garage oppure in giardino. Giuseppe era molto ubbidiente e tornò a casa con la passata e le carrube. La passata che diede alla mamma, gli ricordò la storia di buttugghi i pummorola allora disse alla mamma, se poteva raccontarle la storia di quando lei faceva le bottiglie in casa. La mamma dapprima stentò a raccontarla, aveva già raccontato la storia delle bottiglie si tante volte che non aveva voglia, ma davanti alla faccia del figlio Giuseppe che esprimeva in uno sguardo “dai dai mamma ti prego raccontamela per favore..lo sai che mi piace tanto..” Allora la mamma non ne può fare a meno e comincia la storia. La preparazione delle bottiglie di pomodoro era una ricorrenza annuale, era una cosa bellissima e per noi tutti nella famiglia era come se fosse una festa. La notte precedente alla preparazione delle bottiglie di pomodoro, non riuscivo a pigliare sonno per l’agitazione e per la voglia di fare le bottiglie. La mattina prestissimo, prima ancora dell’alba, mi posizionai li in terrazza ad aspettare il rientro del nonno. Quel momento lo ricordo sempre, mi luccicavano gli occhi a vedere la lapa i Puddicinu arrivare carica di pomodori, tutti rossi ma rossi, rossi belli tutti luccicanti. Non so se perchè ero piccina oppure perché i pomodori erano i casa 2, riuscivo a percepire l’odore già da quella distanza. Di corsa balzavo giù per le scale urlando come una forsennata Oggi facemu i buttigghi!! Oggi facemu i buttigghi!! una volta lì davanti alle cassette, ne prendevo sempre un paio, li toccavo, il odoravo… ummm che buoni!!! Ii adoravo. Gli davo una sciacquata veloce e li mangiavo così sani sani. Nel cortile la nonna preparava tutte le bottiglie di vetro, bottiglie recuperate da innumerevoli bevute di birra. Una per una, le insaponava e metteva in ammollo nella bagnarola. Noi ragazze, le davamo una mano, le immergevamo nelle bagnarole in modo da rimuovere bene tutto il detersivo e così da lavarle bene bene. Ogni tanto di nascosto arrivavano i cugini che ci bagnavano con la pompa, noi per giocare poi bagnavamo loro. Era divertentissimo ridevamo e correvamo come i pazzi. La nonna se ci vedeva si biliava e ci urlava contro Viditi chiddu ca aviti a fari, si vi sciuppiati poi vi dugnu i supra. Oggi amu a travagghiari amo a fari i buttigghi, e si cuntinuati accusi finisci chi vi sciuppiati e namu a ricurdari a iunnata. 3 infine tutti bagnati finivamo di sciacquare le bottiglie rimaste. Lasciavamo tutte le bottiglie, tantissime bottiglie al sole tutte luccicanti a riposare e asciugare. Ma i giochi con l’acqua non finivano qui! Subito dopo si cominciava con il lavaggio delle cassette dei pomodori. buttavamo i pomodori dentro le bagnarole, li lasciavamo in ammollo per un pò e poi uno per uno li prendevamo e riponevamo dentro le cassette. La nonna e le zie nel frattempo, preparavano un gran fuocherello acceso per terra e sopra al fuoco c’era la più grande casseruola mai vista, io anche se piccola, ero molto alta, superavo il metro e venti ma ciò nonostante, non arrivavo a vedere dentro la casseruola. Per vedere dentro, dovevo salire su una scaletta. Mi piaceva guardare i pomodori mentre cuocevano, era bello tutto quei pomodori che strofinavano tra loro, tanto calore e bollore che usciva e un profumo intenso di pomodoro. I pomodori più piccoli ogni tanto saltavano in alto per il tanto bollore. Con un grande arnese, che io da piccola lo vedevo come il più grande cuppino che si è fuso ad uno scolapasta, venivano afferrati i pomodori pronti, quelli più pronti, pronti perché stavano a galla, lì pronti ad uscire. Una volta afferrati, li facevano rotolare giù per la macchina mangia pomodori. La macchina mangia pomodori, stava sotto il controllo della zia Pizzitta. Lei si occupava di mettere in moto la macchina, fermarla se bloccata e aiutava i pomodori a essere spremuti per bene. Li aiutava con un mattarello, li spingeva giù per il canale e man mano che scendevano, gli diceva: entrate entrate, mettetevi in fila e non spingete ci state tutti e a breve sarete uniti per sempre. Un bel succo rosso vivo, molto concentrato, caldo e intenso, colava giù dalla macchina e finiva in una grande pentola. Da un altra estremità della macchina, uscivano le bucce e Pizzitta, di tanto intanto si occupava di passarle nuovamente nella macchina. La postazione successiva era proprio la mia e della zia Maria. Io mi occupavo di prendere le bottiglie dalle ceste, di staccare le foglie dal grande mazzo di basilico e di mettere due foglioline all’interno di ogni bottiglia. Mi piaceva tanto questo compito perchè sentivo l’odore intenso del basilico sulle mani. La zia Maria, prendeva la bottiglia da me preparate e la metteva sotto il rubinetto, girava la valvola e fuoriusciva il caldo succo di pomodoro. Lasciava aperto il rubinetto fintanto che la bottiglia non si fosse ben riempita di succo di pomodoro. Lo zio Tano, con grande maestria e tanta attenzione, prendeva le bottiglie molto calde per via del sugo che fumava fin fuori dal collo delle bottiglie, le piazzava sotto un’altra postazione. Prendeva un tappo e lo attaccava ad una calamita di un altro macchinario, poi con decisione e fermezza girava una grossa manovella fintanto che il tappo non fosse ben attaccato alla bottiglia. Faceva una veloce verifica in modo da testare che fosse ben chiusa e dava l’ok a Giorgo che si occupava di prendere la bottiglia e riporla nella cassetta. Giorgio non era di famiglia ma era come se lo fosse. Noi lo facevamo stare li a svolgere il suo compito. Sebbene fosse un piccolo compito, lo faceva tanto contento. Adesso le bottiglie di pomodoro, dopo tutti questi step seguiti con cura sono pronte!! Giuseppe era ancora molto attento e sentendo la false e sapendo che la storia non finisce così subito ribatte dai mamma lo so che non finisce così, continua a raccontare. E allora la mamma riprende: è vero, Il bello deve ancora arrivare. Si prendevano dei grandi barili ex barili di petrolio davvero enormi sempre molto più alti di me. Lo zio o la nonna con l’aiuto di una scala, si calavano dentro a testa in giù e pian piano ci mettevano dentro le bottiglie di pomodoro, disposte una accanto all’altro non proprio attaccate ma vicine, poi sopra uno strato di panni e pezze vecchi e così via. Quando si giungeva alla fine, sopra l’ultimo strato di pezze, si mettevano delle pietre per tenere il tutto fermo. Infine si versava dentro l’acqua con una pompa che dal basso saliva fino in cima, era importante che fosse riempito tutto fino all’orlo. A questo punto, proprio sotto i grandi barili, venivano accesi altri grandi fuochi. Dopo un pò di ore, il tempo di mettere apposto tutta l’attrezzatura, si spegnevano i fuochi e si lasciavano dentro le bottiglie tutta la notte a raffreddare e a riposare. Nta notti a Nonna non pigghiava sonnu, si girava e si vutava, stava cu penseri pi buttigghi. Giuseppe u sai picchì a nonna aviva u penseri e buttighie? 4 disse la mamma. Picchi na vota, succidiu nu macello, i buttigghi non furunu chiusi pi come si devi e nta nanzi a notti, i buttighi scuppiarunu e na ranni botta si sintiu! 5 rispose subito Giuseppe a tono. Si bravo, fecero una botta fortissima e la nonna saltò in aria. Quella notte fortunatamente era andato tutto bene, nessuna bottiglia scoppiò. L’indomani mattina, tirarono fuori tutte le bottiglie una per una ed erano tutte integre, troppo belle e troppo buone. Riponemmo le bottiglie all’interno delle cassette che poi posizionammo all’interno di grandi scaffali. Che bello tutte le bottiglie per l’anno per la famiglia erano pronte. Ne prendemmo subito un paio e preparammo una pasta al pomodoro per tutti. Quella pasta, la ricordo ancora, aveva il sapore dei pomodori ma anche di tutti noi e di tutti gli sforzi fatti per produrre la passata. Dai Giuseppe è pronto, adesso mangiamoci la nostra pasta al pomodoro e immaginiamo di averla fatta con le nostre mani. Dai Giuseppe è pronto, adesso mangiamoci la nostra pasta al pomodoro e immaginiamo di averla fatta. Traduzioni: Giuseppe tieni 1 euro, vai a comprare una bottiglia di pomodoro, questa mattina me la sono scordata di comprarla alla bottega. Con il resto dei soldi, comprati tutto quello che vuoi, però non ti comprare porcherie. Se proprio te le devi comprare, compra le carrube che sono più sane. Your browser does not support the audio element. ↩︎ prodotti a kilometro 0, prodotti in campagna familiare. ↩︎ Prestate attenzione a quello che fate, se vi fate male poi io vi do altre botte. Oggi dobbiamo lavorare, dobbiamo fare le bottiglie di pomodoro, se continuate così, rischiate di farvi male e poi finisce che finiamo all’ospedale e ci ricorderemo per sempre di questa giornata sventurata. Your browser does not support the audio element. ↩︎ Durante la notte la nonna non prendeva sonno. si girava e rigirava, stava con il pensiero alle bottiglie. Giuseppe lo sai perchè la nonna aveva il pensiero alle bottiglie? Your browser does not support the audio element. ↩︎ Perché una volta è successo che non erano state chiuse bene e allora durante la notte sono scoppiate, facendo un gran fracasso Your browser does not support the audio element. ↩︎
An Happy man It was dark and quiet. A slight fishy smell was perceived on the nose. A rustle in the distance like when the sea beats on the shoreline. It was a beautiful evening. Gino, like every evening, was there, ready on his white Vespa, with a bright smile on his face and proud of himself. The wind lifts and moves its curly curls. At a certain point a man approaches, gives him a package and tells him Piazzale Dante 26. He replies, yes sir! He puts the package in the trunk, starts his Vespa with a sharp blow to the pedal. A burst and a strong discharge of gasoline comes out of the exhaust pipe. Gino breathes intensely, loves that strong smell of burnt mixed petrol with a background of the sea, makes him feel alive. He takes off on the twenty-fourth of May, and goes down fast, darting through the few cars he encounters. As he travels his way, he thinks of his children Pino and Matilde, who are about to go to bed at the moment. Then he bends over the wasp with a gentle movement, as if he were cradling his children. Arriving at the square, he stops, takes the package and heads for number 26. A beautiful family home. Drin Drin! A woman answers the intercom telling him to enter in. Gino enters, the woman asks him: Do you want something? Shall I make you a coffee? Gino replies, Thanks lady but I never drink coffee in the evening! So get a cookie I just made them! the woman reiterates. Gino takes a biscuit, they say goodbye and returns to his Vespa. As he returns, he finishes the last piece of the biscuit and thinks… Ho my god how good are these biscuits! and smiles. A busy girl At the same time, somewhere else, a little further but not too far, Alessia, a woman with all her hair shaved, a nice tattoo that comes out of her white T-shirt, is there waiting for something. There is no smell all covered by the smog of the big city. While waiting, he looks at his smartphone, answers a message, when suddenly he hears the number 16. He jumps up and suddenly turns, takes a few steps and heads towards the restaurant entrance. Come in and say Here I am! A parcel is given to him. He takes it and heads for his new electric bike. Hop on, attach the smartphone to the handlebar and start browsing which shows him where to go for package delivery. Then it rushes through the traffic, heavy traffic at that hour. It is found in the midst of machines and scooters that go beyond her and make fur continuously. During the journey she doesn’t think at anything, she can’t wait to make the last delivery, the evening ends and she can finally go home. Be careful where you go! the scream of a taxi driver. Alessia nervously replies Idiot, a little respect for cyclists! Finally at the delivery point, he reads the name on the smartphone, rings the intercom. Someone replies saying inside 6 staircase 2 third floor. Then attach her new bike to a pole, travel the way as if it were a maze. Arrived, she sees a very silent man, he was there waiting for her. Alessia delivers the package and greets him. The man returns the greeting and immediately closes the door. Alessia going out thinks that sad man…An insensible drone An insensible drone In the same instant of time, somewhere else, much further away, a drone is there, still, the wind does not scratch it, it does not move, it is also waiting. There is a metallic smell in the air. When all of a sudden a package rolls off a conveyor belt and hooks to the drone. Immediately afterwards,is heard Bip Bip followed by Sfruet Sfruet. The propellers start spinning with perfect synchrony, a slight hum is heard. The drone begins to rise from the ground with the packet attached below. Fly fast and look up at the city, and think … ha no! can not think! He goes towards something, he doesn’t know what, for him the position x. Once arrived, it descends slowly until it lands. A series of checks and a message starts from his soul depth. He is waiting. At a certain point a girl with golden ringlets approaches her, caresses him … Not exactly she puts her hand down so that the dorne recognizes her. The drone greets her cordially* Buonasera Marta* and gives her the package. The girl takes her package and runs away without saying goodbye. Poor drone, a little sad … Ha no! true can not be sad! Restart its propellers and return to base.
Un uomo felice Era buio e silenzioso. Un lieve odore di pesce si percepiva all’olfatto. Un fruscio in lontananza come quando il mare batte sulla battigia. Quella sera era proprio una bella serata. Gino, come tutte le sere si trovava lì, pronto sulla sua vespa bianca, con un sorriso smagliante in faccia e fiero di sé. Il vento solleva e smuove i suoi boccoli ricci. Ad un certo punto un uomo si avvicina, gli consegna un pacchetto e gli dice Piazzale Dante 26. Lui risponde, sì signore! Mette il pacchetto nel baule, mette in moto la sua vespa con un colpo secco alla pedalina. Uno scoppio e un forte scarico di benzina proviene fuori dal tubo di scappamento. Gino, respira intensamente, ama quel forte odore di benzina misto bruciato con sottofondo di mare, lo fa sentire vivo. Imbocca Via Ventiquattro Maggio, e va giù veloce sfrecciando in mezzo alle poche macchine che incontra. Mentra percorre il suo tragitto, pensa ai suoi bambini Pino e Matilde, che in questo momento stanno per essere messi a letto. Allora lui si piega sulla vespa con movimento soave, come se stesse cullando i suoi bambini. Arrivato alla piazza, si ferma, prende il pacchetto e si dirige verso il numero 26. Una bella casa familiare. Drin Drin! Una donna risponde al citofono dicendogli di accomodarsi. Allora Gino entra consegnandogli il pacchetto. La donna gli chiede: Vuoi qualcosa? Ti faccio un caffè? Gino risponde, Grazie signora ma non bevo mai caffè la sera! Allora prendi un biscotto, li ho appena fatti! la donna ribadisce. Gino prende un biscotto, si salutano e ritorna alla sua vespa. Mentre torna, finisce l’ultimo pezzetto del biscotto e pensa…mamma mia quanto sono buoni sti biscotti e sorride. Una donna impegnata Nello stesso istante, da qualche altra parte, un pò più lontano ma non troppo, Alessia, donna con i capelli tutti rasati, un bel tatuaggio che viene fuori dalla sua magliettina bianca, è lì ferma ad aspettare qualcosa. Non si sente alcun odore, è tutto coperto dallo smog della grande città. Mentre aspetta, guarda il suo smartphone, risponde ad un messaggio, quando ad un tratto sente chiamare il numero 16. Salta su e si gira di colpo, fa qualche passo e si dirige verso l’ingresso del ristorante. Entra e dice Eccomi! Gli viene consegnato un pacco. Lo prende e si dirige verso la sua nuova bici elettrica. Salta su, attacca lo smartphone al manubrio e avvia la navigazione che gli mostra dove andare per effettuare la consegna del pacchetto. Allora sfreccia in mezzo al traffico, traffico intenso a quell’ora. Si trova in mezzo a macchine e motorini che la superano e le fanno il pelo di continuo. Durante il tragitto non pensa a niente, non vede l’ora di fare le ultime consegna, la serata finisce e finalmente può tornare a casa. Sta attenta a dove vai! l’urlo di un tassista. Alessia risponde con nervosismo Imbecille, un pò di rispetto per i ciclisti! Finalmente arriva al punto di consegna, legge il nome sullo smartphone, suona al citofono. Qualcuno risponde dicendo interno 6 scala 2, terzo piano. Allora attacca la sua bici nuova ad un palo, percorre il tragitto come se fosse un labirinto. Arrivata, vede un uomo molto silenzioso, è lì ad attenderla. Alessia consegna il pacco e lo saluta. L’uomo ricambia il saluto e richiude subito la porta. Alessia uscendo pensa che uomo triste… Un drone insensibile Nello stesso istante di tempo, da qualche altra parte, molto più lontano, un drone è lì, fermo, il vento non lo scalfisce, non si muove, anche lui aspetta. Si sente un odore metallico nell’aria. Quando ad un tratto un pacchetto rotola giù da un nastro trasportatore e si aggancia al drone. Subito dopo, si sente Bip Bip seguito da Sfruet Sfruet. Le eliche cominciano a girare con una sincronia perfetta, si sente un lieve ronzio. Il drone comincia a sollevarsi da terra con il pacchetto sotto attaccato. Vola veloce e in alto osserva la città, e pensa… ah no! non può pensare! Si dirige verso qualcosa, non sa bene cosa, per lui la posizione x. Una volta arrivato, scende piano piano fino ad atterrare. Una serie di controlli e un messaggio parte dalla sua profondità dell’anima. Rimane in attesa. Ad un certo punto una bambina dai boccoli d’oro, si avvicina, lo accarezza…Non esattamente appoggia la mano affinché il dorne la riconosca. Il drone la saluta cordialmente Buonasera Marta e le consegna il pacchetto. La bambina prende il suo pacchetto e corre via senza salutarlo. Povero drone, un po triste…ah no! vero! non può essere triste! Riavvia le sue eliche e torna alla base.
In questi giorni ho voluto sperimentare e approfondire le conoscenze in ambito di chatbot, ho fatto questa scelta perchè, in questo momento, questa tecnologia è molto utilizzata e molto in crescita. Un pò di teoria I chatbot esistono da tempo, davvero da tanto tempo, risalgono ai primi anni 60/70 con ELIZA e PARRY. Questi due progetti sono nati con lo scopo di creare una conversazione simulata con una macchina. In molti casi, questi due chatbot sono riusciti ad ingannare le persono coinvolte, utilizzando però risposte molto vaghe. Tuttavia, questi due progetti, nonostante fossero classificati come chatbot, risultavano essere molto stupidi, difatto non hanno la stessa concezione del chatbot odierno. Il software che c’era dietro ai vecchi chatbot era molto semplice, possiamo vederlo come una mappatura uno a uno ( domanda -> risposta). Non era presente alcuna interpretazione della domanda. La maggior parte dei Chatbot moderni invece utilizzano il linguaggio naturale che possiamo immaginare con la composizione di tre elementi principali: analisi lessicale (scomposizione frase in token) analisi grammaticale (associazione parti del discorso) analisi sintattica (arrangiamento token in una struttura ad albero) analisi semantica (assegnazione di un significato) Così descritto sembra semplice e noi umani siamo in grado di farlo senza sapere che esistono tutti questi step. Insegnare ad una macchina l’elaborazione degli step sopra elencati è davvero difficile. In particolar modo l’ultimo punto dell’analisi semantica. Oggigiorno I Chatbot più comuni che utilizziamo senza quasi rendercene conto sono Ok Google, Alexa, Siri, Cortona e ogni giorno ne sono presenti sempre di più e di nuovi. Gli ambiti in crescita dove vengono maggiormente utilizzati sono banche, servizio clienti e e-commerce. Ma perchè i chatbot si chiamano con nomi propri di persona? Non ho trovato questa risposta online però credo che la risposta possa essere semplice! Dovendo interagire con qualcosa dalle sembianze umane, quale potrebbe essere il miglior modo per interagire se non quello di attribuire un nome con cui possiamo chiamarlo? Quasi tutte le interazioni con altre persone iniziano con il nome proprio della persona, serve per richiedere la sua attenzione. Cosa c’è dietro al cofano? Ma cosa c’è dietro questi Chatbot? Nel caso di Ok Google, il servizio nelle retroscene è proprio DialogFlow, Questo servizio messo a disposizione di tutti, permette a tutti in modo agile di creare un un vero e proprio chatbot in pochi step e senza alcuna conoscenza di programmazione. Il concetto è molto facile, si basa sulla creazione di Intenti e risposte. Vi spiegherò in questo articolo come utilizzare DialogFlow e come implementarlo usando Flutter. Perchè Flutter? Perchè credo sia molto in crescita al momento. Il progetto sta crescendo così tanto da essere compatibile con tante piattaforme. In particolar modo funziona in maniera nativa su Android e IOS e funziona davvero alla grande. Secondo i test fatti, ha delle prestazioni quasi pari ai linguaggi nativi (Java/Swift/c++). Nonostante sia ancora in Beta, la funzione in modalità web va alla grande e funziona anche per ambienti desktop (linux e Windows). Ma cosa manca? ah già! Tv e Smartwatch! credo però che arriverà a breve anche li il supporto, se ne comincia a parlare online! Mi fai vedere il codice? Non voglio entrare nel dettaglio della configurazione del progetto di dialog flow in quanto ci sono davvero tanti tutorial ben fatti. Do per scontato che abbiate installato Flutter. Se non l’avete ancora fatto, potete partire da questa guida. Dopo aver installato Flutter, visto che la sua concezione è Mobile, se volete compilare anche per web dobbiamo eseguire i seguenti comandi: //abilito la verisone web di flutter flutter channel master flutter upgrade flutter config --enable-web //creo e faccio il run di un nuovo progetto cd into project directory flutter create . flutter run -d chrome Una volta abilitata la versione web di flutter e creato il progetto, possiamo procedere con la modifica del file pubspec.yaml. Questo file contiene le configurazioni e dipendenze ai plugin che utilizziamo nel nostro progetto. Dobbiamo quindi aggiungere le dipendenze a DialogFlow, la versione compatibile sia per web che per mobile è la seguente: flutter_dialogflow: ^0.1.3 assets: - assets/yourapifile.json Dobbiamo anche aggiungere negli asset il file json scaricato da google cloud relativo al service account che ha accesso al progetto di DialogFlow. l’implementazione di dialog Flow richiede davvero poco, giusto qualche riga di codice per l’inizializzazione: dialogFlow.AuthGoogle authGoogle = await dialogFlow.AuthGoogle(fileJson:"assets/yourapifile.json").build(); dialogflow = dialogFlow.Dialogflow(authGoogle: authGoogle); Infine, chiediamo a DialogFlow l’elaborazione dell’intent in modo da ricevere una risposta, con la seguente riga di codice: dialogFlow.AIResponse response = await dialogflow.detectIntent(query); print(response.getMessage()); Potete trovare l’intera implementazione funzionante al seguente link Github. Per poterlo utilizzare, dovrete solo aggiungere la vostra chiave Json del vostro account di servizio, che dovrete creare su google cloud e associare a DialogFlow. schermata del risultato finale:
In these days, I wanted to experiment and deepen my knowledge in the field of chatbot, I made this choice because at this moment, this technology is widely used and really growing. A little theory Chatbots have been around for a long time, really for a long time, they date back to the early 60s70s with ELIZA and PARRY. These two projects were born for the purpose of creating a simulated conversation with a machine. In many cases, these two chatbots have managed to deceive the people involved, but using very vague but intelligent answers. These two projects, however, despite being classified as chatbots, were very stupid, they do not have the same conception of today’s chatbot. The software behind the old chatbots was very simple, we can see it as a one-to-one mapping (question -> answer). There was no interpretation of the question. Most modern Chatbots, on the other hand, use natural language that we can imagine with the composition of three main elements: lexical analysis (decomposition of sentence into token) grammatical analysis (association of parts of speech) syntactic analysis (token arrangement in a tree structure) analysis semantics (assigning a meaning) Thus described it seems simple and we as humans are able to do it without knowing the existence of all these steps. Teaching a machine how to process the steps listed above is really difficult. Especially the last point of the semantic analysis. Nowadays the most common Chatbots that we use without realizing it are Ok Google, Alexa, Siri, Cortona and every day there are more and more new ones. The growing areas where they are most used are banks, customer service and e-commerce. But why are chatbots called by personal names? I have not found this answer online but I believe the answer is simple. As we have to interact with something with human features, what could be the best way to do it, if not to give a name with which we can call it? Almost all interactions with other people begin with the person’s first name, it is needed to request his/her attention. What’s behind the hood? But what’s behind these Chatbots? In the case of Ok Google, the service in the background is just DialogFlow, This service made available to everyone, allows everyone in an agile way to create a real chatbot in a few steps and without any programming knowledge, the concept is very easy, it is basically based on the creation of intents and answers. I will explain in this article how to use DialogFlow and how to implement it using Flutter. Why Flutter? Because I think it is really growing at the moment. The project is growing so much that it is compatible with many platforms. In particular, it works natively on Android and IOS and works really well, according to the tests made, it has performances almost equal to the native languages (Java / Swift / c ++). Although still in Beta, the Web function is going great, and it also works for desktop environments (linux and Windows). What is missing? Just TV and Smartwatch, I believe that the support will arrive shortly! Can you show me the code? I don’t want to go in detail in DialogFlow configuration, there are really many good tutorials that explain the setup and how to use it. I assume that you have Flutter installed. If you haven’t, you can start from this guide. After installing Flutter, since its conception is Mobile, if you want to compile also for web you must execute the following commands: //enable web flutter versione flutter channel master flutter upgrade flutter config --enable-web //new web project and run in chrome cd into project directory flutter create . flutter run -d chrome Once the web version of flutter has been enabled and the project has been created, we can proceed by update the pubspec.yaml file. This file contains the configurations and dependencies to the plugins that we use in our project. We must therefore add the dependencies to DialogFlow, the compatible version for both web and mobile is as follows: flutter_dialogflow: ^0.1.3 assets: - assets/yourapifile.json We must also add in the assets the json file downloaded from google cloud relating to the service account that has access to the DialogFlow project. The implementation of dialog Flow requires very little, just a few lines of initialization code: dialogFlow.AuthGoogle authGoogle = await dialogFlow.AuthGoogle(fileJson:"assets/yourapifile.json").build(); dialogflow = dialogFlow.Dialogflow(authGoogle: authGoogle); Finally we have to ask DialogFlow to process the intent in order to receive a response, do this with the following line of code: dialogFlow.AIResponse response = await dialogflow.detectIntent(query); print(response.getMessage()); The whole working implementation can be found at the following link Github. In order to use it, you only need to add your service account Json key, which you will have to create on Google Cloud and associate it with DialogFlow. final result screen:
Nell’articolo shared preferences in depth, ho spiegato come utilizzare la shared preferences in Flutter. In questo articolo voglio invece mostrarvi una classe che ho ideato per semplificare l’utilizzo. Ho creato questa classe per l’esigenza di dover memorizzare i dati degli utenti. Ad esempio l’ho usata in Send to Kindle per memorizzare una lista di mail. L’ho usata anche per memorizzare la lista dei token e dati d’acquisto dell’utente. In generale questa struttura può essere utilizzata laddove serve lavorare con una lista di stringhe. Nell’articolo precedente, ho già mostrato come fare a memorizzare una lista di stringhe in local cache. Questa classe però rende l’utilizzo ancora più semplici e aggiunge dei metodi che ci permettono di lavorare in maniera più rapida. Come prima cosa, ho creato il metodo per salvare una lista il local cache nel seguente modo: class CashedUserData { static Future<void> _writes = Future.value(); //add list by key reference in local cache static Future<void> addlist(String kPrefKey, List<String> stringList) async{ _writes = _writes.then((void _) => _doAddList(kPrefKey,stringList)); return _writes; } } static Future<void> _doAddList(String kPrefKey, List<String> list) async { List<String> cached = await load(kPrefKey); cached.addAll(list); await (await SharedPreferences.getInstance()).setStringList(kPrefKey, cached); } Successivamente ho creato un altro metodo per recuperare la lista, inoltre ho messo qui i controlli del caso se empty o null: static Future<List<String>> load(String kPrefKey) async { return (await SharedPreferences.getInstance()).getStringList(kPrefKey) ?? []; } Man mano che la usavo ho avuto la necessità di dover aggiungere altri metodi. Metodi per: Cancellare la lista. Inserire una stringa per volta. Fare l’override della lista. Verificare se un elemento esiste. Prendere il primo elemento della lista. Salvare un elemento in top alla lista. In questo modo la classe alla fine è diventata così: class CashedUserData { static Future<void> _writes = Future.value(); static Future<void> overrideListData(String kPrefKey, List<String> stringList) async{ _writes = _writes.then((void _) => _doOverrideListData(kPrefKey,stringList)); return _writes; } static Future<void> addlist(String kPrefKey, List<String> stringList) async{ _writes = _writes.then((void _) => _doAddList(kPrefKey,stringList)); return _writes; } static Future<void> save(String kPrefKey, String id) async{ if(await CashedUserData.exist(kPrefKey, id)) return; _writes = _writes.then((void _) => _doSave(kPrefKey,id)); return _writes; } static Future<void> saveAsFirst(String kPrefKey, String id) async{ List<String> actuaList = await load(kPrefKey); if(actuaList!=null && actuaList.length == 0 ) return save(kPrefKey,id); if(actuaList.contains(id)) actuaList.remove(id); if(actuaList.length>1) actuaList.removeAt(0); List<String> newlist = new List(); newlist.add(id); newlist.addAll(actuaList); _writes = _writes.then((void _) => _doOverrideListData(kPrefKey,newlist)); return _writes; } static Future<void> delete(String kPrefKey, String id) { _writes = _writes.then((void _) => _doDelete(kPrefKey,id)); return _writes; } static Future<String> getFirst(String kPrefKey) async { List<String> dataList = (await SharedPreferences.getInstance()).getStringList(kPrefKey); return (dataList!=null && dataList.length>0) ? dataList[0] : ""; } static Future<List<String>> load(String kPrefKey) async { return (await SharedPreferences.getInstance()).getStringList(kPrefKey) ?? []; } static Future<bool> exist(String kPrefKey, String id) async { List<String> userDataList = (await SharedPreferences.getInstance()).getStringList(kPrefKey) ?? []; return (userDataList!=null && userDataList.length > 0) ? userDataList.contains(id) : false; } static Future<void> _doSave(String kPrefKey, String id) async { List<String> cached = await load(kPrefKey); cached.add(id); await (await SharedPreferences.getInstance()).setStringList(kPrefKey, cached); } static Future<void> _doDelete(String kPrefKey, String id) async { List<String> cached = await load(kPrefKey); cached.remove(id); await (await SharedPreferences.getInstance()).setStringList(kPrefKey, cached); } static Future<void> _doAddList(String kPrefKey, List<String> list) async { List<String> cached = await load(kPrefKey); cached.addAll(list); await (await SharedPreferences.getInstance()).setStringList(kPrefKey, cached); } static Future<void> _doOverrideListData(String kPrefKey, List<String> list) async { await (await SharedPreferences.getInstance()).setStringList(kPrefKey, list); } static update(String kPrefKey, String currentItem, String kindlemail) async{ List<String> actuaList = await load(kPrefKey); if(actuaList!=null && actuaList.length > 0 ) { try{actuaList[actuaList.indexOf(currentItem)] = kindlemail;} catch (e) {} } await overrideListData(kPrefKey, actuaList); } } Facendo così ho semplificato molto e reso più semplice i punti di chiamata. Inoltre se dovesse cambiare il plugin shared preference utilizzato, c’è un unico posto dove dover intervenire.
In the article shared preferences in depth, I explained how to use shared preferences in Flutter. In this article I want to show you a class that I designed to simplify its use. I created this class out of the need to store user data. For example, I used it in Send to Kindle to store a mail list. I also used it to store the user’s token list and to purchase data. Overall, this structure can be used where there is the need of working with a list of strings. In my previous article, I have already shown how to store a list of strings in local cache. This class, however, makes it even easier to use and adds methods that allow us to work faster. First, I created the method to save a local cache list as follows: class CashedUserData { static Future<void> _writes = Future.value(); //add list by key reference in local cache static Future<void> addlist(String kPrefKey, List<String> stringList) async{ _writes = _writes.then((void _) => _doAddList(kPrefKey,stringList)); return _writes; } } static Future<void> _doAddList(String kPrefKey, List<String> list) async { List<String> cached = await load(kPrefKey); cached.addAll(list); await (await SharedPreferences.getInstance()).setStringList(kPrefKey, cached); } Then I created another method to retrieve the list, I have also put the appropriate checks here, if empty or null: static Future<List<String>> load(String kPrefKey) async { return (await SharedPreferences.getInstance()).getStringList(kPrefKey) ?? []; } As I used it, I needed to add more methods. Methods to: Clear the list. Enter one string at a time Override the list Check if an element exists Take the first item on the list. Save an item at the top of the list. In this way the class eventually became like this: class CashedUserData { static Future<void> _writes = Future.value(); static Future<void> overrideListData(String kPrefKey, List<String> stringList) async{ _writes = _writes.then((void _) => _doOverrideListData(kPrefKey,stringList)); return _writes; } static Future<void> addlist(String kPrefKey, List<String> stringList) async{ _writes = _writes.then((void _) => _doAddList(kPrefKey,stringList)); return _writes; } static Future<void> save(String kPrefKey, String id) async{ if(await CashedUserData.exist(kPrefKey, id)) return; _writes = _writes.then((void _) => _doSave(kPrefKey,id)); return _writes; } static Future<void> saveAsFirst(String kPrefKey, String id) async{ List<String> actuaList = await load(kPrefKey); if(actuaList!=null && actuaList.length == 0 ) return save(kPrefKey,id); if(actuaList.contains(id)) actuaList.remove(id); if(actuaList.length>1) actuaList.removeAt(0); List<String> newlist = new List(); newlist.add(id); newlist.addAll(actuaList); _writes = _writes.then((void _) => _doOverrideListData(kPrefKey,newlist)); return _writes; } static Future<void> delete(String kPrefKey, String id) { _writes = _writes.then((void _) => _doDelete(kPrefKey,id)); return _writes; } static Future<String> getFirst(String kPrefKey) async { List<String> dataList = (await SharedPreferences.getInstance()).getStringList(kPrefKey); return (dataList!=null && dataList.length>0) ? dataList[0] : ""; } static Future<List<String>> load(String kPrefKey) async { return (await SharedPreferences.getInstance()).getStringList(kPrefKey) ?? []; } static Future<bool> exist(String kPrefKey, String id) async { List<String> userDataList = (await SharedPreferences.getInstance()).getStringList(kPrefKey) ?? []; return (userDataList!=null && userDataList.length > 0) ? userDataList.contains(id) : false; } static Future<void> _doSave(String kPrefKey, String id) async { List<String> cached = await load(kPrefKey); cached.add(id); await (await SharedPreferences.getInstance()).setStringList(kPrefKey, cached); } static Future<void> _doDelete(String kPrefKey, String id) async { List<String> cached = await load(kPrefKey); cached.remove(id); await (await SharedPreferences.getInstance()).setStringList(kPrefKey, cached); } static Future<void> _doAddList(String kPrefKey, List<String> list) async { List<String> cached = await load(kPrefKey); cached.addAll(list); await (await SharedPreferences.getInstance()).setStringList(kPrefKey, cached); } static Future<void> _doOverrideListData(String kPrefKey, List<String> list) async { await (await SharedPreferences.getInstance()).setStringList(kPrefKey, list); } static update(String kPrefKey, String currentItem, String kindlemail) async{ List<String> actuaList = await load(kPrefKey); if(actuaList!=null && actuaList.length > 0 ) { try{actuaList[actuaList.indexOf(currentItem)] = kindlemail;} catch (e) {} } await overrideListData(kPrefKey, actuaList); } } By doing this I simplified the call points. Furthermore, if the plugin shared preference used changed, there would only be one place where to take action.
Prerequisiti Questo articolo richiede una discreta conoscenza dei seguenti argomenti che potete approfondire ai seguenti link: Firebase Remote Config Flutter Shared Preferences Come mai vi parlo di questi due plugin che sembrerebbero non avere niente a che fare l’uno con l’altro? Con questa guida, vi illustrerò come trarre beneficio da questi due plugin e come risparmiare usando i servizi gratuiti offerti da Firebase! :) Nell’app Italiano con Eli, creata per il canale Italiano con Eli, ho creato un progetto su Firebase, in modo da poter utilizzare il remote config e altre potenzialità che Firebase mette a disposizione. L’app si occupa di mostrare una lista video da canale youtube, se interessati potete scaricare il codice sorgente su github, potete utilizzarlo con qualsiasi canale youtube. Per rendere l’app Italiano con Eli più originale ho pensato di aggiungere ai video delle domande sugli argomenti trattati nei video. Avevo quindi bisogno di un posto dove salvare i dati delle domande. Con Firebase, la cosa più logica e anche più diretta sarebbe quella di usare Realtime Database oppure Firestore. Io ho pensato di utilizzare remote config! Sì, avete capito bene! Remote config! Il primo pensiero che vi sta saltando in mente in questo momento sarà “questo è pazzo!” e il secondo “ma perchè?” condivido pienamente con voi il vostro primo pensiero… c’è un pò di pazzia nella scelta adottata. Per quanto riguarda il perché invece, posso rispondere come segue: non avevo molto tempo a disposizione remote config è gratis e non ha limitazione di utilizzo si può dare accesso al servizio a una persona esterna le altre scelte mi avrebbero portato a dover costruire anche un’interfaccia back end per il censimento delle domande Conoscevo già remote config e sarei stato più rapido nell’implementazione Ma come funziona un servizio non nato per questo lavoro? Funziona benissimo, devo dire che mi ha sorpreso la reattività che dimostra l’app e il sistema che ho messo in piedi. Per farlo funzionare così bene però ho dovuto mettere assieme i due plugin nel modo che vi spiego di seguito. All’interno della console di remote config vado a inserire un nuovo parametro per ogni video. Nel campo nome inserisco l’id del video di youtube e all’interno del parametro un json contenente le domande e le risposte così strutturato: { "Playlist": "PLsrqydfBIVzy9IMGwSQieTVeSWC7cRCnN", "Questions": [ { "question": "How do you say how much is it??: ", "answare": [ "Quanto costa?", "Quanto costano? ", "Quanto costi?" ] }, { "question": "How do you say how much are they?: ", "answare": [ "Quanto costano?", "Quanto costate?", "entrambe/both" ] }, { "question": "Choose the correct option", "answare": [ "Quanto costa questo computer? 300 Euro", "quanto computer ti costa? 300 Euro", "Quanto costano questo computer?" ] }, { "question": "Quanto costa? is", "answare": [ "singolare", "plurale" ] } ] } Inoltre firebase mette a disposizione un inserimento facilitato che effettua anche la validazione del json, questo ci permette di editare più facilmente e non commettere errori. Nell’app Flutter ho poi costruito l’oggetto Quiz che conterrà l’id di youtube, una lista di Domande e la Domanda conterrà una lista di Risposte. Con vari metodi utili di gestione e costruttore per deserializzazione dei dei dati Json come segue: class Answer { final String answerText; final bool isCorrect; Answer(this.answerText, this.isCorrect); } class Question { final String question; final List<Answer> answers; Question(this.question, this.answers); } class Quiz { List<Question> _questions; int _currentIndex = -1; int _score = 0; String _videoId; Quiz.fromJson(String videoId, String jsonInput){ _videoId = videoId; _questions = new List<Question>(); var jsonQuestions = json.decode(jsonInput); jsonQuestions['Questions'].forEach((quest) { List<Answer> answers = new List<Answer>(); bool iscorrect=true; quest['answare'].forEach((ans) { //mettere la prima a true answers.add( new Answer(ans, iscorrect) ); iscorrect=false; }); answers.shuffle(); _questions.add( new Question( quest['question'], answers) ); }); } Quiz(this._questions) { _questions.shuffle(); } List<Question> get questions => _questions; int get length => _questions.length; int get questionNumber => _currentIndex + 1; int get score => _score; Question get nextQuestion { _currentIndex++; if (_currentIndex >= length) return null; return _questions[_currentIndex]; } Question get prevQuestion { _currentIndex--; if (_currentIndex < 0) return null; return _questions[_currentIndex]; } String get correctAnsware { return _questions[_currentIndex].answers.where((x) => x.isCorrect == true).first.answerText; } void answer(bool isCorrect) { if (isCorrect) _score++; } saveScore() async { SharedPreferences preferences = await SharedPreferences.getInstance(); preferences.setInt("OK"+_videoId, this.score); preferences.setInt("KO"+_videoId, this.length - this.score ); //preferences.setString(_videoId, "{'questions':'"+ this.length.toString() +"', 'score':'"+ this.score.toString() +"'}"); } } Per prima cosa, l’App recupera la lista dei video tramite le API youtube, successivamente recupera il relativo json da remote config utilizzando l’id del video e lo passa al costruttore dell’oggetto che lo deserializza ed il gioco è fatto! Una volta ultimata l’app mi sono accorto però che youtube mette a disposizione un numero limitato di chiamate API giornaliere e oltretutto ho pensato che fosse inutile che l’App, ogni volta che viene aperta, debba richiamare le API Youtube. I video oltretutto non cambiano tanto in quanto questo canale produce un nuovo video a settimana. Per risolvere il problema delle chiamate API Youtube e rendere il tutto più reattivo ho deciso di salvare tutto in una lista di stringhe json in cache utilizzando proprio la Shared Preferences. Infine ho aggiunto un parametro nella remote config contente una data. In questo modo ho fatto un meccanismo che mi permette di aggiornare i contenuti nei dispositivi solo se la data è più recente rispetto a quella memorizzata in cache del singolo dispositivo stesso: class YoutubeData { RemoteConfig remoteConfig; String apikey; List<YoutubeDto> allYoutubeVideoList = []; List<YoutubeDto> getHomeList() { return List<YoutubeDto>.from(allYoutubeVideoList.where((item) => item.kind == "video" && item.id != item.channelId )); } List<YoutubeDto> allCategory() { return List<YoutubeDto>.from(allYoutubeVideoList.where((item) => item.kind == "playlist" && item.id != item.channelId )); } List<YoutubeDto> getCategoryVideoList(String plylistID) { return List<YoutubeDto>.from(allYoutubeVideoList.where((item) => item.kind == "video" && item.id != item.channelId && item.playlist == plylistID )); } YoutubeData (this.apikey, this.remoteConfig); checkAndLoad() async { SharedPreferences preferences = await SharedPreferences.getInstance(); List<String> videoIDList = preferences.getStringList("VideoIdList"); DateTime lastUpdateDate,updateDate; String date = remoteConfig.getString("UpdateDatetime"); updateDate = DateTime.parse(date); if(preferences.getString("LastUpdateDatetime")!=null && preferences.getString("LastUpdateDatetime").isNotEmpty) lastUpdateDate = DateTime.parse(preferences.getString("LastUpdateDatetime")); else lastUpdateDate = DateTime(2010); if( (videoIDList==null || videoIDList.length==0) || updateDate.isAfter(lastUpdateDate)) { await loadYtDataAndStoreInSharedPref(); preferences.setString("LastUpdateDatetime", DateTime.now().toString() ); } await loadLocalDataAndAddQuestion(); } loadYtDataAndStoreInSharedPref() async { YoutubeAPI ytApi = new YoutubeAPI(apikey, maxResults: 50); List<YT_API> ytResult = await ytApi.channel( remoteConfig.getString('ChannelId') ); //salviamo il conteggio dei video in shared pref SharedPreferences preferences = await SharedPreferences.getInstance(); preferences.setInt("TotVideo", ytResult.length ); //salviamo json youtube video in shared pref e lista video YoutubeDto youtubedto = new YoutubeDto(); List<String> videoIdList = new List<String>(); for(var result in ytResult) { videoIdList.add( result.id); String json = jsonEncode( youtubedto.yApitoJson(result) ); preferences.setString( result.id, json ); } preferences.setStringList("VideoIdList", videoIdList ); } //aggiungiamo le domande agli oggetti youtube precedentemente salvati loadLocalDataAndAddQuestion() async { SharedPreferences preferences = await SharedPreferences.getInstance(); List<String> videoIDList = preferences.getStringList("VideoIdList"); allYoutubeVideoList = new List<YoutubeDto>(); int cntQuestion=0; for(String videoID in videoIDList) { String jsonYoutube = preferences.getString(videoID); Map userMap = jsonDecode(jsonYoutube); YoutubeDto youtubeDao = new YoutubeDto.fromJson(userMap); youtubeDao.questionJson = remoteConfig.getString( "_" + videoID.replaceAll("-", "_trattino_") ); if( youtubeDao.questionJson != null && youtubeDao.questionJson.isNotEmpty ) { cntQuestion = cntQuestion + 'question'.allMatches(youtubeDao.questionJson).length; youtubeDao.playlist = json.decode( youtubeDao.questionJson)['Playlist']; } allYoutubeVideoList.add(youtubeDao); } preferences.setInt("TotQuestion", cntQuestion ); } } Qui di seguito potete vedere un esempio di come funziona:
Prerequisites This article requires a good knowledge of the following topics that you can study at the following links: Firebase Remote Config Flutter Shared Preferences Why are we talking about these two plugins that would see having nothing in common? i With this guide I will show you how to benefit from this two plugins and how to save money using the free services offered by Firebase! :) In the talian with Eli app, created for the youtube channel Italian with Eli, I created a project on Firebase, so you can use the remote config and other potential that Firebase makes available for you. The app takes care of showing a video list from the youtube channel, if interested you can download the source code on github, you can use it with any youtube channel. To make the Italian with Eli app more original I thought of adding questions to the videos on the topics covered in the videos. So I needed a place to save the question data. With Firebase, the most logical and also most direct thing would be to use Realtime Database or Firestore. I thought of using remote config! Yes you got it right remote config! You will be thinking “this guy is crazy” and “why is he doing that?” I fully share with you this thought… there is a bit of madness in the choice made. The reason why I thought of using remote config is the following: I didn’t have much time available remote config is free and has no limitation of use you can give access to the service to an external person the other choices would have led me to have to also build a back end interface to allow the census of the questions I had already used remote config and I would have been faster in the implementation. But how does a service not born with this scope work? It works very well indeed, I must say that I was surprised by the responsiveness that shows the app and the system that I put up. To make it work so well, however, I had to put the two plugins together in the way that I explain below. Inside the remote config console I’m going to insert a new parameter for each video. In the name field I insert the id of the youtube video and inside the parameter a json containing the questions and answers structured as follows: { "Playlist": "PLsrqydfBIVzy9IMGwSQieTVeSWC7cRCnN", " Questions ": [ { " question ":" How do you say how much is it ??: ", " answare ": [ " How much does it cost? ", " How much does it cost? ", " How much does it cost? " ] }, { "question": "How do you say how much are they ?:", "answare": [ "How much do they cost?", "How much do you cost?", "both / both" ] }, { "question" : "Choose the correct option", "answare": [ "How much does this computer cost? 300 Euros", "How much computer does it cost? 300 Euros", "How much does this computer cost?" ] }, { "question": "How much does it cost? is", "answare": [ "singular", "plural" ] } ] } In addition, firebase provides an easy insertion that also performs json validation, this allows us to edit more easily and not to make mistakes. In the Flutter app I then built the Quiz object which contains the youtube id, a list of Questions and the Question will contain a list of Answers. With various useful management and constructor methods for deserializing Json data as follows: class Answer { final String answerText; final bool isCorrect; Answer (this.answerText, this.isCorrect); } class Question { final String question; final List <Answer> answers; Question (this.question, this.answers); } class Quiz { List <Question> _questions; int _currentIndex = -1; int _score = 0; String _videoId; Quiz.fromJson (String videoId, String jsonInput) { _videoId = videoId; _questions = new List <Question> (); var jsonQuestions = json.decode (jsonInput); jsonQuestions ['Questions']. forEach ((quest) { List <Answer> answers = new List <Answer> (); bool iscorrect = true; quest ['answare']. forEach ((ans) { // put the first to true answers.add (new Answer (ans, iscorrect)); iscorrect = false; }); answers.shuffle (); _questions.add (new Question (quest ['question'], answers)); }); } Quiz (this._questions) { _questions.shuffle (); } List <Question> get questions => _questions; int get length => _questions.length; int get questionNumber => _currentIndex + 1; int get score => _score; Question get nextQuestion { _currentIndex ++; if (_currentIndex> = length) return null; return _questions [_currentIndex]; } Question get prevQuestion { _currentIndex--; if (_currentIndex <0) return null; return _questions [_currentIndex]; } String get correctAnsware { return _questions [_currentIndex] .answers.where ((x) => x.isCorrect == true) .first.answerText; } void answer (bool isCorrect) { if (isCorrect) _score ++; } saveScore () async { SharedPreferences preferences = await SharedPreferences.getInstance (); preferences.setInt ("OK" + _ videoId, this.score); preferences.setInt ("KO" + _ videoId, this.length - this.score); //preferences.setString(_videoId, "{'questions': '" + this.length.toString () + "', 'score': '" + this.score.toString () + "'}"); } } The app first retrieves the list of videos via the youtube API, then retrieves the relative json from remote config using the video id and passes it to the constructor of the object that deserialize and you’re done. Once the app was completed, however, I realized that youtube provides a limited number of daily API calls. Moreover I thought it was useless every time I opened the app that it had to call the Youtube API. The videos, moreover, do not change as much as the channel makes a new video a week. To solve the problem of Youtube API calls and make everything more responsive, I decided to save everything in a list of cached json strings using the Shared Preferences. Finally I added a parameter in the remote config containing a date. In this way I made a mechanism that allows me to update the contents on the devices only if the date is more recent than the one stored in the cache of the single device itself: class YoutubeData { RemoteConfig remoteConfig; String apikey; List <YoutubeDto> allYoutubeVideoList = []; List <YoutubeDto> getHomeList () { return List <YoutubeDto> .from (allYoutubeVideoList.where ((item) => item.kind == "video" && item.id! = Item.channelId)); } List <YoutubeDto> allCategory () { return List <YoutubeDto> .from (allYoutubeVideoList.where ((item) => item.kind == "playlist" && item.id! = Item.channelId)); } List <YoutubeDto> getCategoryVideoList (String plylistID) { return List <YoutubeDto> .from (allYoutubeVideoList.where ((item) => item.kind == "video" && item.id! = Item.channelId && item.playlist == plylistID)); } YoutubeData (this.apikey, this.remoteConfig); checkAndLoad () async { SharedPreferences preferences = await SharedPreferences.getInstance (); List <String> videoIDList = preferences.getStringList ("VideoIdList"); DateTime lastUpdateDate, updateDate; String date = remoteConfig.getString ("UpdateDatetime"); updateDate = DateTime.parse (date); if (preferences.getString ("LastUpdateDatetime")! = null && preferences.getString ("LastUpdateDatetime"). isNotEmpty) lastUpdateDate = DateTime.parse (preferences.getString ("LastUpdateDatetime")); else lastUpdateDate = DateTime (2010); if ((videoIDList == null || videoIDList.length == 0) || updateDate.isAfter (lastUpdateDate)) { await loadYtDataAndStoreInSharedPref (); preferences.setString ("LastUpdateDatetime", DateTime.now (). toString ()); } await loadLocalDataAndAddQuestion (); } loadYtDataAndStoreInSharedPref () async { YoutubeAPI ytApi = new YoutubeAPI (apikey, maxResults: 50); List <YT_API> ytResult = await ytApi.channel (remoteConfig.getString ('ChannelId')); // save the video count in shared pref SharedPreferences preferences = await SharedPreferences.getInstance (); preferences.setInt ("TotVideo", ytResult.length); // save json youtube video in shared pref andvideo list YoutubeDtoyoutubedto = new YoutubeDto (); List <String> videoIdList = new List <String> (); for (var result in ytResult) { videoIdList.add (result.id); String json = jsonEncode (youtubedto.yApitoJson (result)); preferences.setString (result.id, json); } preferences.setStringList ("VideoIdList", videoIdList); } // add the questions to the previously saved youtube objects loadLocalDataAndAddQuestion () async { SharedPreferences preferences = await SharedPreferences.getInstance (); List <String> videoIDList = preferences.getStringList ("VideoIdList"); allYoutubeVideoList = new List <YoutubeDto> (); int cntQuestion = 0; for (String videoID in videoIDList) { String jsonYoutube = preferences.getString (videoID); Map userMap = jsonDecode (jsonYoutube); YoutubeDto youtubeDao = new YoutubeDto.fromJson (userMap); youtubeDao.questionJson = remoteConfig.getString ("_" + videoID.replaceAll ("-", "_trattino_")); if (youtubeDao.questionJson! = null && youtubeDao.questionJson.isNotEmpty) { cntQuestion = cntQuestion + 'question'.allMatches (youtubeDao.questionJson) .length; youtubeDao.playlist = json.decode (youtubeDao.questionJson) ['Playlist']; } allYoutubeVideoList.add (youtubeDao); } preferences.setInt ("TotQuestion", cntQuestion); } } Below you can see an example of how it works:
Il nome scelto da Google questa volta è molto comprensivo ed esaustivo. Firebase remote config è una configurazione salvata in remoto, in questo caso su Google Cloud. La configurazione è accessibile in modifica nella console di Firebase, alla voce di menù Firebase remote config che si trova in fondo, molto in fondo, nell’ultima sezione secondo me un pò nascosta. Prerequisiti: creare un account su Firebase utilizzando il nostro account google creare un app, io ho usato flutter collegare la nostra app al codice sorgente con la chiave json Facendo questo potremmo utilizzare tutti i servizi Firebase, ci sono tanti tutorial online e Firebase stesso mette a disposizione wizard con step per guidarvi alla corretta implementazione di Firebase con la vostra app.Possiamo usare Firebase Remote Config per memorizzare tutto quello che la nostra app necessità come parametri di configurazioni. Per prima cosa, dobbiamo individuare e mettere in configurazione ciò che pensiamo possa cambiare nel tempo, in questo modo avremo delle configurazioni nell’app modificabili in tempo reale da remoto in modo da non dover rieffettuare un nuovo deploy dell’app stessa. Un esempio può essere il caso in cui stiamo utilizzando un Api per recuperare dei dati, l’url base delle api potrebbe cambiare nel tempo. Un altro esempio più concreto che utilizzo io nell’app send to kindle è un parametro che contiene il numero di azioni prima di mostrare una pubblicità. In questo modo se gli utenti si lamentano per le troppe pubblicità posso cambiarlo in tempo reale con il solo cambio di un parametro da interfaccia web a costo zero. Il sistema di Firebase funziona davvero alla grande, c’è dietro un sistema push-lazy che utilizza i dati internet per recuperare le configurazioni solo se davvero abbiamo cambiato qualcosa lato server e quindi abbiamo pubblicato una modifica ad uno o più parametri. L’aggiornamento dei parametri avviene in real time e quindi la modifica sarà subito disponibile nell’app. I parametri possono essere anche associati a delle condizioni. Ad esempio un parametro può essere utilizzato solo per gli utenti di una certa età di un determinato paese o lingua a un determinato orario. Figata no? Vantaggi Controllo da remoto istantaneo, semplice e centralizzato. Modifica App in tempo reale. Diminuisce la necessità di dover effettuare un nuovo deploy. i parametri possono essere combinati con delle condizioni. Come lo uso io nelle mie app? Di seguito voglio mostrarvi un esempio di implementazione. In questo modo ho centralizzato la parte di inizializzazione e creazione dei parametri in modo che siano più accessibili. Ho deciso di utilizzare un singleton in modo da poterlo usare in tutte le view e in tutte le classi per cui ne ho bisogno, implementato nel seguente modo: import 'package:firebase_remote_config/firebase_remote_config.dart'; class Configuration { int _link_sent_before_ads; int _max_send_limit_size = 24000000; int get linkSentBeforeAds => _link_sent_before_ads; int get maxSendLimitSize => _max_send_limit_size; int get maxSendLimitSizeMb => (_max_send_limit_size/1000000).round(); static Configuration get instance => _instance; static final Configuration _instance = Configuration._privateConstructor(); Configuration._privateConstructor() { //initializeRemoteConfig(); } loadConfig(RemoteConfig _remoteConfig) async { _link_sent_before_ads = _remoteConfig.getString('link_sent_before_ads').isEmpty ? _link_sent_before_ads : int.tryParse(_remoteConfig.getString('link_sent_before_ads')); _max_send_limit_size = _remoteConfig.getString('max_send_limit_size').isEmpty ? _max_send_limit_size : int.tryParse(_remoteConfig.getString('max_send_limit_size')); } initializeRemoteConfig() async{ RemoteConfig remoteConfig = await RemoteConfig.instance; try { //set some default params in case of crash await remoteConfig.setConfigSettings(RemoteConfigSettings(debugMode: false)); await remoteConfig.setDefaults(<String, dynamic>{ 'service_api_conversion_1': 'https://ebook-converter.app/calibre/ebook-convert', }); // Using default duration to force fetching from remote server. await remoteConfig.fetch(expiration: const Duration(seconds: 0)); await remoteConfig.activateFetched(); } on FetchThrottledException catch (exception) { // Fetch throttled. print(exception); } catch (exception) { print('Unable to fetch remote config. Cached or default values will be used'); } await this.loadConfig(remoteConfig); } } potrà essere infine utilizzato molto semplicemente nel seguente modo: //call this just first time in app initialization await Configuration.instance.initializeRemoteConfig(); Print(Configuration.instance.maxSendLimitSize);
What is shared_preferences where, when and how much to use it? The shared_preference is a plugin that allows you to save data app dedicated memory, the folder that will contain this data is named in Android with the package name of your app, e.g. com.companyname.appname. The data that we can store in this space with this plugin are data of these types: String Integer Bool List It should be noted that these parameters are not persistent, they remain in the area dedicated to the app therefore are eliminated when the app is uninstalled or if the app cache data is removed. So, in general it is right to store non-essential preference data so that it is not a tragedy if they have been lost. A classic example can be the app theme color light or dark or the order of menu title. How to use? Saving: import 'package:shared_preferences/shared_preferences.dart'; SharedPreferences _preferences = await SharedPreferences.getInstance(); _preferences.setString("theme", "dark"); _preferences.setInt("countaccess", 2); Recovery: import 'package:shared_preferences/shared_preferences.dart'; SharedPreferences _preferences = await SharedPreferences.getInstance(); _preferences.getString("theme"); _preferences.getInt("countaccess"); At first we might think that it is of little use since in a string we cannot store a lot of information. In fact, if we think about it in a string, we can also store a lot but really a lot by using a json, below an example: import 'package:shared_preferences/shared_preferences.dart'; SharedPreferences _preferences = await SharedPreferences.getInstance(); _preferences.setString("{"name":"Andrea","surname":"Luciano","age":31,"sex":"M","SurnameCouldBeName":true}"); In this example I put SurnameCouldBeName as a field because my last name is also a first name. All those who don’t know me always ask me “Luciano is your name or surname?” And every time I would like to do something like that: We can therefore create a collection of people using a list of strings instead of the single string as follows: import 'package:shared_preferences/shared_preferences.dart'; SharedPreferences _preferences = await SharedPreferences.getInstance(); List<String> users = new List<String>(); users.add("{"name":"Andrea","surname":"Luciano","age":31,"sex":"M","SurnameCouldBeName":true}"); users.add("{"name":"elisa","surname":"luciano","age":32,"sex":"F","SurnameCouldBeName":false}"); _preferences.setStringList("users", users); This could finally be mapped into an object in order to access the data simply and finally show it to the interface: Class UserDto() { String name, surname, age, sex, SurnameCouldBeName; UserDto.fromJson(Map<String, dynamic> json) : name= json[name], surname= json[surname], age= json[age], sex= json[sex], SurnameCouldBeName = json[SurnameCouldBeName]; } SharedPreferences _preferences = await SharedPreferences.getInstance(); List<UserDto> UserObj = new List<UserDto>; List<String> users = _preferences.getStringList("users"); for(String userjson in users) { Map userMap = jsonDecode(userjson); UserDto = new UserDto.fromJson(userMap); print(UserDto.name); UserObj.add(UserDto); } Think about the possible benefits and applications of this. It can be used to store data returned by an API. In this way, the application would be more efficient and also can work offline without therefore having to use network data for recovering data from the server.
Che cos’è la shared_preferences dove, quando e quanto utilizzarla? La shared_preference è un plugin che vi permette di salvare dei dati in memoria dedicata all’app, la cartella che li conterrà prende il nome in android con il nome del pacchetto della vostra app es com.nomeazienda.nomeapp. I dati che possiamo memorizzare all’interno di questa memoria con questo plugin sono dei seguenti tipi: string intero bool List E’ da notare che questi parametri non sono persistenti, rimangono nell’area dedicata all’app e quindi vengono eliminate nel momento in cui l’app viene disinstallata oppure se vengono rimossi i dati della cache dell’app. Quindi in genere è doveroso memorizzare dati di preferenze non indispensabili in modo che non sia una tragedia se dovessero essere persi. Un esempio classico può essere come voglio il tema light o dark oppure l’ordine degli oggetti nel menu. Come si usa? Salvataggio: import 'package:shared_preferences/shared_preferences.dart'; SharedPreferences _preferences = await SharedPreferences.getInstance(); _preferences.setString("theme", "dark"); _preferences.setInt("countaccess", 2); Recupero: import 'package:shared_preferences/shared_preferences.dart'; SharedPreferences _preferences = await SharedPreferences.getInstance(); _preferences.getString("theme"); _preferences.getInt("countaccess"); In un primo momento potremmo pensare che è una cosa che serve a poco in quanto in una stringa non possiamo memorizzare tante informazioni. In realtà, se ci pensiamo bene, in una stringa possiamo anche memorizzare molto ma davvero molto semplicemente utilizzando un json, di seguito esempio: import 'package:shared_preferences/shared_preferences.dart'; SharedPreferences _preferences = await SharedPreferences.getInstance(); _preferences.setString("{"name":"Andrea","surname":"Luciano","age":31,"sex":"M","SurnameCouldBeName":true}"); In questo esempio ho messo come campo SurnameCouldBeName in quanto il mio cognome è anche un nome. Tutti quelli che non mi conoscono mi chiedono sempre “Luciano è il nome o il cognome?” E ogni volta vorrei fare qualcosa del genere: Possiamo quindi creare una collezione di persone utilizzando, al posto della singola stringa, una lista di stringhe come segue: import 'package:shared_preferences/shared_preferences.dart'; SharedPreferences _preferences = await SharedPreferences.getInstance(); List<String> users = new List<String>(); users.add("{"name":"Andrea","surname":"Luciano","age":31,"sex":"M","SurnameCouldBeName":true}"); users.add("{"name":"elisa","surname":"luciano","age":32,"sex":"F","SurnameCouldBeName":false}"); _preferences.setStringList("users", users); Questo infine potrebbe essere mappato in un oggetto in modo da accedere ai dati più semplicemente ed infine mostrarli nell’interfaccia: Class UserDto() { String name, surname, age, sex, SurnameCouldBeName; UserDto.fromJson(Map<String, dynamic> json) : name= json[name], surname= json[surname], age= json[age], sex= json[sex], SurnameCouldBeName = json[SurnameCouldBeName]; } SharedPreferences _preferences = await SharedPreferences.getInstance(); List<UserDto> UserObj = new List<UserDto>; List<String> users = _preferences.getStringList("users"); for(String userjson in users) { Map userMap = jsonDecode(userjson); UserDto = new UserDto.fromJson(userMap); print(UserDto.name); UserObj.add(UserDto); } Pensate ai possibili vantaggi e alle possibili applicazioni. Può essere utilizzato per memorizzare dati restituiti da un API. Con questo approccio, l’applicazione è più efficiente e potrebbe funzionare anche offline senza quindi dover utilizzare sempre la rete dati per recuperare i dati dal server.
The name chosen by Google this time is very comprehensive and exhaustive. Firebase remote config is a remotely saved configuration, in this case on Google Cloud. This configuration is accessible to insert and update in the Firebase console, under the menu item Firebase remote config which is located at the very bottom, in the last section, in my opinion a little hidden. Prerequisites: create an account on Firebase using our google account create an app, I used flutter connect our app to the source code using json key file By doing this we could use all the Firebase services, there are many online tutorials and Firebase itself provides wizards with steps to guide you to the correct implementation of Firebase with your app. We can use Firebase Remote Config to store everything our app needs as configuration parameters. First of all, We must identify and configure what we think will change over time. In this way we will have configurations in the app that can be modified in real time remotely so as not to have to carry out a new app deployment. An example may be the case in which we are using an API to recover data, the base url may change over time. Another more down to earth-example that I use in the send to kindle app is a parameter that contains the number of actions before showing an advertisement. In this way, if users complain about too many advertisements, I can change it in real time with just changing a parameter into the web interface at no cost. The Firebase system works really well, there is a push-lazy system behind it that uses internet data to retrieve the configurations only if we have really changed something on the server side and therefore we have published a change to one or more parameters. The updating of the parameters takes place in real time so it changes immediately the behavior, state or data in app as real time. Parameters can also be associated with conditions. For example, a parameter can only be used for users of a certain age in a certain country or language at a specific time. Cool! isn’t it? Advantages Instant and simple remote control. Real-time app customization. It decreases the need for a new deployment. Parameters can be combined with conditions. How do I use it in my apps? Below I want to show you an example of implementation. In this way I centralized the initialisation and parameter creation part so that they are more accessible. I decided to use a singleton so that I can use it in all the views and all the classes I need it, implemented in the following way: import 'package:firebase_remote_config/firebase_remote_config.dart'; class Configuration { int _link_sent_before_ads; int _max_send_limit_size = 24000000; int get linkSentBeforeAds => _link_sent_before_ads; int get maxSendLimitSize => _max_send_limit_size; int get maxSendLimitSizeMb => (_max_send_limit_size/1000000).round(); static Configuration get instance => _instance; static final Configuration _instance = Configuration._privateConstructor(); Configuration._privateConstructor() { //initializeRemoteConfig(); } loadConfig(RemoteConfig _remoteConfig) async { _link_sent_before_ads = _remoteConfig.getString('link_sent_before_ads').isEmpty ? _link_sent_before_ads : int.tryParse(_remoteConfig.getString('link_sent_before_ads')); _max_send_limit_size = _remoteConfig.getString('max_send_limit_size').isEmpty ? _max_send_limit_size : int.tryParse(_remoteConfig.getString('max_send_limit_size')); } initializeRemoteConfig() async{ RemoteConfig remoteConfig = await RemoteConfig.instance; try { //set some default params in case of crash await remoteConfig.setConfigSettings(RemoteConfigSettings(debugMode: false)); await remoteConfig.setDefaults(<String, dynamic>{ 'service_api_conversion_1': 'https://ebook-converter.app/calibre/ebook-convert', }); // Using default duration to force fetching from remote server. await remoteConfig.fetch(expiration: const Duration(seconds: 0)); await remoteConfig.activateFetched(); } on FetchThrottledException catch (exception) { // Fetch throttled. print(exception); } catch (exception) { print('Unable to fetch remote config. Cached or default values will be used'); } await this.loadConfig(remoteConfig); } } It can finally be used very simply in the following way: //call this just first time in app initialization await Configuration.instance.initializeRemoteConfig(); Print(Configuration.instance.maxSendLimitSize);
Con questo articolo voglio spiegare come poter fare una app per il proprio canale personale youtube in 10 minuti. Io ho sviluppato in questo modo l’app Italiano con Eli, creata per il canale Italiano con Eli. Mentre stavo sviluppato l’app ho deciso di generalizzare la soluzione in modo da poter essere utilizzata da chiunque per recuperare qualsiasi video dato un determinato link del corrispettivo canale Youtube. L’applicazione completa e funzionante è disponibile al seguente link github. Potete fare un Fork o un Clone, usarla così com’è oppure modificandola come meglio vi pare. Se vi piace potete anche mettere un like. Configurazione necessaria Unica cosa da fare per poterla utilizzare è configurare la connessione alle API di Youtube in modo da poter utilizzare la ricerca dei vostri video del canale. Per una corretta configurazione potete seguire i seguenti step: Accedere al Google Api Console con il vostro account Google se non lo avete, create un nuovo account. Andare sotto progetti e cliccate su NUOVO PROGETTO, Immetti il nome e aspetta che google crei il progetto. Clicca sul link delle Google API nella libreria delle API e cerca oppure seleziona le api youtube come evidenziato nella foto di seguito dopo clicca abilità e vai sotto la voce di menu credenziali Seleziona Youtube Api v3, seleziona dati pubblici e conferma Adesso Google creerà la tua chiave di accesso l’api key recuperata al punto 6 dovrà essere inserita all’interno dell’applicazione e sostituita nella pagina main.dart al posto della key come segue: static String key = ""; // ** ENTER YOUTUBE API KEY HERE ** static String channelid = "UC_8mNVpafplqHNy85No4O2g"; // ** ENTER YOUTUBE CHANNEL ID HERE ** il channel id deve fare riferimento al vostro canale youtube. Lo potete semplicemente estrarre dal link del vostro canale youtube, prendendo la parte finale del link: La configurazione è finita, avete appena creato la vostra nuova App per il vostro canale Youtube Yehaa! Qualche cenno al codice Per agevolare lo sviluppo dell’applicazione ho usato i seguenti due plugin che potete trovate nella pagina dei plugin per flutter flutter packages i plugin utilizzati sono i seguenti: youtube player Flutter youtube Api il codice dell’applicazione è molto semplice e ho usato una struttura di suddivisione pagine abbastanza chiara: Model contiene il modello dei dati recuperati dalle youtube Api. Adesso la classe fa anche qualcosa in più, c’è un metodo che recupera i dati da youtube e li inserisce in una lista di youtube data. Usa un sistema che fa il recupero dei dati solo se non sono presenti in cache usando la flutter shared preference. Questo meccanismo è molto utile in quanto le Api di Google sono a pagamento in funzione dell’utilizzo. Utilizzando la cache risparmierete drasticamente in chiamate. se non avete mai usato la cache in flutter potete leggere quest’altro mio articolo: flutter shared preferences in profondità. Pages contiene le pagine dell’APP home page category page pagina di play video UI contiene un widget per rappresentare la lista dei video. L’ho creato a parte come widget per avere a disposizione una view da poter utilizzare sia nella pagina home che category. In questo modo, potrei usarlo anche in altre pagine in futuro. Main.dart altro non fa che inizializzare la classe del model, caricare i dati di youtube e passarli alle rispettive pagine. Utilizzando questa struttura il codice è molto semplice anche da manutenere ad esempio la pagina home contiene solo il seguente codice: import 'package:flutter/material.dart'; import 'package:youtubeapichannel/Model/YoutubeData.dart'; import 'package:youtubeapichannel/UI/YoutubeVideoList.dart'; YoutubeData youtubeData; class Home extends StatefulWidget { Home(YoutubeData inyoutubeData) { youtubeData = inyoutubeData; } @override _HomeState createState() => new _HomeState(); } class _HomeState extends State<Home> { callAPI() async { setState(() {print('UI Updated'); }); } @override void initState() { super.initState(); //callAPI(); } @override Widget build(BuildContext context) { if(youtubeData!=null) return new YoutubeVideoList(youtubeData.getHomeList()); else return new Container(child:new Text("problem loading data from youtube!")); } } Quindi riceve semplicemente in input la classe contenente la lista dei video e richiama il plugin YoutubeVideoList che sta sotto la UI per rappresentare i video sotto forma di lista di video youtube. Unica pecca, è che quando l’ho realizzata non ero tanto esperto e ho deciso di passare i dati come parametro. Oggi flutter mette a disposizioni tecniche migliori per passare i dati da una view ad un altra, in effetti dovrei fare un po' di refactor, ma oggi non ne ho voglia.
With this article I want to explain how to build an app for your personal youtube channel in 10 minutes. I did this basic project to create Italiano con Eli app, created for the YouTube channel Italiano con Eli. While I was developing the app I decided to generalize the solution so that it can retrieve any video to any Youtube channel. The complete and working application is available at the following link github. You can make a Fork or a Clone or use it as it or by modifying it as you like. If you like you can also put a like to my git project. Required configuration The only thing to do to be able to use it is to configure the connection to the Youtube API so that you can use the search for your channel videos. For a correct configuration you can follow the following steps: Log in to the Google Api Console with your Google Account if you don’t have it, create a new account. Go under projects and click on NEW PROJECT, enter the name and wait for google to create project. Click on the link in the Google API library and search or select the youtube API as highlighted in the photo below after click ability and go under the credentials menu item Select Youtube Api v3, select public data and confirm Now Google will create your api key the api key recovered in step 6 will be inserted within the application and replaced the page main.dart instead of the key as follows: static String key = ""; // ** ENTER YOUTUBE API KEY HERE ** static String channelid = "UC_8mNVpafplqHNy85No4O2g"; // ** ENTER YOUTUBE CHANNEL ID HERE ** the channel id must refer to your youtube channel. You can simply extract it from the link of your youtube channel, taking the final part of the link: The configuration is finished, you have just created your App for your personal Youtube channel! Yehaa! Some references to the code To facilitate the development of the application I used the following two flutter plugins that you can find on flutter packages pages. The plugins used are the following: youtube player Flutter youtube API the application code is very simple and I have given apage division structure fairly clear: Model contains the model of data recovered from API youtube. Now the class also does something more, there is a method that also retrieves the data from youtube and inserts it in the model and some method to recover the data. There is also a system that recovers data only if it is not cached using the shared preference flutter. This mechanism is very useful as the Google API is paid according to the use. Using the cache will save you drastically in calls. if you have never used the cache in flutter you can read this mine other article: flutter shared preferences in depth. Pages contains the APP pages home page category page play video page UI contains a widget to represent the list of videos. I created it separately as a widget to create a view that can be used on both the home page and category and I could also use it in other pages in the future. **Main.dart ** just to initialize view, the model class, load the youtube data and pass it to the respective pages. Using this structure, the code is very simple to maintain, for example the home page contains only the following code: import 'package:flutter/material.dart'; import 'package:youtubeapichannel/Model/YoutubeData.dart'; import 'package:youtubeapichannel/UI/YoutubeVideoList.dart'; YoutubeData youtubeData; class Home extends StatefulWidget { Home(YoutubeData inyoutubeData) { youtubeData = inyoutubeData; } @override _HomeState createState() => new _HomeState(); } class _HomeState extends State<Home> { callAPI() async { setState(() {print('UI Updated'); }); } @override void initState() { super.initState(); //callAPI(); } @override Widget build(BuildContext context) { if(youtubeData!=null) return new YoutubeVideoList(youtubeData.getHomeList()); else return new Container(child:new Text("problem loading data from youtube!")); } } Then simply receive the class containing the list of videos as input and call the YoutubeVideoList plugin that is under the UI to represent them in the form of a list of YouTube videos. The only flaw is that when I made it I was not so expert and I decided to pass the data as a parameter. Today flutter offers better technical provisions for passing data from one view to another, in fact I should make an update and do the usual refactor.
There are many ways to secure our functions. This topic is not clear when you start using the Cloud Function and it is also constantly changing. I decided to implement my own method that uses the basic and simplest authentication, the Basic Auth. It certainly is not the safest method but it was enough for my purpose. The only flaw is that the function has to be added and implemented in every Cloud function that we make.This implementation then gives us the possibility to call our function from anywhere we want to by simply adding as a parameter of the request the user and the password encrypted in the Basic Auth. To simplify the verification, I created a function that I place in each Cloud Function. This function does the parsing of the request data, verifies if there is a basic auth and, if present, it checks whether the user and password data match. Here is the code I used: var compare = require('tsscmp') const checkAuth = async() => { // parse login and password from headers const b64auth = (req.headers.authorization || '').split(' ')[1] || '' const strauth = new Buffer(b64auth, 'base64').toString() const splitIndex = strauth.indexOf(':') const login = strauth.substring(0, splitIndex) const password = strauth.substring(splitIndex + 1) //function to validate credentials using https://www.npmjs.com/package/tsscmp //Prevents timing attacks using Brad Hill's Double HMAC pattern to perform secure string comparison function check (name, pass) { var valid = true // Simple method to prevent short-circut and use timing-safe compare valid = compare(name, 'user') && valid valid = compare(pass,'password') && valid return valid } if (!check(login, password)) { res.statusCode = 401 res.setHeader('WWW-Authenticate', 'Basic realm="example"') res.end('Access denied') } } Then we can use the function within the method called by our Cloud Function in the following way: exports.helloWorld = async (req, res) => { await checkAuth(); let message = req.query.message || req.body.message || 'Hello World!'; res.status(200).send(message); }; And that’s it, our functions are now safe! Not much but it’s a starting point. So in the end if our system is breached,it is not a big problem, in this function we only return Hello World! How do we instead call our function with Flutter? We simply have to create the Basic Auth with user and password and add it to the request as follows: var basicAuth = 'Basic '+base64Encode(utf8.encode(user:password')); http.Response response = await http.get(apiUrl, headers: <String, String>{'authorization': basicAuth}); print(response.statusCode); //200 print(response.body ); //Hello world! This method is not one of the best, however, it is very fast to implement and also gives us an advantage because it can be used in the same way from all Cloud providers. Finally, we can recall it from anywhere using any language, and it does not force us to use json keys that could be displayed. The only problem I had was having the username and password inside the code that I didn’t really like. I solved it by putting these parameters within the firebase configurations. If you are interested in this, you can read this article Flutter 2 indispensable plugins with great potential.
Ci sono tanti modi per poter mettere al sicuro le funzioni. Questo argomento non è chiarissimo dal primo momento in cui si utilizzano le funzioni in Cloud ed è inoltre in continuo cambiamento. Io ho deciso di implementare un mio metodo che utilizza la blasonata e più semplice autenticazione: la Basic Auth. Sicuramente non è la più sicura ma bastava al mio scopo. Unica pecca è che la funzione deve essere aggiunta e implementata in ogni funzione Cloud che andiamo a creare. In questo modo, possiamo chiamare la funzione implementata da dove vogliamo semplicemente aggiungendo come parametro della request user e password criptati nella Basic Auth. Per semplificare la verifica, ho creato, una funzione che inserisco all’interno di ogni Cloud Function. Questa funzione si occupa di fare il parsing dei dati della request, verificare se presente una basic auth e se presente verifica se i dati di user e password corrispondono. Di seguito il codice utilizzato: var compare = require('tsscmp') const checkAuth = async() => { // parse login and password from headers const b64auth = (req.headers.authorization || '').split(' ')[1] || '' const strauth = new Buffer(b64auth, 'base64').toString() const splitIndex = strauth.indexOf(':') const login = strauth.substring(0, splitIndex) const password = strauth.substring(splitIndex + 1) //function to validate credentials using https://www.npmjs.com/package/tsscmp //Prevents timing attacks using Brad Hill's Double HMAC pattern to perform secure string comparison function check (name, pass) { var valid = true // Simple method to prevent short-circut and use timing-safe compare valid = compare(name, 'user') && valid valid = compare(pass,'password') && valid return valid } if (!check(login, password)) { res.statusCode = 401 res.setHeader('WWW-Authenticate', 'Basic realm="example"') res.end('Access denied') } } Successivamente possiamo utilizzare la funzione all’interno del metodo chiamato della nostra Cloud Function nel seguente modo: exports.helloWorld = async (req, res) => { await checkAuth(); let message = req.query.message || req.body.message || 'Hello World!'; res.status(200).send(message); }; Ed il gioco è fatto, le nostre funzioni adesso sono al sicuro. Mica tanto! ma è un inizio. Tanto alla fine se dovessero superare la nostra autenticazione, non sarebbe un grosso problema. In questa funzione restituiamo solo Hello World! Come facciamo invece a richiamare la nostra funzione con Flutter? Dobbiamo semplicemente creare la Basic Auth con user e password e aggiungerla alla request come segue: var basicAuth = 'Basic '+base64Encode(utf8.encode(user:password')); http.Response response = await http.get(apiUrl, headers: <String, String>{'authorization': basicAuth}); print(response.statusCode); //200 print(response.body ); //Hello world! Questo metodo non è uno dei migliori però è molto rapido implementarlo e inoltre ci dà un vantaggio perché può essere utilizzato allo stesso modo per tutti i provider cloud. Infine, possiamo richiamarlo dappertutto utilizzando qualsiasi linguaggio, e non costringe a doversi portare dietro chiavi json che potrebbero essere esposte. L’unico problema che ho avuto è stato quello di avere nel codice scolpiti user e password che non trovavo gradevoli. Ho risolto questo problema mettendo questi parametri all’interno delle configurazioni di firebase. Se siete interessati potete leggere questo articolo Flutter 2 plugin indispensabili dalle grandi potenzialità
Nell’articolo shared preferences in depth, ho spiegato come utilizzare la shared preferences in Flutter. In questo articolo voglio invece mostrarvi una classe che ho ideato per semplificare l’utilizzo. Ho creato questa classe per l’esigenza di dover memorizzare i dati degli utenti. Ad esempio l’ho usata in Send to Kindle per memorizzare una lista di mail. L’ho usata anche per memorizzare la lista dei token e dati d’acquisto dell’utente. In generale questa struttura può essere utilizzata laddove serve lavorare con una lista di stringhe. Nell’articolo precedente, ho già mostrato come fare a memorizzare una lista di stringhe in local cache. Questa classe però rende l’utilizzo ancora più semplici e aggiunge dei metodi che ci permettono di lavorare in maniera più rapida. Come prima cosa, ho creato il metodo per salvare una lista il local cache nel seguente modo: class CashedUserData { static Future<void> _writes = Future.value(); //add list by key reference in local cache static Future<void> addlist(String kPrefKey, List<String> stringList) async{ _writes = _writes.then((void _) => _doAddList(kPrefKey,stringList)); return _writes; } } static Future<void> _doAddList(String kPrefKey, List<String> list) async { List<String> cached = await load(kPrefKey); cached.addAll(list); await (await SharedPreferences.getInstance()).setStringList(kPrefKey, cached); } Successivamente ho creato un altro metodo per recuperare la lista, inoltre ho messo qui i controlli del caso se empty o null: static Future<List<String>> load(String kPrefKey) async { return (await SharedPreferences.getInstance()).getStringList(kPrefKey) ?? []; } Man mano che la usavo ho avuto la necessità di dover aggiungere altri metodi. Metodi per: Cancellare la lista. Inserire una stringa per volta. Fare l’override della lista. Verificare se un elemento esiste. Prendere il primo elemento della lista. Salvare un elemento in top alla lista. In questo modo la classe alla fine è diventata così: class CashedUserData { static Future<void> _writes = Future.value(); static Future<void> overrideListData(String kPrefKey, List<String> stringList) async{ _writes = _writes.then((void _) => _doOverrideListData(kPrefKey,stringList)); return _writes; } static Future<void> addlist(String kPrefKey, List<String> stringList) async{ _writes = _writes.then((void _) => _doAddList(kPrefKey,stringList)); return _writes; } static Future<void> save(String kPrefKey, String id) async{ if(await CashedUserData.exist(kPrefKey, id)) return; _writes = _writes.then((void _) => _doSave(kPrefKey,id)); return _writes; } static Future<void> saveAsFirst(String kPrefKey, String id) async{ List<String> actuaList = await load(kPrefKey); if(actuaList!=null && actuaList.length == 0 ) return save(kPrefKey,id); if(actuaList.contains(id)) actuaList.remove(id); if(actuaList.length>1) actuaList.removeAt(0); List<String> newlist = new List(); newlist.add(id); newlist.addAll(actuaList); _writes = _writes.then((void _) => _doOverrideListData(kPrefKey,newlist)); return _writes; } static Future<void> delete(String kPrefKey, String id) { _writes = _writes.then((void _) => _doDelete(kPrefKey,id)); return _writes; } static Future<String> getFirst(String kPrefKey) async { List<String> dataList = (await SharedPreferences.getInstance()).getStringList(kPrefKey); return (dataList!=null && dataList.length>0) ? dataList[0] : ""; } static Future<List<String>> load(String kPrefKey) async { return (await SharedPreferences.getInstance()).getStringList(kPrefKey) ?? []; } static Future<bool> exist(String kPrefKey, String id) async { List<String> userDataList = (await SharedPreferences.getInstance()).getStringList(kPrefKey) ?? []; return (userDataList!=null && userDataList.length > 0) ? userDataList.contains(id) : false; } static Future<void> _doSave(String kPrefKey, String id) async { List<String> cached = await load(kPrefKey); cached.add(id); await (await SharedPreferences.getInstance()).setStringList(kPrefKey, cached); } static Future<void> _doDelete(String kPrefKey, String id) async { List<String> cached = await load(kPrefKey); cached.remove(id); await (await SharedPreferences.getInstance()).setStringList(kPrefKey, cached); } static Future<void> _doAddList(String kPrefKey, List<String> list) async { List<String> cached = await load(kPrefKey); cached.addAll(list); await (await SharedPreferences.getInstance()).setStringList(kPrefKey, cached); } static Future<void> _doOverrideListData(String kPrefKey, List<String> list) async { await (await SharedPreferences.getInstance()).setStringList(kPrefKey, list); } static update(String kPrefKey, String currentItem, String kindlemail) async{ List<String> actuaList = await load(kPrefKey); if(actuaList!=null && actuaList.length > 0 ) { try{actuaList[actuaList.indexOf(currentItem)] = kindlemail;} catch (e) {} } await overrideListData(kPrefKey, actuaList); } } Facendo così ho semplificato molto e reso più semplice i punti di chiamata. Inoltre se dovesse cambiare il plugin shared preference utilizzato, c’è un unico posto dove dover intervenire.
In the article shared preferences in depth, I explained how to use shared preferences in Flutter. In this article I want to show you a class that I designed to simplify its use. I created this class out of the need to store user data. For example, I used it in Send to Kindle to store a mail list. I also used it to store the user’s token list and to purchase data. Overall, this structure can be used where there is the need of working with a list of strings. In my previous article, I have already shown how to store a list of strings in local cache. This class, however, makes it even easier to use and adds methods that allow us to work faster. First, I created the method to save a local cache list as follows: class CashedUserData { static Future<void> _writes = Future.value(); //add list by key reference in local cache static Future<void> addlist(String kPrefKey, List<String> stringList) async{ _writes = _writes.then((void _) => _doAddList(kPrefKey,stringList)); return _writes; } } static Future<void> _doAddList(String kPrefKey, List<String> list) async { List<String> cached = await load(kPrefKey); cached.addAll(list); await (await SharedPreferences.getInstance()).setStringList(kPrefKey, cached); } Then I created another method to retrieve the list, I have also put the appropriate checks here, if empty or null: static Future<List<String>> load(String kPrefKey) async { return (await SharedPreferences.getInstance()).getStringList(kPrefKey) ?? []; } As I used it, I needed to add more methods. Methods to: Clear the list. Enter one string at a time Override the list Check if an element exists Take the first item on the list. Save an item at the top of the list. In this way the class eventually became like this: class CashedUserData { static Future<void> _writes = Future.value(); static Future<void> overrideListData(String kPrefKey, List<String> stringList) async{ _writes = _writes.then((void _) => _doOverrideListData(kPrefKey,stringList)); return _writes; } static Future<void> addlist(String kPrefKey, List<String> stringList) async{ _writes = _writes.then((void _) => _doAddList(kPrefKey,stringList)); return _writes; } static Future<void> save(String kPrefKey, String id) async{ if(await CashedUserData.exist(kPrefKey, id)) return; _writes = _writes.then((void _) => _doSave(kPrefKey,id)); return _writes; } static Future<void> saveAsFirst(String kPrefKey, String id) async{ List<String> actuaList = await load(kPrefKey); if(actuaList!=null && actuaList.length == 0 ) return save(kPrefKey,id); if(actuaList.contains(id)) actuaList.remove(id); if(actuaList.length>1) actuaList.removeAt(0); List<String> newlist = new List(); newlist.add(id); newlist.addAll(actuaList); _writes = _writes.then((void _) => _doOverrideListData(kPrefKey,newlist)); return _writes; } static Future<void> delete(String kPrefKey, String id) { _writes = _writes.then((void _) => _doDelete(kPrefKey,id)); return _writes; } static Future<String> getFirst(String kPrefKey) async { List<String> dataList = (await SharedPreferences.getInstance()).getStringList(kPrefKey); return (dataList!=null && dataList.length>0) ? dataList[0] : ""; } static Future<List<String>> load(String kPrefKey) async { return (await SharedPreferences.getInstance()).getStringList(kPrefKey) ?? []; } static Future<bool> exist(String kPrefKey, String id) async { List<String> userDataList = (await SharedPreferences.getInstance()).getStringList(kPrefKey) ?? []; return (userDataList!=null && userDataList.length > 0) ? userDataList.contains(id) : false; } static Future<void> _doSave(String kPrefKey, String id) async { List<String> cached = await load(kPrefKey); cached.add(id); await (await SharedPreferences.getInstance()).setStringList(kPrefKey, cached); } static Future<void> _doDelete(String kPrefKey, String id) async { List<String> cached = await load(kPrefKey); cached.remove(id); await (await SharedPreferences.getInstance()).setStringList(kPrefKey, cached); } static Future<void> _doAddList(String kPrefKey, List<String> list) async { List<String> cached = await load(kPrefKey); cached.addAll(list); await (await SharedPreferences.getInstance()).setStringList(kPrefKey, cached); } static Future<void> _doOverrideListData(String kPrefKey, List<String> list) async { await (await SharedPreferences.getInstance()).setStringList(kPrefKey, list); } static update(String kPrefKey, String currentItem, String kindlemail) async{ List<String> actuaList = await load(kPrefKey); if(actuaList!=null && actuaList.length > 0 ) { try{actuaList[actuaList.indexOf(currentItem)] = kindlemail;} catch (e) {} } await overrideListData(kPrefKey, actuaList); } } By doing this I simplified the call points. Furthermore, if the plugin shared preference used changed, there would only be one place where to take action.
In questi giorni ho voluto sperimentare e approfondire le conoscenze in ambito di chatbot, ho fatto questa scelta perchè, in questo momento, questa tecnologia è molto utilizzata e molto in crescita. Un pò di teoria I chatbot esistono da tempo, davvero da tanto tempo, risalgono ai primi anni 60/70 con ELIZA e PARRY. Questi due progetti sono nati con lo scopo di creare una conversazione simulata con una macchina. In molti casi, questi due chatbot sono riusciti ad ingannare le persono coinvolte, utilizzando però risposte molto vaghe. Tuttavia, questi due progetti, nonostante fossero classificati come chatbot, risultavano essere molto stupidi, difatto non hanno la stessa concezione del chatbot odierno. Il software che c’era dietro ai vecchi chatbot era molto semplice, possiamo vederlo come una mappatura uno a uno ( domanda -> risposta). Non era presente alcuna interpretazione della domanda. La maggior parte dei Chatbot moderni invece utilizzano il linguaggio naturale che possiamo immaginare con la composizione di tre elementi principali: analisi lessicale (scomposizione frase in token) analisi grammaticale (associazione parti del discorso) analisi sintattica (arrangiamento token in una struttura ad albero) analisi semantica (assegnazione di un significato) Così descritto sembra semplice e noi umani siamo in grado di farlo senza sapere che esistono tutti questi step. Insegnare ad una macchina l’elaborazione degli step sopra elencati è davvero difficile. In particolar modo l’ultimo punto dell’analisi semantica. Oggigiorno I Chatbot più comuni che utilizziamo senza quasi rendercene conto sono Ok Google, Alexa, Siri, Cortona e ogni giorno ne sono presenti sempre di più e di nuovi. Gli ambiti in crescita dove vengono maggiormente utilizzati sono banche, servizio clienti e e-commerce. Ma perchè i chatbot si chiamano con nomi propri di persona? Non ho trovato questa risposta online però credo che la risposta possa essere semplice! Dovendo interagire con qualcosa dalle sembianze umane, quale potrebbe essere il miglior modo per interagire se non quello di attribuire un nome con cui possiamo chiamarlo? Quasi tutte le interazioni con altre persone iniziano con il nome proprio della persona, serve per richiedere la sua attenzione. Cosa c’è dietro al cofano? Ma cosa c’è dietro questi Chatbot? Nel caso di Ok Google, il servizio nelle retroscene è proprio DialogFlow, Questo servizio messo a disposizione di tutti, permette a tutti in modo agile di creare un un vero e proprio chatbot in pochi step e senza alcuna conoscenza di programmazione. Il concetto è molto facile, si basa sulla creazione di Intenti e risposte. Vi spiegherò in questo articolo come utilizzare DialogFlow e come implementarlo usando Flutter. Perchè Flutter? Perchè credo sia molto in crescita al momento. Il progetto sta crescendo così tanto da essere compatibile con tante piattaforme. In particolar modo funziona in maniera nativa su Android e IOS e funziona davvero alla grande. Secondo i test fatti, ha delle prestazioni quasi pari ai linguaggi nativi (Java/Swift/c++). Nonostante sia ancora in Beta, la funzione in modalità web va alla grande e funziona anche per ambienti desktop (linux e Windows). Ma cosa manca? ah già! Tv e Smartwatch! credo però che arriverà a breve anche li il supporto, se ne comincia a parlare online! Mi fai vedere il codice? Non voglio entrare nel dettaglio della configurazione del progetto di dialog flow in quanto ci sono davvero tanti tutorial ben fatti. Do per scontato che abbiate installato Flutter. Se non l’avete ancora fatto, potete partire da questa guida. Dopo aver installato Flutter, visto che la sua concezione è Mobile, se volete compilare anche per web dobbiamo eseguire i seguenti comandi: //abilito la verisone web di flutter flutter channel master flutter upgrade flutter config --enable-web //creo e faccio il run di un nuovo progetto cd into project directory flutter create . flutter run -d chrome Una volta abilitata la versione web di flutter e creato il progetto, possiamo procedere con la modifica del file pubspec.yaml. Questo file contiene le configurazioni e dipendenze ai plugin che utilizziamo nel nostro progetto. Dobbiamo quindi aggiungere le dipendenze a DialogFlow, la versione compatibile sia per web che per mobile è la seguente: flutter_dialogflow: ^0.1.3 assets: - assets/yourapifile.json Dobbiamo anche aggiungere negli asset il file json scaricato da google cloud relativo al service account che ha accesso al progetto di DialogFlow. l’implementazione di dialog Flow richiede davvero poco, giusto qualche riga di codice per l’inizializzazione: dialogFlow.AuthGoogle authGoogle = await dialogFlow.AuthGoogle(fileJson:"assets/yourapifile.json").build(); dialogflow = dialogFlow.Dialogflow(authGoogle: authGoogle); Infine, chiediamo a DialogFlow l’elaborazione dell’intent in modo da ricevere una risposta, con la seguente riga di codice: dialogFlow.AIResponse response = await dialogflow.detectIntent(query); print(response.getMessage()); Potete trovare l’intera implementazione funzionante al seguente link Github. Per poterlo utilizzare, dovrete solo aggiungere la vostra chiave Json del vostro account di servizio, che dovrete creare su google cloud e associare a DialogFlow. schermata del risultato finale:
In these days, I wanted to experiment and deepen my knowledge in the field of chatbot, I made this choice because at this moment, this technology is widely used and really growing. A little theory Chatbots have been around for a long time, really for a long time, they date back to the early 60s70s with ELIZA and PARRY. These two projects were born for the purpose of creating a simulated conversation with a machine. In many cases, these two chatbots have managed to deceive the people involved, but using very vague but intelligent answers. These two projects, however, despite being classified as chatbots, were very stupid, they do not have the same conception of today’s chatbot. The software behind the old chatbots was very simple, we can see it as a one-to-one mapping (question -> answer). There was no interpretation of the question. Most modern Chatbots, on the other hand, use natural language that we can imagine with the composition of three main elements: lexical analysis (decomposition of sentence into token) grammatical analysis (association of parts of speech) syntactic analysis (token arrangement in a tree structure) analysis semantics (assigning a meaning) Thus described it seems simple and we as humans are able to do it without knowing the existence of all these steps. Teaching a machine how to process the steps listed above is really difficult. Especially the last point of the semantic analysis. Nowadays the most common Chatbots that we use without realizing it are Ok Google, Alexa, Siri, Cortona and every day there are more and more new ones. The growing areas where they are most used are banks, customer service and e-commerce. But why are chatbots called by personal names? I have not found this answer online but I believe the answer is simple. As we have to interact with something with human features, what could be the best way to do it, if not to give a name with which we can call it? Almost all interactions with other people begin with the person’s first name, it is needed to request his/her attention. What’s behind the hood? But what’s behind these Chatbots? In the case of Ok Google, the service in the background is just DialogFlow, This service made available to everyone, allows everyone in an agile way to create a real chatbot in a few steps and without any programming knowledge, the concept is very easy, it is basically based on the creation of intents and answers. I will explain in this article how to use DialogFlow and how to implement it using Flutter. Why Flutter? Because I think it is really growing at the moment. The project is growing so much that it is compatible with many platforms. In particular, it works natively on Android and IOS and works really well, according to the tests made, it has performances almost equal to the native languages (Java / Swift / c ++). Although still in Beta, the Web function is going great, and it also works for desktop environments (linux and Windows). What is missing? Just TV and Smartwatch, I believe that the support will arrive shortly! Can you show me the code? I don’t want to go in detail in DialogFlow configuration, there are really many good tutorials that explain the setup and how to use it. I assume that you have Flutter installed. If you haven’t, you can start from this guide. After installing Flutter, since its conception is Mobile, if you want to compile also for web you must execute the following commands: //enable web flutter versione flutter channel master flutter upgrade flutter config --enable-web //new web project and run in chrome cd into project directory flutter create . flutter run -d chrome Once the web version of flutter has been enabled and the project has been created, we can proceed by update the pubspec.yaml file. This file contains the configurations and dependencies to the plugins that we use in our project. We must therefore add the dependencies to DialogFlow, the compatible version for both web and mobile is as follows: flutter_dialogflow: ^0.1.3 assets: - assets/yourapifile.json We must also add in the assets the json file downloaded from google cloud relating to the service account that has access to the DialogFlow project. The implementation of dialog Flow requires very little, just a few lines of initialization code: dialogFlow.AuthGoogle authGoogle = await dialogFlow.AuthGoogle(fileJson:"assets/yourapifile.json").build(); dialogflow = dialogFlow.Dialogflow(authGoogle: authGoogle); Finally we have to ask DialogFlow to process the intent in order to receive a response, do this with the following line of code: dialogFlow.AIResponse response = await dialogflow.detectIntent(query); print(response.getMessage()); The whole working implementation can be found at the following link Github. In order to use it, you only need to add your service account Json key, which you will have to create on Google Cloud and associate it with DialogFlow. final result screen:
Prerequisiti Questo articolo richiede una discreta conoscenza dei seguenti argomenti che potete approfondire ai seguenti link: Firebase Remote Config Flutter Shared Preferences Come mai vi parlo di questi due plugin che sembrerebbero non avere niente a che fare l’uno con l’altro? Con questa guida, vi illustrerò come trarre beneficio da questi due plugin e come risparmiare usando i servizi gratuiti offerti da Firebase! :) Nell’app Italiano con Eli, creata per il canale Italiano con Eli, ho creato un progetto su Firebase, in modo da poter utilizzare il remote config e altre potenzialità che Firebase mette a disposizione. L’app si occupa di mostrare una lista video da canale youtube, se interessati potete scaricare il codice sorgente su github, potete utilizzarlo con qualsiasi canale youtube. Per rendere l’app Italiano con Eli più originale ho pensato di aggiungere ai video delle domande sugli argomenti trattati nei video. Avevo quindi bisogno di un posto dove salvare i dati delle domande. Con Firebase, la cosa più logica e anche più diretta sarebbe quella di usare Realtime Database oppure Firestore. Io ho pensato di utilizzare remote config! Sì, avete capito bene! Remote config! Il primo pensiero che vi sta saltando in mente in questo momento sarà “questo è pazzo!” e il secondo “ma perchè?” condivido pienamente con voi il vostro primo pensiero… c’è un pò di pazzia nella scelta adottata. Per quanto riguarda il perché invece, posso rispondere come segue: non avevo molto tempo a disposizione remote config è gratis e non ha limitazione di utilizzo si può dare accesso al servizio a una persona esterna le altre scelte mi avrebbero portato a dover costruire anche un’interfaccia back end per il censimento delle domande Conoscevo già remote config e sarei stato più rapido nell’implementazione Ma come funziona un servizio non nato per questo lavoro? Funziona benissimo, devo dire che mi ha sorpreso la reattività che dimostra l’app e il sistema che ho messo in piedi. Per farlo funzionare così bene però ho dovuto mettere assieme i due plugin nel modo che vi spiego di seguito. All’interno della console di remote config vado a inserire un nuovo parametro per ogni video. Nel campo nome inserisco l’id del video di youtube e all’interno del parametro un json contenente le domande e le risposte così strutturato: { "Playlist": "PLsrqydfBIVzy9IMGwSQieTVeSWC7cRCnN", "Questions": [ { "question": "How do you say how much is it??: ", "answare": [ "Quanto costa?", "Quanto costano? ", "Quanto costi?" ] }, { "question": "How do you say how much are they?: ", "answare": [ "Quanto costano?", "Quanto costate?", "entrambe/both" ] }, { "question": "Choose the correct option", "answare": [ "Quanto costa questo computer? 300 Euro", "quanto computer ti costa? 300 Euro", "Quanto costano questo computer?" ] }, { "question": "Quanto costa? is", "answare": [ "singolare", "plurale" ] } ] } Inoltre firebase mette a disposizione un inserimento facilitato che effettua anche la validazione del json, questo ci permette di editare più facilmente e non commettere errori. Nell’app Flutter ho poi costruito l’oggetto Quiz che conterrà l’id di youtube, una lista di Domande e la Domanda conterrà una lista di Risposte. Con vari metodi utili di gestione e costruttore per deserializzazione dei dei dati Json come segue: class Answer { final String answerText; final bool isCorrect; Answer(this.answerText, this.isCorrect); } class Question { final String question; final List<Answer> answers; Question(this.question, this.answers); } class Quiz { List<Question> _questions; int _currentIndex = -1; int _score = 0; String _videoId; Quiz.fromJson(String videoId, String jsonInput){ _videoId = videoId; _questions = new List<Question>(); var jsonQuestions = json.decode(jsonInput); jsonQuestions['Questions'].forEach((quest) { List<Answer> answers = new List<Answer>(); bool iscorrect=true; quest['answare'].forEach((ans) { //mettere la prima a true answers.add( new Answer(ans, iscorrect) ); iscorrect=false; }); answers.shuffle(); _questions.add( new Question( quest['question'], answers) ); }); } Quiz(this._questions) { _questions.shuffle(); } List<Question> get questions => _questions; int get length => _questions.length; int get questionNumber => _currentIndex + 1; int get score => _score; Question get nextQuestion { _currentIndex++; if (_currentIndex >= length) return null; return _questions[_currentIndex]; } Question get prevQuestion { _currentIndex--; if (_currentIndex < 0) return null; return _questions[_currentIndex]; } String get correctAnsware { return _questions[_currentIndex].answers.where((x) => x.isCorrect == true).first.answerText; } void answer(bool isCorrect) { if (isCorrect) _score++; } saveScore() async { SharedPreferences preferences = await SharedPreferences.getInstance(); preferences.setInt("OK"+_videoId, this.score); preferences.setInt("KO"+_videoId, this.length - this.score ); //preferences.setString(_videoId, "{'questions':'"+ this.length.toString() +"', 'score':'"+ this.score.toString() +"'}"); } } Per prima cosa, l’App recupera la lista dei video tramite le API youtube, successivamente recupera il relativo json da remote config utilizzando l’id del video e lo passa al costruttore dell’oggetto che lo deserializza ed il gioco è fatto! Una volta ultimata l’app mi sono accorto però che youtube mette a disposizione un numero limitato di chiamate API giornaliere e oltretutto ho pensato che fosse inutile che l’App, ogni volta che viene aperta, debba richiamare le API Youtube. I video oltretutto non cambiano tanto in quanto questo canale produce un nuovo video a settimana. Per risolvere il problema delle chiamate API Youtube e rendere il tutto più reattivo ho deciso di salvare tutto in una lista di stringhe json in cache utilizzando proprio la Shared Preferences. Infine ho aggiunto un parametro nella remote config contente una data. In questo modo ho fatto un meccanismo che mi permette di aggiornare i contenuti nei dispositivi solo se la data è più recente rispetto a quella memorizzata in cache del singolo dispositivo stesso: class YoutubeData { RemoteConfig remoteConfig; String apikey; List<YoutubeDto> allYoutubeVideoList = []; List<YoutubeDto> getHomeList() { return List<YoutubeDto>.from(allYoutubeVideoList.where((item) => item.kind == "video" && item.id != item.channelId )); } List<YoutubeDto> allCategory() { return List<YoutubeDto>.from(allYoutubeVideoList.where((item) => item.kind == "playlist" && item.id != item.channelId )); } List<YoutubeDto> getCategoryVideoList(String plylistID) { return List<YoutubeDto>.from(allYoutubeVideoList.where((item) => item.kind == "video" && item.id != item.channelId && item.playlist == plylistID )); } YoutubeData (this.apikey, this.remoteConfig); checkAndLoad() async { SharedPreferences preferences = await SharedPreferences.getInstance(); List<String> videoIDList = preferences.getStringList("VideoIdList"); DateTime lastUpdateDate,updateDate; String date = remoteConfig.getString("UpdateDatetime"); updateDate = DateTime.parse(date); if(preferences.getString("LastUpdateDatetime")!=null && preferences.getString("LastUpdateDatetime").isNotEmpty) lastUpdateDate = DateTime.parse(preferences.getString("LastUpdateDatetime")); else lastUpdateDate = DateTime(2010); if( (videoIDList==null || videoIDList.length==0) || updateDate.isAfter(lastUpdateDate)) { await loadYtDataAndStoreInSharedPref(); preferences.setString("LastUpdateDatetime", DateTime.now().toString() ); } await loadLocalDataAndAddQuestion(); } loadYtDataAndStoreInSharedPref() async { YoutubeAPI ytApi = new YoutubeAPI(apikey, maxResults: 50); List<YT_API> ytResult = await ytApi.channel( remoteConfig.getString('ChannelId') ); //salviamo il conteggio dei video in shared pref SharedPreferences preferences = await SharedPreferences.getInstance(); preferences.setInt("TotVideo", ytResult.length ); //salviamo json youtube video in shared pref e lista video YoutubeDto youtubedto = new YoutubeDto(); List<String> videoIdList = new List<String>(); for(var result in ytResult) { videoIdList.add( result.id); String json = jsonEncode( youtubedto.yApitoJson(result) ); preferences.setString( result.id, json ); } preferences.setStringList("VideoIdList", videoIdList ); } //aggiungiamo le domande agli oggetti youtube precedentemente salvati loadLocalDataAndAddQuestion() async { SharedPreferences preferences = await SharedPreferences.getInstance(); List<String> videoIDList = preferences.getStringList("VideoIdList"); allYoutubeVideoList = new List<YoutubeDto>(); int cntQuestion=0; for(String videoID in videoIDList) { String jsonYoutube = preferences.getString(videoID); Map userMap = jsonDecode(jsonYoutube); YoutubeDto youtubeDao = new YoutubeDto.fromJson(userMap); youtubeDao.questionJson = remoteConfig.getString( "_" + videoID.replaceAll("-", "_trattino_") ); if( youtubeDao.questionJson != null && youtubeDao.questionJson.isNotEmpty ) { cntQuestion = cntQuestion + 'question'.allMatches(youtubeDao.questionJson).length; youtubeDao.playlist = json.decode( youtubeDao.questionJson)['Playlist']; } allYoutubeVideoList.add(youtubeDao); } preferences.setInt("TotQuestion", cntQuestion ); } } Qui di seguito potete vedere un esempio di come funziona:
Prerequisites This article requires a good knowledge of the following topics that you can study at the following links: Firebase Remote Config Flutter Shared Preferences Why are we talking about these two plugins that would see having nothing in common? i With this guide I will show you how to benefit from this two plugins and how to save money using the free services offered by Firebase! :) In the talian with Eli app, created for the youtube channel Italian with Eli, I created a project on Firebase, so you can use the remote config and other potential that Firebase makes available for you. The app takes care of showing a video list from the youtube channel, if interested you can download the source code on github, you can use it with any youtube channel. To make the Italian with Eli app more original I thought of adding questions to the videos on the topics covered in the videos. So I needed a place to save the question data. With Firebase, the most logical and also most direct thing would be to use Realtime Database or Firestore. I thought of using remote config! Yes you got it right remote config! You will be thinking “this guy is crazy” and “why is he doing that?” I fully share with you this thought… there is a bit of madness in the choice made. The reason why I thought of using remote config is the following: I didn’t have much time available remote config is free and has no limitation of use you can give access to the service to an external person the other choices would have led me to have to also build a back end interface to allow the census of the questions I had already used remote config and I would have been faster in the implementation. But how does a service not born with this scope work? It works very well indeed, I must say that I was surprised by the responsiveness that shows the app and the system that I put up. To make it work so well, however, I had to put the two plugins together in the way that I explain below. Inside the remote config console I’m going to insert a new parameter for each video. In the name field I insert the id of the youtube video and inside the parameter a json containing the questions and answers structured as follows: { "Playlist": "PLsrqydfBIVzy9IMGwSQieTVeSWC7cRCnN", " Questions ": [ { " question ":" How do you say how much is it ??: ", " answare ": [ " How much does it cost? ", " How much does it cost? ", " How much does it cost? " ] }, { "question": "How do you say how much are they ?:", "answare": [ "How much do they cost?", "How much do you cost?", "both / both" ] }, { "question" : "Choose the correct option", "answare": [ "How much does this computer cost? 300 Euros", "How much computer does it cost? 300 Euros", "How much does this computer cost?" ] }, { "question": "How much does it cost? is", "answare": [ "singular", "plural" ] } ] } In addition, firebase provides an easy insertion that also performs json validation, this allows us to edit more easily and not to make mistakes. In the Flutter app I then built the Quiz object which contains the youtube id, a list of Questions and the Question will contain a list of Answers. With various useful management and constructor methods for deserializing Json data as follows: class Answer { final String answerText; final bool isCorrect; Answer (this.answerText, this.isCorrect); } class Question { final String question; final List <Answer> answers; Question (this.question, this.answers); } class Quiz { List <Question> _questions; int _currentIndex = -1; int _score = 0; String _videoId; Quiz.fromJson (String videoId, String jsonInput) { _videoId = videoId; _questions = new List <Question> (); var jsonQuestions = json.decode (jsonInput); jsonQuestions ['Questions']. forEach ((quest) { List <Answer> answers = new List <Answer> (); bool iscorrect = true; quest ['answare']. forEach ((ans) { // put the first to true answers.add (new Answer (ans, iscorrect)); iscorrect = false; }); answers.shuffle (); _questions.add (new Question (quest ['question'], answers)); }); } Quiz (this._questions) { _questions.shuffle (); } List <Question> get questions => _questions; int get length => _questions.length; int get questionNumber => _currentIndex + 1; int get score => _score; Question get nextQuestion { _currentIndex ++; if (_currentIndex> = length) return null; return _questions [_currentIndex]; } Question get prevQuestion { _currentIndex--; if (_currentIndex <0) return null; return _questions [_currentIndex]; } String get correctAnsware { return _questions [_currentIndex] .answers.where ((x) => x.isCorrect == true) .first.answerText; } void answer (bool isCorrect) { if (isCorrect) _score ++; } saveScore () async { SharedPreferences preferences = await SharedPreferences.getInstance (); preferences.setInt ("OK" + _ videoId, this.score); preferences.setInt ("KO" + _ videoId, this.length - this.score); //preferences.setString(_videoId, "{'questions': '" + this.length.toString () + "', 'score': '" + this.score.toString () + "'}"); } } The app first retrieves the list of videos via the youtube API, then retrieves the relative json from remote config using the video id and passes it to the constructor of the object that deserialize and you’re done. Once the app was completed, however, I realized that youtube provides a limited number of daily API calls. Moreover I thought it was useless every time I opened the app that it had to call the Youtube API. The videos, moreover, do not change as much as the channel makes a new video a week. To solve the problem of Youtube API calls and make everything more responsive, I decided to save everything in a list of cached json strings using the Shared Preferences. Finally I added a parameter in the remote config containing a date. In this way I made a mechanism that allows me to update the contents on the devices only if the date is more recent than the one stored in the cache of the single device itself: class YoutubeData { RemoteConfig remoteConfig; String apikey; List <YoutubeDto> allYoutubeVideoList = []; List <YoutubeDto> getHomeList () { return List <YoutubeDto> .from (allYoutubeVideoList.where ((item) => item.kind == "video" && item.id! = Item.channelId)); } List <YoutubeDto> allCategory () { return List <YoutubeDto> .from (allYoutubeVideoList.where ((item) => item.kind == "playlist" && item.id! = Item.channelId)); } List <YoutubeDto> getCategoryVideoList (String plylistID) { return List <YoutubeDto> .from (allYoutubeVideoList.where ((item) => item.kind == "video" && item.id! = Item.channelId && item.playlist == plylistID)); } YoutubeData (this.apikey, this.remoteConfig); checkAndLoad () async { SharedPreferences preferences = await SharedPreferences.getInstance (); List <String> videoIDList = preferences.getStringList ("VideoIdList"); DateTime lastUpdateDate, updateDate; String date = remoteConfig.getString ("UpdateDatetime"); updateDate = DateTime.parse (date); if (preferences.getString ("LastUpdateDatetime")! = null && preferences.getString ("LastUpdateDatetime"). isNotEmpty) lastUpdateDate = DateTime.parse (preferences.getString ("LastUpdateDatetime")); else lastUpdateDate = DateTime (2010); if ((videoIDList == null || videoIDList.length == 0) || updateDate.isAfter (lastUpdateDate)) { await loadYtDataAndStoreInSharedPref (); preferences.setString ("LastUpdateDatetime", DateTime.now (). toString ()); } await loadLocalDataAndAddQuestion (); } loadYtDataAndStoreInSharedPref () async { YoutubeAPI ytApi = new YoutubeAPI (apikey, maxResults: 50); List <YT_API> ytResult = await ytApi.channel (remoteConfig.getString ('ChannelId')); // save the video count in shared pref SharedPreferences preferences = await SharedPreferences.getInstance (); preferences.setInt ("TotVideo", ytResult.length); // save json youtube video in shared pref andvideo list YoutubeDtoyoutubedto = new YoutubeDto (); List <String> videoIdList = new List <String> (); for (var result in ytResult) { videoIdList.add (result.id); String json = jsonEncode (youtubedto.yApitoJson (result)); preferences.setString (result.id, json); } preferences.setStringList ("VideoIdList", videoIdList); } // add the questions to the previously saved youtube objects loadLocalDataAndAddQuestion () async { SharedPreferences preferences = await SharedPreferences.getInstance (); List <String> videoIDList = preferences.getStringList ("VideoIdList"); allYoutubeVideoList = new List <YoutubeDto> (); int cntQuestion = 0; for (String videoID in videoIDList) { String jsonYoutube = preferences.getString (videoID); Map userMap = jsonDecode (jsonYoutube); YoutubeDto youtubeDao = new YoutubeDto.fromJson (userMap); youtubeDao.questionJson = remoteConfig.getString ("_" + videoID.replaceAll ("-", "_trattino_")); if (youtubeDao.questionJson! = null && youtubeDao.questionJson.isNotEmpty) { cntQuestion = cntQuestion + 'question'.allMatches (youtubeDao.questionJson) .length; youtubeDao.playlist = json.decode (youtubeDao.questionJson) ['Playlist']; } allYoutubeVideoList.add (youtubeDao); } preferences.setInt ("TotQuestion", cntQuestion); } } Below you can see an example of how it works:
Il nome scelto da Google questa volta è molto comprensivo ed esaustivo. Firebase remote config è una configurazione salvata in remoto, in questo caso su Google Cloud. La configurazione è accessibile in modifica nella console di Firebase, alla voce di menù Firebase remote config che si trova in fondo, molto in fondo, nell’ultima sezione secondo me un pò nascosta. Prerequisiti: creare un account su Firebase utilizzando il nostro account google creare un app, io ho usato flutter collegare la nostra app al codice sorgente con la chiave json Facendo questo potremmo utilizzare tutti i servizi Firebase, ci sono tanti tutorial online e Firebase stesso mette a disposizione wizard con step per guidarvi alla corretta implementazione di Firebase con la vostra app.Possiamo usare Firebase Remote Config per memorizzare tutto quello che la nostra app necessità come parametri di configurazioni. Per prima cosa, dobbiamo individuare e mettere in configurazione ciò che pensiamo possa cambiare nel tempo, in questo modo avremo delle configurazioni nell’app modificabili in tempo reale da remoto in modo da non dover rieffettuare un nuovo deploy dell’app stessa. Un esempio può essere il caso in cui stiamo utilizzando un Api per recuperare dei dati, l’url base delle api potrebbe cambiare nel tempo. Un altro esempio più concreto che utilizzo io nell’app send to kindle è un parametro che contiene il numero di azioni prima di mostrare una pubblicità. In questo modo se gli utenti si lamentano per le troppe pubblicità posso cambiarlo in tempo reale con il solo cambio di un parametro da interfaccia web a costo zero. Il sistema di Firebase funziona davvero alla grande, c’è dietro un sistema push-lazy che utilizza i dati internet per recuperare le configurazioni solo se davvero abbiamo cambiato qualcosa lato server e quindi abbiamo pubblicato una modifica ad uno o più parametri. L’aggiornamento dei parametri avviene in real time e quindi la modifica sarà subito disponibile nell’app. I parametri possono essere anche associati a delle condizioni. Ad esempio un parametro può essere utilizzato solo per gli utenti di una certa età di un determinato paese o lingua a un determinato orario. Figata no? Vantaggi Controllo da remoto istantaneo, semplice e centralizzato. Modifica App in tempo reale. Diminuisce la necessità di dover effettuare un nuovo deploy. i parametri possono essere combinati con delle condizioni. Come lo uso io nelle mie app? Di seguito voglio mostrarvi un esempio di implementazione. In questo modo ho centralizzato la parte di inizializzazione e creazione dei parametri in modo che siano più accessibili. Ho deciso di utilizzare un singleton in modo da poterlo usare in tutte le view e in tutte le classi per cui ne ho bisogno, implementato nel seguente modo: import 'package:firebase_remote_config/firebase_remote_config.dart'; class Configuration { int _link_sent_before_ads; int _max_send_limit_size = 24000000; int get linkSentBeforeAds => _link_sent_before_ads; int get maxSendLimitSize => _max_send_limit_size; int get maxSendLimitSizeMb => (_max_send_limit_size/1000000).round(); static Configuration get instance => _instance; static final Configuration _instance = Configuration._privateConstructor(); Configuration._privateConstructor() { //initializeRemoteConfig(); } loadConfig(RemoteConfig _remoteConfig) async { _link_sent_before_ads = _remoteConfig.getString('link_sent_before_ads').isEmpty ? _link_sent_before_ads : int.tryParse(_remoteConfig.getString('link_sent_before_ads')); _max_send_limit_size = _remoteConfig.getString('max_send_limit_size').isEmpty ? _max_send_limit_size : int.tryParse(_remoteConfig.getString('max_send_limit_size')); } initializeRemoteConfig() async{ RemoteConfig remoteConfig = await RemoteConfig.instance; try { //set some default params in case of crash await remoteConfig.setConfigSettings(RemoteConfigSettings(debugMode: false)); await remoteConfig.setDefaults(<String, dynamic>{ 'service_api_conversion_1': 'https://ebook-converter.app/calibre/ebook-convert', }); // Using default duration to force fetching from remote server. await remoteConfig.fetch(expiration: const Duration(seconds: 0)); await remoteConfig.activateFetched(); } on FetchThrottledException catch (exception) { // Fetch throttled. print(exception); } catch (exception) { print('Unable to fetch remote config. Cached or default values will be used'); } await this.loadConfig(remoteConfig); } } potrà essere infine utilizzato molto semplicemente nel seguente modo: //call this just first time in app initialization await Configuration.instance.initializeRemoteConfig(); Print(Configuration.instance.maxSendLimitSize);
What is shared_preferences where, when and how much to use it? The shared_preference is a plugin that allows you to save data app dedicated memory, the folder that will contain this data is named in Android with the package name of your app, e.g. com.companyname.appname. The data that we can store in this space with this plugin are data of these types: String Integer Bool List It should be noted that these parameters are not persistent, they remain in the area dedicated to the app therefore are eliminated when the app is uninstalled or if the app cache data is removed. So, in general it is right to store non-essential preference data so that it is not a tragedy if they have been lost. A classic example can be the app theme color light or dark or the order of menu title. How to use? Saving: import 'package:shared_preferences/shared_preferences.dart'; SharedPreferences _preferences = await SharedPreferences.getInstance(); _preferences.setString("theme", "dark"); _preferences.setInt("countaccess", 2); Recovery: import 'package:shared_preferences/shared_preferences.dart'; SharedPreferences _preferences = await SharedPreferences.getInstance(); _preferences.getString("theme"); _preferences.getInt("countaccess"); At first we might think that it is of little use since in a string we cannot store a lot of information. In fact, if we think about it in a string, we can also store a lot but really a lot by using a json, below an example: import 'package:shared_preferences/shared_preferences.dart'; SharedPreferences _preferences = await SharedPreferences.getInstance(); _preferences.setString("{"name":"Andrea","surname":"Luciano","age":31,"sex":"M","SurnameCouldBeName":true}"); In this example I put SurnameCouldBeName as a field because my last name is also a first name. All those who don’t know me always ask me “Luciano is your name or surname?” And every time I would like to do something like that: We can therefore create a collection of people using a list of strings instead of the single string as follows: import 'package:shared_preferences/shared_preferences.dart'; SharedPreferences _preferences = await SharedPreferences.getInstance(); List<String> users = new List<String>(); users.add("{"name":"Andrea","surname":"Luciano","age":31,"sex":"M","SurnameCouldBeName":true}"); users.add("{"name":"elisa","surname":"luciano","age":32,"sex":"F","SurnameCouldBeName":false}"); _preferences.setStringList("users", users); This could finally be mapped into an object in order to access the data simply and finally show it to the interface: Class UserDto() { String name, surname, age, sex, SurnameCouldBeName; UserDto.fromJson(Map<String, dynamic> json) : name= json[name], surname= json[surname], age= json[age], sex= json[sex], SurnameCouldBeName = json[SurnameCouldBeName]; } SharedPreferences _preferences = await SharedPreferences.getInstance(); List<UserDto> UserObj = new List<UserDto>; List<String> users = _preferences.getStringList("users"); for(String userjson in users) { Map userMap = jsonDecode(userjson); UserDto = new UserDto.fromJson(userMap); print(UserDto.name); UserObj.add(UserDto); } Think about the possible benefits and applications of this. It can be used to store data returned by an API. In this way, the application would be more efficient and also can work offline without therefore having to use network data for recovering data from the server.
Che cos’è la shared_preferences dove, quando e quanto utilizzarla? La shared_preference è un plugin che vi permette di salvare dei dati in memoria dedicata all’app, la cartella che li conterrà prende il nome in android con il nome del pacchetto della vostra app es com.nomeazienda.nomeapp. I dati che possiamo memorizzare all’interno di questa memoria con questo plugin sono dei seguenti tipi: string intero bool List E’ da notare che questi parametri non sono persistenti, rimangono nell’area dedicata all’app e quindi vengono eliminate nel momento in cui l’app viene disinstallata oppure se vengono rimossi i dati della cache dell’app. Quindi in genere è doveroso memorizzare dati di preferenze non indispensabili in modo che non sia una tragedia se dovessero essere persi. Un esempio classico può essere come voglio il tema light o dark oppure l’ordine degli oggetti nel menu. Come si usa? Salvataggio: import 'package:shared_preferences/shared_preferences.dart'; SharedPreferences _preferences = await SharedPreferences.getInstance(); _preferences.setString("theme", "dark"); _preferences.setInt("countaccess", 2); Recupero: import 'package:shared_preferences/shared_preferences.dart'; SharedPreferences _preferences = await SharedPreferences.getInstance(); _preferences.getString("theme"); _preferences.getInt("countaccess"); In un primo momento potremmo pensare che è una cosa che serve a poco in quanto in una stringa non possiamo memorizzare tante informazioni. In realtà, se ci pensiamo bene, in una stringa possiamo anche memorizzare molto ma davvero molto semplicemente utilizzando un json, di seguito esempio: import 'package:shared_preferences/shared_preferences.dart'; SharedPreferences _preferences = await SharedPreferences.getInstance(); _preferences.setString("{"name":"Andrea","surname":"Luciano","age":31,"sex":"M","SurnameCouldBeName":true}"); In questo esempio ho messo come campo SurnameCouldBeName in quanto il mio cognome è anche un nome. Tutti quelli che non mi conoscono mi chiedono sempre “Luciano è il nome o il cognome?” E ogni volta vorrei fare qualcosa del genere: Possiamo quindi creare una collezione di persone utilizzando, al posto della singola stringa, una lista di stringhe come segue: import 'package:shared_preferences/shared_preferences.dart'; SharedPreferences _preferences = await SharedPreferences.getInstance(); List<String> users = new List<String>(); users.add("{"name":"Andrea","surname":"Luciano","age":31,"sex":"M","SurnameCouldBeName":true}"); users.add("{"name":"elisa","surname":"luciano","age":32,"sex":"F","SurnameCouldBeName":false}"); _preferences.setStringList("users", users); Questo infine potrebbe essere mappato in un oggetto in modo da accedere ai dati più semplicemente ed infine mostrarli nell’interfaccia: Class UserDto() { String name, surname, age, sex, SurnameCouldBeName; UserDto.fromJson(Map<String, dynamic> json) : name= json[name], surname= json[surname], age= json[age], sex= json[sex], SurnameCouldBeName = json[SurnameCouldBeName]; } SharedPreferences _preferences = await SharedPreferences.getInstance(); List<UserDto> UserObj = new List<UserDto>; List<String> users = _preferences.getStringList("users"); for(String userjson in users) { Map userMap = jsonDecode(userjson); UserDto = new UserDto.fromJson(userMap); print(UserDto.name); UserObj.add(UserDto); } Pensate ai possibili vantaggi e alle possibili applicazioni. Può essere utilizzato per memorizzare dati restituiti da un API. Con questo approccio, l’applicazione è più efficiente e potrebbe funzionare anche offline senza quindi dover utilizzare sempre la rete dati per recuperare i dati dal server.
The name chosen by Google this time is very comprehensive and exhaustive. Firebase remote config is a remotely saved configuration, in this case on Google Cloud. This configuration is accessible to insert and update in the Firebase console, under the menu item Firebase remote config which is located at the very bottom, in the last section, in my opinion a little hidden. Prerequisites: create an account on Firebase using our google account create an app, I used flutter connect our app to the source code using json key file By doing this we could use all the Firebase services, there are many online tutorials and Firebase itself provides wizards with steps to guide you to the correct implementation of Firebase with your app. We can use Firebase Remote Config to store everything our app needs as configuration parameters. First of all, We must identify and configure what we think will change over time. In this way we will have configurations in the app that can be modified in real time remotely so as not to have to carry out a new app deployment. An example may be the case in which we are using an API to recover data, the base url may change over time. Another more down to earth-example that I use in the send to kindle app is a parameter that contains the number of actions before showing an advertisement. In this way, if users complain about too many advertisements, I can change it in real time with just changing a parameter into the web interface at no cost. The Firebase system works really well, there is a push-lazy system behind it that uses internet data to retrieve the configurations only if we have really changed something on the server side and therefore we have published a change to one or more parameters. The updating of the parameters takes place in real time so it changes immediately the behavior, state or data in app as real time. Parameters can also be associated with conditions. For example, a parameter can only be used for users of a certain age in a certain country or language at a specific time. Cool! isn’t it? Advantages Instant and simple remote control. Real-time app customization. It decreases the need for a new deployment. Parameters can be combined with conditions. How do I use it in my apps? Below I want to show you an example of implementation. In this way I centralized the initialisation and parameter creation part so that they are more accessible. I decided to use a singleton so that I can use it in all the views and all the classes I need it, implemented in the following way: import 'package:firebase_remote_config/firebase_remote_config.dart'; class Configuration { int _link_sent_before_ads; int _max_send_limit_size = 24000000; int get linkSentBeforeAds => _link_sent_before_ads; int get maxSendLimitSize => _max_send_limit_size; int get maxSendLimitSizeMb => (_max_send_limit_size/1000000).round(); static Configuration get instance => _instance; static final Configuration _instance = Configuration._privateConstructor(); Configuration._privateConstructor() { //initializeRemoteConfig(); } loadConfig(RemoteConfig _remoteConfig) async { _link_sent_before_ads = _remoteConfig.getString('link_sent_before_ads').isEmpty ? _link_sent_before_ads : int.tryParse(_remoteConfig.getString('link_sent_before_ads')); _max_send_limit_size = _remoteConfig.getString('max_send_limit_size').isEmpty ? _max_send_limit_size : int.tryParse(_remoteConfig.getString('max_send_limit_size')); } initializeRemoteConfig() async{ RemoteConfig remoteConfig = await RemoteConfig.instance; try { //set some default params in case of crash await remoteConfig.setConfigSettings(RemoteConfigSettings(debugMode: false)); await remoteConfig.setDefaults(<String, dynamic>{ 'service_api_conversion_1': 'https://ebook-converter.app/calibre/ebook-convert', }); // Using default duration to force fetching from remote server. await remoteConfig.fetch(expiration: const Duration(seconds: 0)); await remoteConfig.activateFetched(); } on FetchThrottledException catch (exception) { // Fetch throttled. print(exception); } catch (exception) { print('Unable to fetch remote config. Cached or default values will be used'); } await this.loadConfig(remoteConfig); } } It can finally be used very simply in the following way: //call this just first time in app initialization await Configuration.instance.initializeRemoteConfig(); Print(Configuration.instance.maxSendLimitSize);
Theory Flutter is a new language that has recently come out from the beta, implemented by Google on the basis of Dartlanguage less known today than FLutter. It is a modern language but based on Dart, the syntax is very similar to any other modern object language. The main concept on which it is based is Everything is a widget. But what does it really mean? All we are dealing with when developing in Flutter is a widget. A widget may or may not have a status, which can determine its content or form. It could be seen as an object as you imagine it with the difference that it can have a state to update the view and it can contain contents and shapes. Yes, you have understood shapes correctly, since a widget, besides defining properties and methods, can define shapes. Finally, a widget can include or extend another widget. So an app in flutter is the composition of the widgets together. An example could be a form containing n input text widgets etc. and a button also widget. The main widget that we can make are of two types, at least for now before google upsets everything, and are as follows: Widgets stateless (statelesswidget) are immutable, like a static class. A button that never changes color can be defined as a static widget. It can also be used in case we need to show only non-interactive descriptions. Stateful widgets (statefulwidget): one (or more) property related to them, changing, can change the behavior, appearance or functionality of the widget. An app screen that changes based on content in an http response is a widget with status (just because the screen content varies based on the http response). Example: in case we need to request data from users and then update the same widget to validate or show an ok data entered correctly. To update the widget you need to use the setstate with the property updated so that the change is extended. Consideration The thing I liked most about Flutter: that we can develop everything through a single language. the livereload works really well and allows us to write the code faster. with single environment and same code we have a build for Android and IOS as Native Recently flutter can also be used in web and desktop (Linux & Windows) :) At the beginning I was a little skeptical about using widgets and it was a little complicated and dispersed to understand the advantages. After using them I must say that they are simpler than they seem to be implemented and thanks to this approach it will be easier to reason according to the best practice “DRY” (Don’t Repeat Yourself). On the one hand it adds an incredible nesting during the construction phase of the interface by boxing one widget into another and so on; however it has a logic that is modular in nature and this is excellent for decoupling small widgets that will consist of their properties and their tasks. By decoupling, we can test them and use them with different widgets to make more complex widgets. In this way the code will be more maintainable and functional.
Teoria Flutter è un nuovo linguaggio uscito da non molto dalla beta, implementato da google sulla base del linguaggio Dart, meno noto ad oggi di FLutter. E’ un linguaggio moderno ma basandosi su Dart, la sintassi è molto simile a qualsiasi altro linguaggio moderno ad oggetti. Il concetto principale su cui si basa è Everything is a widget. Ma cosa vuol dire davvero? Tutto ciò con cui abbiamo a che fare sviluppando in Flutter è un widget. Un widget può avere o meno uno stato, che ne può determinare contenuto o forma. Potrebbe essere visto come un oggetto come lo immaginate con la differenza che può avere uno stato per aggiornare la view e può contenere contenuti e forme. Sì, avete capito bene! forme in quanto un widget oltre a definire proprietà e metodi, può definire forme. Infine un widget può includere o estendere un altro widget. Quindi un’applicazione in flutter è la composizione dei widget insieme. Un esempio può essere un form che contiene un widget di input text ecc. e un botton anch’esso widget. I widget principali che possiamo realizzare sono di due tipi, almeno per adesso prima che google sconvolga tutto, e sono i seguenti: Widget privi di stato (statelesswidget): sono immutabili, come una classe statico. Un bottone che non cambia mai colore può essere definito come uno widget statico, può essere utilizzato anche nel caso abbiamo bisogno di mostrare solo descrizioni non interattive. Widget dotati di stato (statefulwidget): una (o più) proprietà ad essi afferente, cambiando, può mutare il comportamento, le sembianze o le funzionalità del widget. Una schermata dell’app che cambia in base al contenuto in una risposta http è un widget dotato di stato (proprio perché il contenuto della schermata varia in base alla risposta http). Esempio: nel caso in cui dobbiamo richiedere dei dati agli utenti e poi riaggiornare lo stesso widget per validare o mostrare un ok dati inseriti correttamente. Per aggiornare il widget è necessario utilizzare il setstate con la proprietà aggiornata in modo che venga prorogata la modifica. Considerazioni La cosa che ho apprezzato di più di Flutter: che possiamo sviluppare tutto tramite un unico linguaggio. il live reload funziona davvero bene e ci permette di scrivere il codice più velocemente. Dobbiamo scrivere e manutenere un’ unica linea di codice fruibile su Android e IOS come nativo. da poco con la stessa linea di codice è possibile fare il build anche per web e desktop (Linux e Windows) :) All’inizio ero un pò scettico nell’utilizzare i widget ed è stato un pò complicato e dispersivo comprenderne i vantaggi. Tuttavia, dopo averli utilizzati devo dire che sono più semplici di quello che sembrano da implementare e grazie a questo approccio sarà più semplice ragionare secondo la best practice “DRY” (Don’t Repeat Yourself). Da un lato aggiunge una nidificazione incredibile in fase di costruzione dell’interfaccia iscatolando un widget dentro un altro e così via, dall’altro ha una logica che di sua natura è modulare e ciò è ottimo per disaccoppiare piccoli widget che saranno consistenti delle loro proprietà e i loro compiti. Disaccoppiando, possiamo testarli e usarli con diversi widget per fare widget più complessi. In questo modo il codice sarà più manutenibile e funzionale.
Quando abbiamo un’idea per un’App, ci sono tante domande che dobbiamo porci: E’ già presente sul mercato? Potrebbe funzionare? Quanto ci costa svilupparla? Quanto possiamo guadagnarci? Come facciamo a monetizzare da questa idea? In questo articolo voglio parlarvi di come monetizzare. Ci sono tante modalità e tanti strumenti che ci permettono di monetizzare, voglio riportare qui la mia esperienza, i miei errori e i suggerimenti su come utilizzare al meglio questi strumenti. Quando ho cominciato, non avevo per niente le idee chiare. Il mio obiettivo principale non era quello di guadagnarci ma di mettere in piedi un servizio auto-sostenibile, che non avesse costi o comunque fossero molto vicini allo zero, in modo da poter permettermi di vendere il servizio ad un prezzo molto basso. Primo step La prima cosa in assoluto da fare è individuare il nostro pubblico potenziale, per farlo vi consiglio di utilizzare Google adWords oppure facebook Ads. Questi strumenti sono ottimi per capire quale potrebbe essere il nostro pubblico potenziale. Per farlo dobbiamo inserire le parole chiavi riguardo le “specialità” della nostra App, selezionare il range di età e i paesi coinvolti. Il risultato che otteniamo da questa analisi è il numero dei potenziali utilizzatori totali e il paese con più percentuale di utilizzo. Quest’analisi potrebbe aiutarci ad individuare i nostri concorrenti, se esistono, e capire il loro pubblico e cosa ci distingue. Strumenti di guadagno Stabiliamo la modalità di monetizzazione. Ce ne sono davvero di svariate e dipende anche molto dal nostro tipo di business, però le principali più utilizzate e più adattabili nel mondo delle app credo possano essere le seguenti: Pubblicità Prodotti e Abbonamenti in app Vendita e analisi dati Pubblicità: Sebbene Google, uno dei più grandi colossi web al mondo, guadagni solo o quasi con la pubblicità, quest’ultima non è secondo me un ottimo strumento da utilizzare in tutti i contesti in quanto presenta i seguenti problemi: non è adeguata nei momenti iniziali Il guadagno è molto basso a meno che non si posseggano davvero tanti utenti Il prodotto deve attrarre e coinvolgere l’utente per un tempo medio lungo Rischia di essere troppo fastidiosa e potrebbe rovinare l’esperienza utente La pubblicità più semplice da implementare con Flutter guarda caso è la stessa di google, quindi Google AdMob. Vendita di prodotti: se decidi di mettere in vendita la tua app, hai a disposizione attraverso la Google play console diverse possibilità di vendita tra cui: Prezzo download App Ci permette di stabilire un prezzo per il download dell’app. Questa è la cosa più semplice da implementare, in quanto basta impostare il prezzo dell’app all’interno della console. Una volta fatto, abbiamo terminato! a meno che non si voglia mettere un ulteriore controllo all’interno dell’app, per evitare che vengano create delle copie pirate della nostra app e non la compri più nessuno. Secondo me questa modalità di vendita è ormai superata, la gente non si fida a comprare al buio, sebbene può sempre richiedere il rimborso ;credo comunque che imporre un prezzo sin dall’inizio sia un ostacolo. Sicuramente per alcune app ha senso, spesso viene utilizzato per la versione pro dell’app. Prodotti in App questa tipologia di prodotto dà accesso all’applicazione in versione gratuita e mette a disposizione alcuni prodotti all’interno dell’app stessa. In questo caso però oltre a definire i vari prodotti, scegliere e impostare i vari prezzi, dobbiamo anche sviluppare un pezzo nella nostra app per recuperare i prodotti da google play store per poter mostrare i prodotti. Anche se sono a disposizione librerie e plugin, prevede comunque un nostro sviluppo per poterlo utilizzare. Abbonamenti in App Gli abbonamenti sono molto simili ai prodotti con la differenza sostanziale che hanno una scadenza quindi hanno una validità temporale. Differenze tra Prodotti e Abbonamenti Il prodotto in App può essere utilizzato solo una volta, consumato subito, oppure potrebbe essere utilizzato per sempre, dipende dal valore che noi decidiamo di attribuirgli nella nostra app. Una volta comprato sblocca qualcosa. L’abbonamento invece ha una scadenza specifica, quindi comprende lo stesso comportamento del prodotto base ma ha una durata che può essere settimanale, mensile, trimestrale e annuale. Quanto davvero si guadagna dalla vendita su google play store? Questa è una cosa che all’inizio non è nota a tutti. Come nella vita normale, quando ci sono di mezzo i soldi, tutti ci devono guadagnare. Per tutti in questo caso intendo Google e lo stato. Google mette a disposizione: una vetrina uno store gestione rimborsi gestione prezzi normalizzazione valute tasse in tutti i Paesi del mondo. e tante altre cose Sebbene questo servizio sia per il 95% automatico, s’ha da pagare. Ma la cosa interessante e curiosa è che la percentuale che prende Google è di circa il 30% del prezzo della nostra App. la percentuale invece dovuta allo stato dipende dal Paese di riferimento dell’acquisto. Esempio: se vendiamo un’App dal costo di 2 euro, il nostro guadagno netto effettivo sarà di 1 euro circa. Invece. nel caso di Abbonamento, la percentuale spettante a Google, guarda caso cambia e scende al 15%. Ma perchè Google ci invoglia a vendere abbonamenti? Non ci vuole tanto ad arrivarci, il prodotto una volta venduto ha terminato il suo guadagno. L’abbonamento invece è per sempre. Stabiliamo il prezzo Non è semplice stabilire il prezzo giusto. E’ molto importante sceglierlo adeguatamente. Io ho fatto tanti errori in fase di scelta e credo che sia ancora sbagliato. Però l’errore principale che ho commesso, e credo commettano in molti, è il fatto che sottovalutiamo il prodotto che abbiamo sviluppato. Lavorandoci da tanto e conoscendolo bene, tendiamo a sottovalutare. Dalla mia esperienza ho capito che il modo giusto per definirlo è moltiplicare per 3 o per 4 il prezzo che io stesso sarei disposto a pagare. Google play console permette di inserire diversi prezzi per ogni paese, sebbene alcuni paesi siano più ricchi e più disposti a pagare per un’App, io ho deciso di mettere per tutti lo stesso prezzo, nel mio caso credo sia giusto. Anche perché si tratta di un prezzo molto basso. Inoltre l’app send to kindle è destinata ai possessori di un kindle che quindi, avendo comprato un kindle, credo possano essere disposti a pagare qualche euro per una comoda funzionalità. Definiamo la strategia Questo è il punto più complicato e dipende molto dal tipo di app o servizio che abbiamo implementato. Io per definirla in send to kindle, ho fatto questo ragionamento: Posso guadagnare solo con la pubblicità? Sebbene il mio pubblico stia crescendo e può diventare molto ampio, per adesso la risposta è no e credo lo sarà per molto. Perchè? Ho deciso di rendere l’app maledettamente veloce e quindi l’interazione utente è troppo breve. L’utente entra, invia il contenuto al kindle e ha finito. Non ha altro da fare nella mia app. Quindi le scelte che avrei potuto seguire erano le seguenti: lavorare per proporre qualcos’altro legato al contesto utilizzare uno strumento diverso rispetto alla pubblicità Alla fine ho deciso di non usare la pubblicità come guadagno principale. Ma allora, perchè l’ho usata lo stesso? Ho usato la pubblicità come elemento di fastidio e noia. Sì, avete capito bene noia, ho creato un sistema in modo che ad ogni tre invii si è costretti a guardare un video, per poter proseguire. La gente può continuare a guardare video, ma prima o poi si annoierà così tanto di guardare video pubblicitari che deciderà di valutare la rimozione della pubblicità. Oltre alla conversione degli articoli, send to kindle converte anche pdf e diversi formati di ebook. Di recente ho deciso anche di introdurre un abbonamento per questo che consente di effettuare un numero infinito di conversioni per un anno. Ho deciso di mettere un abbonamento perché la conversione utilizza un container in cloud ed ho dei costi per il servizio cloud da pagare ogni mese. In questo modo se i miei utenti dovessero sottoscrivere l’abbonamento, riceverò qualcosa ogni anno che spero possa essere utile ai fini di pagare il servizio in cloud e guadagnarci anche qualcosa. Finchè troverete l’app nello store vuol dire che sta funzionando ;) Considerazioni Oggigiorno quasi tutti cercano di proporre un servizio e contestualmente un abbonamento. Questo perchè a lungo andare è il più profittevole. Secondo me ogni app e ogni sistema necessita delle giuste considerazioni e scelte. Credo che tutti oggi possano comprendere e sono più propensi ad acquistare un oggetto fatto e finito anziché sottoscrivere un abbonamento. L’abbonamento non è visto molto bene da tutti, inoltre va motivato bene e bisogna ragionare bene su come venderlo e far comprendere tutti i vantaggi che si ottengono. Comunque non vi fasciate troppo la testa, il prezzo non è per sempre. Google play console, permette di cambiare il prezzo in ogni momento. Se uno lavora bene e fa crescere il suo servizio/app, il valore di quest’ultima cresce quindi è giusto aumentare il prezzo e aggiungere nuove funzionalità che aprono le porte a nuovi guadagni.
When we have an idea for an App, there are many questions that come to our mind, the first ones are: Is this app already on the market? Can this idea work? How much does it cost to develop it? How much can we earn? How to monetize this idea? In this article I want to talk about how to monetize. There are many ways and many tools that allow us to monetize, I want to report here my experience, my mistakes and suggestions on how to use these tools at the best. When I started, I had no clear idea. My main goal was not to earn money but to set up a self-sustainable service, which had no costs or costs very close to zero, so that I could afford to sell the service at a low price. First step The first thing to do is to identify our potential audience, to do this I recommend using Google adWords or Facebook Ads. These tools are great to understand what our potential audience might be. To do this we must enter the keywords of our App services, select the age range and the countries involved. The result we get from this analysis is the total number of potential users and the country with the highest percentage of use. It could help us identify our competitors and understand what is their audience like and what sets us apart. Earning tools It is time to establish our monetization method; there are really many different ways and choosing one of them depends on our type of business. However, the most used ones and the most adaptable in the world of Mobile Apps are the following: Advertising Products and Subscriptions in the app Sales and data analysis Advertising: Although one of the largest web giants in the world Google earns only or almost with advertising, the latter is not, in my opinion, a good tool to be used in all contexts, I believe it has the following problems: It is not appropriate at the beginning The profit is very low unless you have many users The product must attract and involve the user for some time. There is the risk of being too annoying and could ruin the user experience. The simplest advertising to implement with Flutter is the same used in google, therefore Google AdMob. Sale of products: if you decide to put your App on sale, you have various sales options available through the Google play console, including: **The price related to the App download ** This allows us to set a price for the App download. This is the simplest thing to implement, as we only have to just set the price of the app inside the console. Once done, we can be over with it unless we want to put a further check inside the app to prevent people from creating pirated copies of our app and from buying it. In my opinion setting a price from the beginning is now outdated, people do not trust buying in the dark. Although they can always ask for a refund, I think it is still an obstacle to impose a price from the beginning. It certainly makes sense for some apps, it is often used for the pro version of the app. Products in the App This type of product gives access to a free version of the application and makes the products available within the app itself. In this case, however, in addition to defining the various products, choosing and setting the various prices, we must also develop a piece in our app to retrieve the products from the Google Play Store in order to be able to show the products. Although libraries and plugins are available, we still have to develop it in order to use it. Subscriptions in the App Subscriptions are very similar to products with the difference that they have an expiring date and therefore have a temporal validity. Differences between Products and Subscriptions The product in the App can be used only once, expended immediately, or it could be used forever: it actually depends on the value that we decide to assign in our app. Once bought, it unlocks something. The subscription instead has a specific deadline, therefore it includes the same behavior as the base product but has a duration that can be weekly, monthly, three-monthly and annual. How much do you really earn from selling on google play store? This is something that is not known at first. As in normal life, when money is involved, everyone has to earn it. By “everyone"in this case I mean Google and the state. Google offers: a showcase a store refund management price management currency normalization taxes in all countries of the world. And lot of other stuff Although the 95% of this service is completely automatic, it has to be paid. But the interesting thing is that the percentage that Google takes is about 30% of the price of our app. the percentage due to the State instead depends on the country of reference of the purchase. Example: if we sell an app with a cost of 2 euros, our actual net profit will be around 1 Euro. Instead if we subscribe the percentage due to Google changes and drops to 15%. But why does Google encourages us to sell subscriptions? It does not take long to get there, once we have sold the product, we won’t have any further earnings. The subscription instead is forever. We establish the price It is not easy to establish the right price but it is very important to choose it properly. The main mistake I made, and I believe many people commit, is the fact that we underestimate the product we have developed. Since we have been working on it for a long time and know it well, we tend to underestimate it. From my experience, I understood that the right way to define it is to multiply by 3 or 4 the price that I would be willing to pay. Google play console allows you to enter different prices for each country, although some countries are richer and more willing to pay for an App, I decided to put the same price for everyone, in my case I think it’s right. Also because it is a very low price. In addition, the App send to kindle is intended for the owners of a kindle who therefore can be willing to pay a few euros for a convenient feature. We define the strategy This I think is the most complicated part and depends a lot on the type of app or service that we have implemented. Consider send to kindle I can ask myself this question: Can I earn only with advertising? Although my audience is growing and can become very large for now, the answer is no and I believe it will be no also in the future. Why? I decided to make the app bloody fast and therefore the user interaction is too short. The user enters, sends the content to the kindle and he is done. He has nothing else to do in my app. So the choices I could follow were the following: Suggest something else related to the context Use a different tool rather than advertising. In the end I decided not to use advertising as the main income. So why did I use it anyway? I used advertising as an element of annoyance and boredom. Yes you got it right! boredom, I created a system in a way that every three submissions, you are forced to watch an advert video, in order to continue. People can continue watching videos, but sooner or later they will get so bored with watching advertising videos that they will decide to consider removing the advertisements. In addition to converting articles, send to kindle also converts pdfs and various ebook formats. I recently decided to introduce a subscription for this. It allows you to perform an infinite number of conversions for a year. I decided to put a subscription because the conversion uses a container in the cloud and I have costs for the cloud service to be paid every month. In this way, if my users had to subscribe, I would receive something every year which, I hope, will be useful in order to pay for the cloud service and also earn something. As long as you’ll find it in the app store means it’s working;) Considerations In these days almost everyone is trying to offer a service using a subscription. This is because in the long run it is the most profitable. In my opinion, every app and every system need the right considerations and choices. I think a lot of people today can understand and are more likely to buy a product on time rather than signing up for a subscription. People don’t like the subscription, moreover it must be well motivated and you have to think about how to sell it and make everyone understand the advantages that they get. However, do not bandage your head too much, the price is not forever. Google play console, allows you to change the price at any time. If the person works well and the app grows, the value of the latter grows so it is correct to increase the price and add new features that open new doors to new earnings.
Ricerca Eccoci qua finalmente! In questi giorni ho sentito la necessità di creare questo blog che state proprio visitando! Mentre navigavo on line, sono venuto a conoscenza degli static site generator; ho sempre saputo della loro esistenza ma non avevo mai dedicato loro molta importanza. Tuttavia, navigando più a fondo, sono stato attratto da Hugo, uno fra i tanti nuovi motori per generare website statici. No Wordpres Partivo dall’esigenza di avere qualcosa di statico in quanto non dispongo di un server dove installare Wordpress o altri CMS e, per un piccolo blog personale, non credo sia necessario e oltretutto è troppo costoso. Infine, volevo che il blog fosse efficiente e scalabile e cross platform. DRY La prima cosa a cui ho pensato da programmatore è stata: quante operazioni dovrò fare per eseguire un deploy senza un CMS? come farò a portare i miei nuovi contenuti online? Sono anche un’amante di Google Docs e mi piace scrivere ed avere tutto in un unico posto. Allora mi sono chiesto: come posso unire Hugo e Google Docs senza dover girare mille manovelle? Innanzitutto, ho studiato la struttura dei contenuti MarkDown e mi sono messo alla ricerca di qualcosa che mi desse la possibilità di convertire i Documenti Google in MarkDown. La cosa più utile che ho trovato sul web è stato questo Google script mangini/gdocs2md: Convert a Google Drive Document to the Markdown format, suitable for publishing. a cui faccio i miei più sentiti ringraziamenti. Nonostante la comprovata efficacia del suddetto script, avevo bisogno di qualcosa in più rispetto al ricevere la conversione di un articolo via mail. Di conseguenza, per avere qualcosa di più automatizzato, ho modificato un pò lo script e creato il link che vedi sotto: Link allo script su github Lo script converte tutti i documenti elencati in un foglio Google, organizza in cartelle per categorie, estrae immagini da articoli, aggiunge l’intestazione del MarkDown, i tags ed infine crea un file zip contenente tutti i contenuti. Ho deciso di impostare lo zip con visibilità pubblica quindi scaricabile, pronto per il deploy. Ho successivamente creato una sorta di database utilizzando uno sheet di Google come se fosse una tabella con la seguente struttura: ToDeploy Title Summary SummaryImage Category Data tag1 tag2 tag3 tag4 tag5 Language 1 Come Inviare File PDF e articoli web al Kindle da Android 0 Kindle 2020-05-09 kindle pdf articolo web send to kindle android it In questo modo ho la possibilità di fare il deploy di tutto o di aggiornare solo ciò che ho modificato utilizzando il primo campo ToDeploy. Successivamente, ho creato un altro google script che riceve in ingresso l’id della cartella su cui è presente lo zip precedentemente creato e restituisce l’url pubblico da cui effettuare il download dello zip con i contenuti. function doGet(e) { params = JSON.parse(JSON.stringify(e)); var docid = params.parameters.docid; if(docid==null || docid=="") return; var parentFolder=DriveApp.getFolderById(docid); if(parentFolder==null) return; var url = parentFolder.getFilesByName("BlogProd.zip").next().getDownloadUrl(); return ContentService.createTextOutput(url); } Infine ho messo tutte insieme all’interno del seguente script linux sh in modo da poter automatizzare build e il deploy: #!/bin/bash #ln -s /home/yourname/works/luciosoftsite/blog/ ~/luciosoftsite #ln -s /home/yourname/works/luciosoftsite/blog/content/ ~/luciosoftblogcontent #requirements wget|unzip|hugo folderid="your google doc folder id” yourscriptid=”your google script id” cd ~/luciosoftblogcontent; retriveurl="https://script.google.com/macros/s/$scriptid/exec?folderid=$folderid”; downlodUrl=$(wget $retriveurl -q -O -); fileName="download.zip"; wget -c $downlodUrl -O $fileName; unzip -o $fileName; rm -f $fileName; cd ~/luciosoftblog; #hugo serve -D HUGO_ENV="production" hugo --config config.yaml firebase deploy Lo script è molto semplice, non fa altro che richiamare lo script google per recuperare l’url di download. Una volta recuperato l’url, a questo punto lo script effettua il download dello zip, estrae i contenuti all’interno del folder dei contenuti di Hugo, esegue il comando per creare Build, ed infine richiama firebase per effettuare il deploy. E anche oggi la magia è avvenuta. Sì, probabilmente avrei potuto seguire mille altre strade, anche più sicure e più efficienti ma questo è un inizio, il mio obiettivo era più grande. Vorrei trasformare lo script linux in un container docker e lo script google in un’applicazione dinamica con login google. Ma questa è un’altra storia che, se avrà seguito, vedrete prossimamente in un altro link all’interno del mio blog :) Poject ghithub link
Research In these days I have felt the need to create the blog you are reading. While surfing the web, I became aware of the static site generators, I have always known what they are and that they have always existed but I had never given them much attention. Though, while surfing deeper, I was attracted by Hugo, one of the many new engines to generate static websites. No WordPress I needed to have something static for I don’t have a server where I can install Wordpress or other CMS and, for a small personal blog, I don’t think it’s necessary and it is also too expensive. Finally I wanted the blog to be efficient and scalable and cross platform. DRY The first thing I thought about as a programmer was: how many operations do I need to do to deploy without a CMS? how will I bring my new content online? I am also a Google Docs lover and I like writing and having everything in one place. So I asked to myself: how do I combine Hugo and Google Docs without having to turn a thousand cranks? I studied the structure of MarkDown content and I started looking for something to convert Google Docs to MarkDown. The only useful thing I found on the web was this Googlescript mangini/ gdocs2md: Convert to Google Drive Document to the Markdown format, suitable for publishing. to whom I offer my heartfelt thanks. Although this script is very effective, I needed something more than receiving the conversion of an article by email. To have something more automated, I modified the script a bit and created the link script as follows: Link to my github repos The script converts all the documents listed in a Google sheet, organizes into folders by categories, extracts images from articles, adds the MarkDown header, tags and finally creates a zip file containing all the contents. I decided to make the zip public and therefore downloadable, ready for deployment. I then created a sort of database using a Google sheet as a table with this struct: ToDeploy Title Summary SummaryImage Category Date tag1 tag2 tag3 Tag4 Tag5 Language 1 Send As PDF files and web articles to the Kindle from Android 0 Kindle 09/05/2020 kindle pdf web article send to kindle android it In this way I have the possibility to deploy everything or to update only what I modified using the first field ToDeploy field. Subsequently, I created another google script that receives the id of the folder where there is the previously created zip. The script finally returns the public url from which it is possible to download the zip with the contents. function doGet (e) { params = JSON.parse (JSON.stringify (e)); var docid = params.parameters.docid; if (docid == null || docid == "") return; var parentFolder = DriveApp.getFolderById (docid); if (parentFolder == null) return; var url = parentFolder.getFilesByName ("BlogProd.zip"). next (). getDownloadUrl (); return ContentService.createTextOutput (url); } Finally I put them all together in the following script linux sh. So I can automate building and deployment: #!/bin/bash #ln -s /home/yourname/works/luciosoftsite/blog/ ~/luciosoftsite #ln -s /home/yourname/works/luciosoftsite/blog/content/ ~/luciosoftblogcontent #requirements wget|unzip|hugo folderid=”your google doc folder id” yourscriptid=”your google script id” cd ~/luciosoftblogcontent; retriveurl="https://script.google.com/macros/s/$scriptid/exec?folderid=$folderid”; downlodUrl=$(wget $retriveurl -q -O -); fileName="download.zip"; wget -c $downlodUrl -O $fileName; unzip -o $fileName; rm -f $fileName; cd ~/luciosoftblog; #hugo serve -D HUGO_ENV="production" hugo --config config.yaml firebase deploy The script is very simple, it calls the google script to retrieve the download url. Once the url is recovered, the script does the download of the zip, extracts the contents into the Hugo contents folder, executes the command to create Build, and finally calls firebase for deploy. And even today the magic has happened. Yes, I could have followed a thousand other ways, even safer and more efficient. But this is a starting point, my goal is bigger. I would like to turn the linux script into a docker container and the google script into a dynamic application with google login. But this is another story that, maybe, you will be able to see in another link at my blog :) Link al progetto ghithub
Ricerca Eccoci qua finalmente! In questi giorni ho sentito la necessità di creare questo blog che state proprio visitando! Mentre navigavo on line, sono venuto a conoscenza degli static site generator; ho sempre saputo della loro esistenza ma non avevo mai dedicato loro molta importanza. Tuttavia, navigando più a fondo, sono stato attratto da Hugo, uno fra i tanti nuovi motori per generare website statici. No Wordpres Partivo dall’esigenza di avere qualcosa di statico in quanto non dispongo di un server dove installare Wordpress o altri CMS e, per un piccolo blog personale, non credo sia necessario e oltretutto è troppo costoso. Infine, volevo che il blog fosse efficiente e scalabile e cross platform. DRY La prima cosa a cui ho pensato da programmatore è stata: quante operazioni dovrò fare per eseguire un deploy senza un CMS? come farò a portare i miei nuovi contenuti online? Sono anche un’amante di Google Docs e mi piace scrivere ed avere tutto in un unico posto. Allora mi sono chiesto: come posso unire Hugo e Google Docs senza dover girare mille manovelle? Innanzitutto, ho studiato la struttura dei contenuti MarkDown e mi sono messo alla ricerca di qualcosa che mi desse la possibilità di convertire i Documenti Google in MarkDown. La cosa più utile che ho trovato sul web è stato questo Google script mangini/gdocs2md: Convert a Google Drive Document to the Markdown format, suitable for publishing. a cui faccio i miei più sentiti ringraziamenti. Nonostante la comprovata efficacia del suddetto script, avevo bisogno di qualcosa in più rispetto al ricevere la conversione di un articolo via mail. Di conseguenza, per avere qualcosa di più automatizzato, ho modificato un pò lo script e creato il link che vedi sotto: Link allo script su github Lo script converte tutti i documenti elencati in un foglio Google, organizza in cartelle per categorie, estrae immagini da articoli, aggiunge l’intestazione del MarkDown, i tags ed infine crea un file zip contenente tutti i contenuti. Ho deciso di impostare lo zip con visibilità pubblica quindi scaricabile, pronto per il deploy. Ho successivamente creato una sorta di database utilizzando uno sheet di Google come se fosse una tabella con la seguente struttura: ToDeploy Title Summary SummaryImage Category Data tag1 tag2 tag3 tag4 tag5 Language 1 Come Inviare File PDF e articoli web al Kindle da Android 0 Kindle 2020-05-09 kindle pdf articolo web send to kindle android it In questo modo ho la possibilità di fare il deploy di tutto o di aggiornare solo ciò che ho modificato utilizzando il primo campo ToDeploy. Successivamente, ho creato un altro google script che riceve in ingresso l’id della cartella su cui è presente lo zip precedentemente creato e restituisce l’url pubblico da cui effettuare il download dello zip con i contenuti. function doGet(e) { params = JSON.parse(JSON.stringify(e)); var docid = params.parameters.docid; if(docid==null || docid=="") return; var parentFolder=DriveApp.getFolderById(docid); if(parentFolder==null) return; var url = parentFolder.getFilesByName("BlogProd.zip").next().getDownloadUrl(); return ContentService.createTextOutput(url); } Infine ho messo tutte insieme all’interno del seguente script linux sh in modo da poter automatizzare build e il deploy: #!/bin/bash #ln -s /home/yourname/works/luciosoftsite/blog/ ~/luciosoftsite #ln -s /home/yourname/works/luciosoftsite/blog/content/ ~/luciosoftblogcontent #requirements wget|unzip|hugo folderid="your google doc folder id” yourscriptid=”your google script id” cd ~/luciosoftblogcontent; retriveurl="https://script.google.com/macros/s/$scriptid/exec?folderid=$folderid”; downlodUrl=$(wget $retriveurl -q -O -); fileName="download.zip"; wget -c $downlodUrl -O $fileName; unzip -o $fileName; rm -f $fileName; cd ~/luciosoftblog; #hugo serve -D HUGO_ENV="production" hugo --config config.yaml firebase deploy Lo script è molto semplice, non fa altro che richiamare lo script google per recuperare l’url di download. Una volta recuperato l’url, a questo punto lo script effettua il download dello zip, estrae i contenuti all’interno del folder dei contenuti di Hugo, esegue il comando per creare Build, ed infine richiama firebase per effettuare il deploy. E anche oggi la magia è avvenuta. Sì, probabilmente avrei potuto seguire mille altre strade, anche più sicure e più efficienti ma questo è un inizio, il mio obiettivo era più grande. Vorrei trasformare lo script linux in un container docker e lo script google in un’applicazione dinamica con login google. Ma questa è un’altra storia che, se avrà seguito, vedrete prossimamente in un altro link all’interno del mio blog :) Poject ghithub link
Research In these days I have felt the need to create the blog you are reading. While surfing the web, I became aware of the static site generators, I have always known what they are and that they have always existed but I had never given them much attention. Though, while surfing deeper, I was attracted by Hugo, one of the many new engines to generate static websites. No WordPress I needed to have something static for I don’t have a server where I can install Wordpress or other CMS and, for a small personal blog, I don’t think it’s necessary and it is also too expensive. Finally I wanted the blog to be efficient and scalable and cross platform. DRY The first thing I thought about as a programmer was: how many operations do I need to do to deploy without a CMS? how will I bring my new content online? I am also a Google Docs lover and I like writing and having everything in one place. So I asked to myself: how do I combine Hugo and Google Docs without having to turn a thousand cranks? I studied the structure of MarkDown content and I started looking for something to convert Google Docs to MarkDown. The only useful thing I found on the web was this Googlescript mangini/ gdocs2md: Convert to Google Drive Document to the Markdown format, suitable for publishing. to whom I offer my heartfelt thanks. Although this script is very effective, I needed something more than receiving the conversion of an article by email. To have something more automated, I modified the script a bit and created the link script as follows: Link to my github repos The script converts all the documents listed in a Google sheet, organizes into folders by categories, extracts images from articles, adds the MarkDown header, tags and finally creates a zip file containing all the contents. I decided to make the zip public and therefore downloadable, ready for deployment. I then created a sort of database using a Google sheet as a table with this struct: ToDeploy Title Summary SummaryImage Category Date tag1 tag2 tag3 Tag4 Tag5 Language 1 Send As PDF files and web articles to the Kindle from Android 0 Kindle 09/05/2020 kindle pdf web article send to kindle android it In this way I have the possibility to deploy everything or to update only what I modified using the first field ToDeploy field. Subsequently, I created another google script that receives the id of the folder where there is the previously created zip. The script finally returns the public url from which it is possible to download the zip with the contents. function doGet (e) { params = JSON.parse (JSON.stringify (e)); var docid = params.parameters.docid; if (docid == null || docid == "") return; var parentFolder = DriveApp.getFolderById (docid); if (parentFolder == null) return; var url = parentFolder.getFilesByName ("BlogProd.zip"). next (). getDownloadUrl (); return ContentService.createTextOutput (url); } Finally I put them all together in the following script linux sh. So I can automate building and deployment: #!/bin/bash #ln -s /home/yourname/works/luciosoftsite/blog/ ~/luciosoftsite #ln -s /home/yourname/works/luciosoftsite/blog/content/ ~/luciosoftblogcontent #requirements wget|unzip|hugo folderid=”your google doc folder id” yourscriptid=”your google script id” cd ~/luciosoftblogcontent; retriveurl="https://script.google.com/macros/s/$scriptid/exec?folderid=$folderid”; downlodUrl=$(wget $retriveurl -q -O -); fileName="download.zip"; wget -c $downlodUrl -O $fileName; unzip -o $fileName; rm -f $fileName; cd ~/luciosoftblog; #hugo serve -D HUGO_ENV="production" hugo --config config.yaml firebase deploy The script is very simple, it calls the google script to retrieve the download url. Once the url is recovered, the script does the download of the zip, extracts the contents into the Hugo contents folder, executes the command to create Build, and finally calls firebase for deploy. And even today the magic has happened. Yes, I could have followed a thousand other ways, even safer and more efficient. But this is a starting point, my goal is bigger. I would like to turn the linux script into a docker container and the google script into a dynamic application with google login. But this is another story that, maybe, you will be able to see in another link at my blog :) Link al progetto ghithub
Ricerca Eccoci qua finalmente! In questi giorni ho sentito la necessità di creare questo blog che state proprio visitando! Mentre navigavo on line, sono venuto a conoscenza degli static site generator; ho sempre saputo della loro esistenza ma non avevo mai dedicato loro molta importanza. Tuttavia, navigando più a fondo, sono stato attratto da Hugo, uno fra i tanti nuovi motori per generare website statici. No Wordpres Partivo dall’esigenza di avere qualcosa di statico in quanto non dispongo di un server dove installare Wordpress o altri CMS e, per un piccolo blog personale, non credo sia necessario e oltretutto è troppo costoso. Infine, volevo che il blog fosse efficiente e scalabile e cross platform. DRY La prima cosa a cui ho pensato da programmatore è stata: quante operazioni dovrò fare per eseguire un deploy senza un CMS? come farò a portare i miei nuovi contenuti online? Sono anche un’amante di Google Docs e mi piace scrivere ed avere tutto in un unico posto. Allora mi sono chiesto: come posso unire Hugo e Google Docs senza dover girare mille manovelle? Innanzitutto, ho studiato la struttura dei contenuti MarkDown e mi sono messo alla ricerca di qualcosa che mi desse la possibilità di convertire i Documenti Google in MarkDown. La cosa più utile che ho trovato sul web è stato questo Google script mangini/gdocs2md: Convert a Google Drive Document to the Markdown format, suitable for publishing. a cui faccio i miei più sentiti ringraziamenti. Nonostante la comprovata efficacia del suddetto script, avevo bisogno di qualcosa in più rispetto al ricevere la conversione di un articolo via mail. Di conseguenza, per avere qualcosa di più automatizzato, ho modificato un pò lo script e creato il link che vedi sotto: Link allo script su github Lo script converte tutti i documenti elencati in un foglio Google, organizza in cartelle per categorie, estrae immagini da articoli, aggiunge l’intestazione del MarkDown, i tags ed infine crea un file zip contenente tutti i contenuti. Ho deciso di impostare lo zip con visibilità pubblica quindi scaricabile, pronto per il deploy. Ho successivamente creato una sorta di database utilizzando uno sheet di Google come se fosse una tabella con la seguente struttura: ToDeploy Title Summary SummaryImage Category Data tag1 tag2 tag3 tag4 tag5 Language 1 Come Inviare File PDF e articoli web al Kindle da Android 0 Kindle 2020-05-09 kindle pdf articolo web send to kindle android it In questo modo ho la possibilità di fare il deploy di tutto o di aggiornare solo ciò che ho modificato utilizzando il primo campo ToDeploy. Successivamente, ho creato un altro google script che riceve in ingresso l’id della cartella su cui è presente lo zip precedentemente creato e restituisce l’url pubblico da cui effettuare il download dello zip con i contenuti. function doGet(e) { params = JSON.parse(JSON.stringify(e)); var docid = params.parameters.docid; if(docid==null || docid=="") return; var parentFolder=DriveApp.getFolderById(docid); if(parentFolder==null) return; var url = parentFolder.getFilesByName("BlogProd.zip").next().getDownloadUrl(); return ContentService.createTextOutput(url); } Infine ho messo tutte insieme all’interno del seguente script linux sh in modo da poter automatizzare build e il deploy: #!/bin/bash #ln -s /home/yourname/works/luciosoftsite/blog/ ~/luciosoftsite #ln -s /home/yourname/works/luciosoftsite/blog/content/ ~/luciosoftblogcontent #requirements wget|unzip|hugo folderid="your google doc folder id” yourscriptid=”your google script id” cd ~/luciosoftblogcontent; retriveurl="https://script.google.com/macros/s/$scriptid/exec?folderid=$folderid”; downlodUrl=$(wget $retriveurl -q -O -); fileName="download.zip"; wget -c $downlodUrl -O $fileName; unzip -o $fileName; rm -f $fileName; cd ~/luciosoftblog; #hugo serve -D HUGO_ENV="production" hugo --config config.yaml firebase deploy Lo script è molto semplice, non fa altro che richiamare lo script google per recuperare l’url di download. Una volta recuperato l’url, a questo punto lo script effettua il download dello zip, estrae i contenuti all’interno del folder dei contenuti di Hugo, esegue il comando per creare Build, ed infine richiama firebase per effettuare il deploy. E anche oggi la magia è avvenuta. Sì, probabilmente avrei potuto seguire mille altre strade, anche più sicure e più efficienti ma questo è un inizio, il mio obiettivo era più grande. Vorrei trasformare lo script linux in un container docker e lo script google in un’applicazione dinamica con login google. Ma questa è un’altra storia che, se avrà seguito, vedrete prossimamente in un altro link all’interno del mio blog :) Poject ghithub link
Research In these days I have felt the need to create the blog you are reading. While surfing the web, I became aware of the static site generators, I have always known what they are and that they have always existed but I had never given them much attention. Though, while surfing deeper, I was attracted by Hugo, one of the many new engines to generate static websites. No WordPress I needed to have something static for I don’t have a server where I can install Wordpress or other CMS and, for a small personal blog, I don’t think it’s necessary and it is also too expensive. Finally I wanted the blog to be efficient and scalable and cross platform. DRY The first thing I thought about as a programmer was: how many operations do I need to do to deploy without a CMS? how will I bring my new content online? I am also a Google Docs lover and I like writing and having everything in one place. So I asked to myself: how do I combine Hugo and Google Docs without having to turn a thousand cranks? I studied the structure of MarkDown content and I started looking for something to convert Google Docs to MarkDown. The only useful thing I found on the web was this Googlescript mangini/ gdocs2md: Convert to Google Drive Document to the Markdown format, suitable for publishing. to whom I offer my heartfelt thanks. Although this script is very effective, I needed something more than receiving the conversion of an article by email. To have something more automated, I modified the script a bit and created the link script as follows: Link to my github repos The script converts all the documents listed in a Google sheet, organizes into folders by categories, extracts images from articles, adds the MarkDown header, tags and finally creates a zip file containing all the contents. I decided to make the zip public and therefore downloadable, ready for deployment. I then created a sort of database using a Google sheet as a table with this struct: ToDeploy Title Summary SummaryImage Category Date tag1 tag2 tag3 Tag4 Tag5 Language 1 Send As PDF files and web articles to the Kindle from Android 0 Kindle 09/05/2020 kindle pdf web article send to kindle android it In this way I have the possibility to deploy everything or to update only what I modified using the first field ToDeploy field. Subsequently, I created another google script that receives the id of the folder where there is the previously created zip. The script finally returns the public url from which it is possible to download the zip with the contents. function doGet (e) { params = JSON.parse (JSON.stringify (e)); var docid = params.parameters.docid; if (docid == null || docid == "") return; var parentFolder = DriveApp.getFolderById (docid); if (parentFolder == null) return; var url = parentFolder.getFilesByName ("BlogProd.zip"). next (). getDownloadUrl (); return ContentService.createTextOutput (url); } Finally I put them all together in the following script linux sh. So I can automate building and deployment: #!/bin/bash #ln -s /home/yourname/works/luciosoftsite/blog/ ~/luciosoftsite #ln -s /home/yourname/works/luciosoftsite/blog/content/ ~/luciosoftblogcontent #requirements wget|unzip|hugo folderid=”your google doc folder id” yourscriptid=”your google script id” cd ~/luciosoftblogcontent; retriveurl="https://script.google.com/macros/s/$scriptid/exec?folderid=$folderid”; downlodUrl=$(wget $retriveurl -q -O -); fileName="download.zip"; wget -c $downlodUrl -O $fileName; unzip -o $fileName; rm -f $fileName; cd ~/luciosoftblog; #hugo serve -D HUGO_ENV="production" hugo --config config.yaml firebase deploy The script is very simple, it calls the google script to retrieve the download url. Once the url is recovered, the script does the download of the zip, extracts the contents into the Hugo contents folder, executes the command to create Build, and finally calls firebase for deploy. And even today the magic has happened. Yes, I could have followed a thousand other ways, even safer and more efficient. But this is a starting point, my goal is bigger. I would like to turn the linux script into a docker container and the google script into a dynamic application with google login. But this is another story that, maybe, you will be able to see in another link at my blog :) Link al progetto ghithub
Quando abbiamo un’idea per un’App, ci sono tante domande che dobbiamo porci: E’ già presente sul mercato? Potrebbe funzionare? Quanto ci costa svilupparla? Quanto possiamo guadagnarci? Come facciamo a monetizzare da questa idea? In questo articolo voglio parlarvi di come monetizzare. Ci sono tante modalità e tanti strumenti che ci permettono di monetizzare, voglio riportare qui la mia esperienza, i miei errori e i suggerimenti su come utilizzare al meglio questi strumenti. Quando ho cominciato, non avevo per niente le idee chiare. Il mio obiettivo principale non era quello di guadagnarci ma di mettere in piedi un servizio auto-sostenibile, che non avesse costi o comunque fossero molto vicini allo zero, in modo da poter permettermi di vendere il servizio ad un prezzo molto basso. Primo step La prima cosa in assoluto da fare è individuare il nostro pubblico potenziale, per farlo vi consiglio di utilizzare Google adWords oppure facebook Ads. Questi strumenti sono ottimi per capire quale potrebbe essere il nostro pubblico potenziale. Per farlo dobbiamo inserire le parole chiavi riguardo le “specialità” della nostra App, selezionare il range di età e i paesi coinvolti. Il risultato che otteniamo da questa analisi è il numero dei potenziali utilizzatori totali e il paese con più percentuale di utilizzo. Quest’analisi potrebbe aiutarci ad individuare i nostri concorrenti, se esistono, e capire il loro pubblico e cosa ci distingue. Strumenti di guadagno Stabiliamo la modalità di monetizzazione. Ce ne sono davvero di svariate e dipende anche molto dal nostro tipo di business, però le principali più utilizzate e più adattabili nel mondo delle app credo possano essere le seguenti: Pubblicità Prodotti e Abbonamenti in app Vendita e analisi dati Pubblicità: Sebbene Google, uno dei più grandi colossi web al mondo, guadagni solo o quasi con la pubblicità, quest’ultima non è secondo me un ottimo strumento da utilizzare in tutti i contesti in quanto presenta i seguenti problemi: non è adeguata nei momenti iniziali Il guadagno è molto basso a meno che non si posseggano davvero tanti utenti Il prodotto deve attrarre e coinvolgere l’utente per un tempo medio lungo Rischia di essere troppo fastidiosa e potrebbe rovinare l’esperienza utente La pubblicità più semplice da implementare con Flutter guarda caso è la stessa di google, quindi Google AdMob. Vendita di prodotti: se decidi di mettere in vendita la tua app, hai a disposizione attraverso la Google play console diverse possibilità di vendita tra cui: Prezzo download App Ci permette di stabilire un prezzo per il download dell’app. Questa è la cosa più semplice da implementare, in quanto basta impostare il prezzo dell’app all’interno della console. Una volta fatto, abbiamo terminato! a meno che non si voglia mettere un ulteriore controllo all’interno dell’app, per evitare che vengano create delle copie pirate della nostra app e non la compri più nessuno. Secondo me questa modalità di vendita è ormai superata, la gente non si fida a comprare al buio, sebbene può sempre richiedere il rimborso ;credo comunque che imporre un prezzo sin dall’inizio sia un ostacolo. Sicuramente per alcune app ha senso, spesso viene utilizzato per la versione pro dell’app. Prodotti in App questa tipologia di prodotto dà accesso all’applicazione in versione gratuita e mette a disposizione alcuni prodotti all’interno dell’app stessa. In questo caso però oltre a definire i vari prodotti, scegliere e impostare i vari prezzi, dobbiamo anche sviluppare un pezzo nella nostra app per recuperare i prodotti da google play store per poter mostrare i prodotti. Anche se sono a disposizione librerie e plugin, prevede comunque un nostro sviluppo per poterlo utilizzare. Abbonamenti in App Gli abbonamenti sono molto simili ai prodotti con la differenza sostanziale che hanno una scadenza quindi hanno una validità temporale. Differenze tra Prodotti e Abbonamenti Il prodotto in App può essere utilizzato solo una volta, consumato subito, oppure potrebbe essere utilizzato per sempre, dipende dal valore che noi decidiamo di attribuirgli nella nostra app. Una volta comprato sblocca qualcosa. L’abbonamento invece ha una scadenza specifica, quindi comprende lo stesso comportamento del prodotto base ma ha una durata che può essere settimanale, mensile, trimestrale e annuale. Quanto davvero si guadagna dalla vendita su google play store? Questa è una cosa che all’inizio non è nota a tutti. Come nella vita normale, quando ci sono di mezzo i soldi, tutti ci devono guadagnare. Per tutti in questo caso intendo Google e lo stato. Google mette a disposizione: una vetrina uno store gestione rimborsi gestione prezzi normalizzazione valute tasse in tutti i Paesi del mondo. e tante altre cose Sebbene questo servizio sia per il 95% automatico, s’ha da pagare. Ma la cosa interessante e curiosa è che la percentuale che prende Google è di circa il 30% del prezzo della nostra App. la percentuale invece dovuta allo stato dipende dal Paese di riferimento dell’acquisto. Esempio: se vendiamo un’App dal costo di 2 euro, il nostro guadagno netto effettivo sarà di 1 euro circa. Invece. nel caso di Abbonamento, la percentuale spettante a Google, guarda caso cambia e scende al 15%. Ma perchè Google ci invoglia a vendere abbonamenti? Non ci vuole tanto ad arrivarci, il prodotto una volta venduto ha terminato il suo guadagno. L’abbonamento invece è per sempre. Stabiliamo il prezzo Non è semplice stabilire il prezzo giusto. E’ molto importante sceglierlo adeguatamente. Io ho fatto tanti errori in fase di scelta e credo che sia ancora sbagliato. Però l’errore principale che ho commesso, e credo commettano in molti, è il fatto che sottovalutiamo il prodotto che abbiamo sviluppato. Lavorandoci da tanto e conoscendolo bene, tendiamo a sottovalutare. Dalla mia esperienza ho capito che il modo giusto per definirlo è moltiplicare per 3 o per 4 il prezzo che io stesso sarei disposto a pagare. Google play console permette di inserire diversi prezzi per ogni paese, sebbene alcuni paesi siano più ricchi e più disposti a pagare per un’App, io ho deciso di mettere per tutti lo stesso prezzo, nel mio caso credo sia giusto. Anche perché si tratta di un prezzo molto basso. Inoltre l’app send to kindle è destinata ai possessori di un kindle che quindi, avendo comprato un kindle, credo possano essere disposti a pagare qualche euro per una comoda funzionalità. Definiamo la strategia Questo è il punto più complicato e dipende molto dal tipo di app o servizio che abbiamo implementato. Io per definirla in send to kindle, ho fatto questo ragionamento: Posso guadagnare solo con la pubblicità? Sebbene il mio pubblico stia crescendo e può diventare molto ampio, per adesso la risposta è no e credo lo sarà per molto. Perchè? Ho deciso di rendere l’app maledettamente veloce e quindi l’interazione utente è troppo breve. L’utente entra, invia il contenuto al kindle e ha finito. Non ha altro da fare nella mia app. Quindi le scelte che avrei potuto seguire erano le seguenti: lavorare per proporre qualcos’altro legato al contesto utilizzare uno strumento diverso rispetto alla pubblicità Alla fine ho deciso di non usare la pubblicità come guadagno principale. Ma allora, perchè l’ho usata lo stesso? Ho usato la pubblicità come elemento di fastidio e noia. Sì, avete capito bene noia, ho creato un sistema in modo che ad ogni tre invii si è costretti a guardare un video, per poter proseguire. La gente può continuare a guardare video, ma prima o poi si annoierà così tanto di guardare video pubblicitari che deciderà di valutare la rimozione della pubblicità. Oltre alla conversione degli articoli, send to kindle converte anche pdf e diversi formati di ebook. Di recente ho deciso anche di introdurre un abbonamento per questo che consente di effettuare un numero infinito di conversioni per un anno. Ho deciso di mettere un abbonamento perché la conversione utilizza un container in cloud ed ho dei costi per il servizio cloud da pagare ogni mese. In questo modo se i miei utenti dovessero sottoscrivere l’abbonamento, riceverò qualcosa ogni anno che spero possa essere utile ai fini di pagare il servizio in cloud e guadagnarci anche qualcosa. Finchè troverete l’app nello store vuol dire che sta funzionando ;) Considerazioni Oggigiorno quasi tutti cercano di proporre un servizio e contestualmente un abbonamento. Questo perchè a lungo andare è il più profittevole. Secondo me ogni app e ogni sistema necessita delle giuste considerazioni e scelte. Credo che tutti oggi possano comprendere e sono più propensi ad acquistare un oggetto fatto e finito anziché sottoscrivere un abbonamento. L’abbonamento non è visto molto bene da tutti, inoltre va motivato bene e bisogna ragionare bene su come venderlo e far comprendere tutti i vantaggi che si ottengono. Comunque non vi fasciate troppo la testa, il prezzo non è per sempre. Google play console, permette di cambiare il prezzo in ogni momento. Se uno lavora bene e fa crescere il suo servizio/app, il valore di quest’ultima cresce quindi è giusto aumentare il prezzo e aggiungere nuove funzionalità che aprono le porte a nuovi guadagni.
When we have an idea for an App, there are many questions that come to our mind, the first ones are: Is this app already on the market? Can this idea work? How much does it cost to develop it? How much can we earn? How to monetize this idea? In this article I want to talk about how to monetize. There are many ways and many tools that allow us to monetize, I want to report here my experience, my mistakes and suggestions on how to use these tools at the best. When I started, I had no clear idea. My main goal was not to earn money but to set up a self-sustainable service, which had no costs or costs very close to zero, so that I could afford to sell the service at a low price. First step The first thing to do is to identify our potential audience, to do this I recommend using Google adWords or Facebook Ads. These tools are great to understand what our potential audience might be. To do this we must enter the keywords of our App services, select the age range and the countries involved. The result we get from this analysis is the total number of potential users and the country with the highest percentage of use. It could help us identify our competitors and understand what is their audience like and what sets us apart. Earning tools It is time to establish our monetization method; there are really many different ways and choosing one of them depends on our type of business. However, the most used ones and the most adaptable in the world of Mobile Apps are the following: Advertising Products and Subscriptions in the app Sales and data analysis Advertising: Although one of the largest web giants in the world Google earns only or almost with advertising, the latter is not, in my opinion, a good tool to be used in all contexts, I believe it has the following problems: It is not appropriate at the beginning The profit is very low unless you have many users The product must attract and involve the user for some time. There is the risk of being too annoying and could ruin the user experience. The simplest advertising to implement with Flutter is the same used in google, therefore Google AdMob. Sale of products: if you decide to put your App on sale, you have various sales options available through the Google play console, including: **The price related to the App download ** This allows us to set a price for the App download. This is the simplest thing to implement, as we only have to just set the price of the app inside the console. Once done, we can be over with it unless we want to put a further check inside the app to prevent people from creating pirated copies of our app and from buying it. In my opinion setting a price from the beginning is now outdated, people do not trust buying in the dark. Although they can always ask for a refund, I think it is still an obstacle to impose a price from the beginning. It certainly makes sense for some apps, it is often used for the pro version of the app. Products in the App This type of product gives access to a free version of the application and makes the products available within the app itself. In this case, however, in addition to defining the various products, choosing and setting the various prices, we must also develop a piece in our app to retrieve the products from the Google Play Store in order to be able to show the products. Although libraries and plugins are available, we still have to develop it in order to use it. Subscriptions in the App Subscriptions are very similar to products with the difference that they have an expiring date and therefore have a temporal validity. Differences between Products and Subscriptions The product in the App can be used only once, expended immediately, or it could be used forever: it actually depends on the value that we decide to assign in our app. Once bought, it unlocks something. The subscription instead has a specific deadline, therefore it includes the same behavior as the base product but has a duration that can be weekly, monthly, three-monthly and annual. How much do you really earn from selling on google play store? This is something that is not known at first. As in normal life, when money is involved, everyone has to earn it. By “everyone"in this case I mean Google and the state. Google offers: a showcase a store refund management price management currency normalization taxes in all countries of the world. And lot of other stuff Although the 95% of this service is completely automatic, it has to be paid. But the interesting thing is that the percentage that Google takes is about 30% of the price of our app. the percentage due to the State instead depends on the country of reference of the purchase. Example: if we sell an app with a cost of 2 euros, our actual net profit will be around 1 Euro. Instead if we subscribe the percentage due to Google changes and drops to 15%. But why does Google encourages us to sell subscriptions? It does not take long to get there, once we have sold the product, we won’t have any further earnings. The subscription instead is forever. We establish the price It is not easy to establish the right price but it is very important to choose it properly. The main mistake I made, and I believe many people commit, is the fact that we underestimate the product we have developed. Since we have been working on it for a long time and know it well, we tend to underestimate it. From my experience, I understood that the right way to define it is to multiply by 3 or 4 the price that I would be willing to pay. Google play console allows you to enter different prices for each country, although some countries are richer and more willing to pay for an App, I decided to put the same price for everyone, in my case I think it’s right. Also because it is a very low price. In addition, the App send to kindle is intended for the owners of a kindle who therefore can be willing to pay a few euros for a convenient feature. We define the strategy This I think is the most complicated part and depends a lot on the type of app or service that we have implemented. Consider send to kindle I can ask myself this question: Can I earn only with advertising? Although my audience is growing and can become very large for now, the answer is no and I believe it will be no also in the future. Why? I decided to make the app bloody fast and therefore the user interaction is too short. The user enters, sends the content to the kindle and he is done. He has nothing else to do in my app. So the choices I could follow were the following: Suggest something else related to the context Use a different tool rather than advertising. In the end I decided not to use advertising as the main income. So why did I use it anyway? I used advertising as an element of annoyance and boredom. Yes you got it right! boredom, I created a system in a way that every three submissions, you are forced to watch an advert video, in order to continue. People can continue watching videos, but sooner or later they will get so bored with watching advertising videos that they will decide to consider removing the advertisements. In addition to converting articles, send to kindle also converts pdfs and various ebook formats. I recently decided to introduce a subscription for this. It allows you to perform an infinite number of conversions for a year. I decided to put a subscription because the conversion uses a container in the cloud and I have costs for the cloud service to be paid every month. In this way, if my users had to subscribe, I would receive something every year which, I hope, will be useful in order to pay for the cloud service and also earn something. As long as you’ll find it in the app store means it’s working;) Considerations In these days almost everyone is trying to offer a service using a subscription. This is because in the long run it is the most profitable. In my opinion, every app and every system need the right considerations and choices. I think a lot of people today can understand and are more likely to buy a product on time rather than signing up for a subscription. People don’t like the subscription, moreover it must be well motivated and you have to think about how to sell it and make everyone understand the advantages that they get. However, do not bandage your head too much, the price is not forever. Google play console, allows you to change the price at any time. If the person works well and the app grows, the value of the latter grows so it is correct to increase the price and add new features that open new doors to new earnings.
Oggi voglio raccontarvi una storia, la storia di una mia creazione e del progetto che ad oggi mi ha portato più successo e soddisfazioni. Partiamo dagli albori Io sono nato e soprattutto cresciuto nell’era digitale e sono sempre stato a contatto con la tecnologia, purtroppo non ho mai avuto un ottimo rapporto invece con i libri, credo e spero di poter recuperare in futuro. Questo mio mal rapporto con i libri però, in un certo senso, mi ha portato al successo di cui sto per raccontarvi. E’ strano in effetti ma è proprio così. Tanto tempo fa, essendo frustrato dal fatto che non leggevo abbastanza, ho deciso di comprare un Kindle pensando che mi avrebbe dato nuove opportunità di lettura. Il Kindle in effetti mi ha dato diverse soddisfazioni, ho letto diversi libri ma il mio utilizzo principale è sempre stato quello di leggere articoli online recuperati dal web. Articoli di programmazione, tecnologia, scienza e matematica. Sì, come avete già potuto capire, non sono un letterato e men che meno un filosofo, scrivo perchè mi piace ma non credo di saperlo fare bene non avendo studiato materie adeguate. Vabbè… mi sà che sono troppo prolisso adesso. Veniamo al dunque… ! perchè vi ho raccontato questa storia? Perché il Kindle è stato il punto cardine del mio successo? Avendone apprezzato tanto le sue capacità, ho deciso di consigliarlo a molti miei amici e parenti. L’ho consigliato anche a un mio caro collega di lavoro con il quale ci siamo spesso dati suggerimenti a vicenda su come ottenere il massimo dal kindle. L’app che più utilizzavamo era un’ app per smartphone che permetteva di inviare articoli al kindle. Un giorno quest’ app smette di funzionare e sparisce dallo store in maniera misteriosa e ahimè! non se ne sa più nulla. Nello store erano presenti altre app per fare questa azione, ma non funzionavano benissimo o alcune di queste erano a pagamento. Io e il mio collega eravamo un po’ frustrati da questa cosa. Il mio collega Un giorno il mio collega mi dice: ma perchè non la fai tu l’app per inviare contenuti al kindle? Io ci ho pensato un attimo, non sapevo bene da dove cominciare, ma poi mi son detto: lo sai che c’è? sono programmatore, ho le basi posso farcela! E’ così è nato Send To Kindle. Oggi devo molto al mio caro collega e alla sua richiesta. Quando ho rilasciato la prima versione dell’app, era molto spartana, l’ho implementata per me, i miei amici/parenti e soprattutto per il mio collega, non credevo e non potevo mai immaginare che ad oggi, dopo un anno dalla sua creazione, ho raggiunto più di 70.000 installazioni in tutto il mondo e questo numero cresce giornalmente. Cosa Abbiamo imparato? Quest’app mi ha fatto crescere in poco tempo e mi ha fatto capire tante cose che voglio riassumere e condividere con voi: Se non ci provi non ci riesci A volte ci pensiamo troppo, oppure attendiamo la perfezione per poter rilasciare qualcosa. Invece, in molti casi è meglio buttarla lì e poi se funziona siamo sempre in tempo per aggiornarla ed aggiungere nuove feature. Più fai e più possibilità hai di riuscita Se ci riesci devi correre come un matto Anche se hai pensato e preparato tutto ai minimi dettagli, una volta che la tua app o il tuo sito iniziano ad ingranare e si popolano di utenti, cominciano i problemi! Ed è li che devi fare il possibile in modo da perdere meno utenti possibili. Impossibile accontentare tutti Ogni utente ha le sue necessità e non è possibile fare un prodotto generico che accontenti tutti. Non seguire troppo e valuta bene le richieste degli utenti L’ho provato sulla mia pelle, gli utenti potrebbero non accontentarsi mai! Un esempio: Send To Kindle nel primo periodo inviava solo articoli. Tutte le recensioni negative dicevano che non inviava documenti/file. Allora ho implementato l’invio dei documenti che mi è costato tempo e denaro. Adesso secondo voi cosa dicono gli utenti? Alcuni sono soddisfatti ma molti altri mettono sempre una stella e dicono: non invia file superiori a 25 mb. Secondo voi, se mai aumentassi questo limite cosa diranno? Esegui tanti rilasci frequenti Anche se un giorno tutto va bene pensa sempre che il giorno dopo andrà tutto di ma* Presta attenziona alle risposte date ai clienti Aggiornati sempre sulle mosse dei tuoi concorrenti Continua ad aggiornare ed innovare Considerazioni finali Sono conscio del fatto che Send To Kindle non sia un’invenzione fuori dal normale o un’ innovazione. In effetti, non ho inventato qualcosa di completamente nuovo; ho reso migliore, più efficiente e integrato qualcosa che c’era già con tecnologie nuove. Credo che il mio punto di forza sia il fatto di aver reso fruibile il servizio da mobile. A volte basta poco per ottenere successo, basta essere meglio dei propri concorrenti. Sicuramente però per farlo sono necessari tanti sforzi e tanto lavoro. Se siete arrivati a leggere fino a qui complimenti!!! Se non l’avete ancora fatto scaricate Send To Kindle, e inviate questo articolo o altri articoli al kindle! Assaporate una lettura più rilassante!.
Today I want to tell you a story, the story of my creation and the project that has brought me more success and satisfaction to date. Let’s start from the dawn I was born and especially raised in the digital age and I have always been in contact with technology. Unfortunately I have never had an excellent relationship with books, I believe and I hope to be able to catch up in the future. This bad relationship with books, however, has led me to the success I’m about to tell you. A long time ago, being frustrated by the fact that I wasn’t reading enough, I decided to buy a Kindle thinking that would give me new reading opportunities. The Kindle indeed gave me different satisfactions, I have read several books but my main use has always been to read online articles retrieved from the web. I used to read programming, technology, science and math articles. Yes, as you have already understood, I am not a writer and least of all a philosopher, I write because I like it but I don’t think I can do it well as I haven’t studied subjects related to writing. Ok, I’m too long-winded now! But why did I tell you this story? Why was the Kindle the cornerstone of my success? Since I appreciated its skills so much, I decided to recommend it to many of my friends and relatives. I also recommended it to a close colleague of mine, with whom we often given each other suggestions on how to get the most out of the kindle. The app that we used most was a smartphone app that allowed us to send items to the kindle. One day this app stopped working and disappeared from the store in a mysterious and unknown way. There were other apps in the store that did this, but they didn’t work very well and some of them weren’t free. My colleague and I were somewhat frustrated by this. My colleague One day my colleague tells me: why don’t you make the app to send content to the kindle? I thought about it for a moment, I wasn’t sure from where to start, but then I said to myself: I’m a programmer and I have the basics … I can do it! This is how Send To Kindle was born. Today I owe a lot to my dear colleague and to his question. When I released the first version of the app, it was very spartan, I implemented it for myself, my friends/relatives and especially for my colleague. I did not believe and I could never imagine that today, one year after its creation, I would have reached more than 70,000 installations worldwide and this number grows daily. What have we learned? This app made me grow in a short time and made me understand many things that I want to summarize and share with you: If don’t try, you don’t succeed Sometimes we think about it too much or we wait for perfection before releasing something. Instead, in many cases, it is better to throw it there and then if it works we are always in time to update it and add new features. The more you do/work on it and the more chances you have to succeed If you succeed you have to run like crazy It is Impossible to satisfy everyone Even if you have thought and worked to the smallest details, once your app or your site begin to mesh and populates with users, your problems will begin and that’s where you have to do your best in order not to lose your users. It is Impossible to satisfy everyone Every user has his own needs and it is not possible to make a generic product that will satisfy everyone. Don’t follow too much and evaluate user requests well I tried it on my skin, users may never be satisfied! An example, Send To Kindle in the first period only used to send articles. All the negative reviews said: the app doesn’t send documents/files. So I implemented the sending of documents which cost me time and money. Now, what do you think users say? Some are satisfied but many others always put a star and say: it doesn’t send files larger than 25 mb. In your opinion, if I ever increase this limit what will they say? Do many frequent releases Even if one day everything goes well, always think that the next day everything will be st** Pay attention to the answers given to customers Always be updated with the moves of your competitors Continue to update and innovate Final considerations I am aware of the fact that Send To Kindle is not an unusual invention or an innovation. In fact, I did not invent something completely new, I improved something that was already there with new technologies and I made it more efficient. I think my strong point is that I made the mobile service accessible. Sometimes it takes little to be successful, you just need to be better than your competitors. Certainly, a lot of effort and a lot of work are needed to do it. Congratulations if you’ve read this far!!! if you haven’t done it yet, download Send To Kindle, and send this article or other articles to you kindle! Have a better reading and relaxing experience!
Sei pronto per iniziare una nuova esperienza di lettura? Finalmente puoi dire addio alle scomode letture notturne da smartphone. Leggere articoli lunghi su uno schermo di uno smartphone può essere scomodo, specialmente se l’articolo è molto lungo e sono presenti molti annunci. Leggere su un Kindle è molto più semplice per gli occhi e si può avere anche un maggiore controllo sulla dimensione e sul carattere usato. Trova il tuo indirizzo Kindle con 3 passaggi 1. Recupera la email del tuo kindle, puoi trovarlo accedendo al seguente link: https://www.amazon.it/hz/mycd/myx#/home/settings/ ( assicurati che il .it sia l’Amazon utilizzato nel tuo paese, quella in cui hai associato il tuo kindle) 2. Scorri verso il basso fino alle impostazioni del documento personale e fai clic su di esso. Modifica l’indirizzo del kindle che ti servirà per accedere all’app Invia a Kindle. 3. Scorri verso il basso, nella sezione mail approvate, aggiungi l’indirizzo e-mail che usi tutti i giorni o quello da cui desideri inviare i documenti, verifica che l’archiviazione sia abilitata. Adesso rilassati e divertiti a inviare documenti con Send to Kindle. Send to Kindle La migliore app per farlo è Send To Kindle. Questa app non è la stessa di Invia a Kindle fornita da Amazon. Invia a Kindle ti consente di: inviare articoli Web al tuo Kindle tramite la funzione Condividi sul tuo browser Web mobile. Invia documento pdf / epub / azw convertendolo in mobi. Articolo Web Invia a Kindle Scarica l’app Invia a Kindle dal Play Store Send To Kindle. Individua l’articolo che desideri inviare al tuo Kindle su qualsiasi browser o app di notizie. Apri le impostazioni del tuo browser e cerca tre punti nell’angolo in alto a destra. Fai clic su Condividi. Scegli Send To Kindle come app di condivisione. Attendi che l’app converta il tuo articolo nel formato corretto. Fare clic sul pulsante Invia con la freccia gialla. Se è la prima volta che usi l’app, dovrai impostare il tuo indirizzo e-mail Kindle. Tocca il messaggio per farlo. Digita la tua e-mail Kindle e premi Salva. Scegli il tuo client di posta elettronica. Invia l’e-mail. Controlla il tuo Kindle per l’articolo. Invia a Kindle PDF, EPUB o AZW Fai clic sull’icona più nella barra di ricerca Seleziona il file pdf da inviare Fai clic su Convert Invia la mail utilizzando il tuo client di posta preferito
Are you ready to start a new better reading experience? You can finally say goodbye to the uncomfortable night readings from your smartphone. Reading long articles on a mobile screen can be difficult, especially if the site has lots ads. Reading on a Kindle is much easier on the eyes, and you have more control over the size and the font you use. Find Your Kindle Address with 3 step configuration 1. Recover the email of your kindle, you can find it by accessing to the following link: https://www.amazon.com/hz/mycd/myx#/home/settings/ (make sure that the .com is the Amazon used in your country, the one where you have associated your kindle) 2. Scroll down to personal document settings and click on it. Edit the kindle address that you need in order to enter the Send to Kindle app. 3. Scroll down, in the section approved mails, add the e-mail address you use every day or the one you want to send the documents from, please verify that archiving is enabled. Now, relax and enjoy sending documents from Send to Kindle. Send to Kindle The best app for doing so is Send to Kindle app. This app is not the same as the Send to Kindle provided by Amazon. Send to Kindle allows you: send web articles to your Kindle by Share feature on your mobile web browser. Send pdf/epub/azw document by converting it to mobi. Send to Kindle Web Article Download Send to Kindle app from the Play Store Send to Kindle. Locate the article you want to send to your Kindle on any browser or news app. Open the settings for your browser and look for three dots in the top-right corner. Click Share. Choose Send to Kindle as your sharing app. Wait for the app to convert your article to the correct format. Click the yellow-arrow Send button. If this is the first time you have used the app, you will need to set up your Kindle email address. Tap the message to do so. Type in your Kindle email and press Save. Choose your email client. Send the email. Check your Kindle for the article. Send to Kindle per PDF, EPUB o AZW Click on plus icon in search bar Select your pdf file Click convert Send the mail
Din..Don..Din..Don.. Era Domenica mattina, le campane suonarono la prima volta alle 8 la mattina, il suono era molto forte e riecheggia in tutto il paese, quella domenica come tutte le domeniche a quell’ora quasi tutti dormivano compreso Gino. La vecchietta Concettina invece era sveglia da prima dell’alba, sprizzava di gioia e tanta energia, era il suo unico giorno di uscita. Come ogni domenica, Concettina, stava seguendo la sua routine domenicale, si stava preparando a festa! Concettina, dopo aver fatto una colazione molto abbondante, aveva bisogno di tante energie di domenica, si attinge ad effettuare tutte le sue preparazioni. Una doccia molto lunga per sentirsi ben profumata e sveglia, dopo si preparava i capelli, facendo tanti boccoli raccolti uno per uno per comporre come un mazzo di tulipani. Nonostante l’età Concettina aveva ancora tanti capelli, tutti neri. Faceva il colore pochissime volte. Il nero dei suoi capelli era sempre molto forte e acceso. Di domenica, usava legare i capelli con nastro di colore azzurro e rosa e ci faceva sopra un bel fiocco. La faccia tutta incipriata per coprire le occhiaie e un pò i solchi degli anni, pesanti rughe che nonostante la cipria, restavano comunque visibili. Sulle labbra, passava un rossetto, di un rosso ma di un rosso, molto forte che accendeva tutta la faccia e delineava un gran sorriso. Sorriso che aveva sempre stampato in faccia, un sorriso vissuto, che esprime molta serenità e bontà. Scorreva tutti i vestiti migliori, li guardava, li riguardava, li girava, ma alla fine sceglieva quasi sempre il suo vestito preferito. Un vestito bello a quadri nero bianco e grigio. Molto classico ed elegante. Ci attaccava sopra la sua spilla d’oro, spilla di un delfino che aveva ricevuto da sua mamma e a sua volta, sua mamma l’aveva ricevuta da sua Mamma Concetta. Aggiungeva, una collana di perle e due orecchini di perle per dare un tocco ancor più di eleganza. Prendeva la sua borsetta e la riempiva di tutto ciò che poteva essergli utile, un fazzoletto bianco, tutto ricamato da lei stessa, con le sue iniziali PC, un foulard nel caso facesse freschetto, un ventaglio nel caso facesse caldo e un ombrello nel caso piovesse. Pensava proprio a tutto e aveva tanto tempo per prepare tutto al minimo dettaglio. Una volta pronta, si posizionava nel suo posto preferito, posto in cui passava le mattine e i pomeriggi, in genere stava lì, seduta sul balcone a non fare nulla, guardare le macchine passare, i bambini giocare, le foglie cadere e il tempo passare. Invece oggi, aveva una cosa da fare. Stava lì seduta ad aspettare Petra. Petra era una ragazza giovane, figlia di Pippo, cugino di terzo grado di Concetta. Petra era molto affezionata a Concetta e ogni domenica, andava a prenderla a casa e l’accompagnava in chiesa, Concetta faceva fatica a camminare per i suoi dolori di artrosi dovuti all’età. Petra arrivò puntuale alle 10 be un ora prima dall’inizio della messa. Al suo arrivo di nuovo Din..Don..Din..Don.. le campane suonarono di nuovo a festa. Concetta abitava circa 150 mt di distanza dalla Chiesa, generalmente una persona giovane e in salute ci mette circa un minuto a percorrere questo tratto, invece Concetta e Petra uscivano almeno un ora prima, si la vecchietta Concetta aveva qualche acciacco ma non era per quello che uscivano così prima bensì perchè Concetta aveva tanto tempo a disposizione e quello era il suo giorno e voleva goderselo al massimo. Petra una volta arrivata, salutava Concetta. Buongiorno Zia Concetta! Ma comu ti parasti! Si troppu bedda stamatina! 1. Concetta ricambia con tanta gioia e un gran sorriso Buongiorno Petra! oggi, tu si a chiu bedda du paesi! Putemu fari a sfilata! 2. Petra aveva portato un fiore come tutte le domeniche, raccolto dal suo giardino. Si avvicinò a Concetta e lo mise sotto al fiocco e disse. Ora putemu nesciri si troppu bedda! 3. Allora prese la borsetta e insieme si avviarono piano piano verso le scale. Concetta si appoggiò a Petra da un lato e al corrimano in legno dall’altro lato. Faceva scivolare la mano sullo scorrimano, ma benché si fidasse molto di Petra, non si fidava delle sue gambe quindi, stringeva molto forte lo scorrimano che provocava uno stridio derivante dall’attrito della sua mano sudata che scivolava sullo scorrimano. Una volta finite le scale, Concetta si sedeva sul bisolo per fare una piccola pausa. Concetta una volta seduta disse Hai Hai! Chi bella iunnata i suli sta matina…ma Petra dimmi chi facisti a ieri sira? mi dissiru chi niscisti con cocca adunu. 4 Petra rispose ieri niscia cu Ginu. Ie un bravu figghiolo! allora subito Concetta ribatte: E cu ie Gino? I cui è figghiu? Petra ci penso un attimo, poi rispose: ie u figghiu i Mimma e i Pippu a bonanima sua. 5 Concetta subito curioso ribatte e chi facisti? cunta cunta…6 Allora concetta comincia: ma chi nì sai mi vinni a pigghiari fino a casa! ruvao tutto allicchittato supra a so vespa..Matri tuttu troppu beddu chi capiddi sistemati ie na camicia pulita… 7 Non finì di raccontare la storia che arrivò Mari Ciccia. Le tre si scambiarono affettuosi saluti e si incamminarono verso la messa. Appena partiti sentirono ancora Din..Don..Din..Don.. l’ultima ora, le campane suonavano ogni 15 minuti. Gino questa volta fece un sussulto sentendo le campane ma poi si girò dall’altra parte e continuò a dormire. Camminarono per ben 15 minuti tirati facendo qualche pausa qua e là e si ritrovarono in piazza. Una volta in piazza, camminarono ancora più piano in modo da farsi vedere da tutti. Si sedettero proprio nella panchina centrale della piazza, tutti le guardarono, loro salutarono e si misero a chiacchierare con tutti, quando ancora Din..Don..Din..Don… Ad un tratto, si avvicinò Cammela a cuttigghiara era sempre in cerca di qualcuno a cui raccontare i suoi curtigghiu ma soprattutto ascoltare altri curtigghi in modo da riempire il suo tempo, allenare la sua parlantina. Una volta lì davanti Buongiorno signore, ma come siamo belle questa mattina! le tre, ricambiarono il saluto. Carmela utilizzava sempre la stessa strategia, inizialmente buttava giù una serie di complimenti e ringraziamenti vari in modo da attrarre l’attenzione e fare sentire bene il suo pubblico, poi quando tutte pendevano dalle sue labbra partiva con la sua storia, inizia sempre dalla storia più in voga del momento, poi se vedeva esserci un ricambio allora andava giù più pesante fino agli scoop che manco i muri conoscevano. E fidatevi lei ne sapeva davvero tante, il suo lavoro era quello ed era assatanata dal saperne sempre di più, sapere tutto di tutti. Quel giorno esordì con la storia da Fubba (la furba). La Fubba, era una vecchietta che non si vedeva mai, stava sempre chiusa in casa e non si sapeva niente di lei, non si sapeva cosa facesse, come vivesse e se fosse ancora viva. Si parlava di lei spesso, era l’unica di cui nessuno aveva filtro. Si raccontavano tante storie brutte e belle ma la maggior parte di queste vedeva sempre la donna in maniera cattiva vincente e furba. Ieri a putia ntrasiu na signora, Gina (la proprietaria della putia), mi dissi…chi mai l’avia vista. Iera vecchia, ma strana, aviva nu stile tuttu particulari cu nu cappeddu a tipo chiddi chi si vidunu nta televisione e parrava italianu. Ma Italianu bonu, comu parrano chiddi chi studiano assai, sicuru chi fici magari i superiori. Na vota chi ntrasiu, vaddau tutti i cosi e ci fici 50 mila dumanni, e da ago e filo si cattao fino a attrezzi i travagghiu. Proprio strana sta vecchia. Si cattau si tanti cosi. Gina mi dissi chi manco nto misi migghiori fici tutto sto guadagnu. Appena nisciu Gina cuminciò a cantari e ballari da contentizza e chiamò subito so figghiu Filippò e u mannau mi ci va appresso pi capiri da unni nisciu sta vecchia. Allora Filippu ci cuntao chi si fici u giro i tutti l’attività du paisi. Cattò, tutto cattau, inchiu u carrello si cattò magari nautru carrelo e u ghinchì. Tutti aierie ieruno sbarruati i sta cosa e tutti i vindanti mi cuntaruno a stissa cosa chi era troppu stranuuu. Ma a sapiti na cosa? Filippu a nsucutau fino a casa e ndovinati aunnni ruvao…A casa da Fubba! Cumbinazioni ie fossi o ie proprio a Fubba? E cu sapi e cui a cunusci! mai nuddu a vidiu nta facci! 8 La storia era quasi finita quando fu interrotta dalle campane Din..Don..Din..Don..Din..Don..Din..Don.. che suonavano di più per segnalare che mancava solo l’ultima quarto d’ora all’inizio della messa. Allora le tre ringraziarono Carmela per la storia e si incamminarono verso la chiesa. Carmela non aveva tratto alcun guadagno da quello scambio allora si avvicinò e disse, u sapiti chi c’è, magari io a nari a missa oggi, vengu con vui così vi ncumpagnu fino a ghiesa. 9 In questo modo, nel tragitto poteva ascoltare le loro discussioni. Le tre lo sapevano, conoscevano bene Carmela, ma era inutile cercare di evitarla o non parlare. Allora si misero in marcia e ripresero a chiacchierare lo stesso però ogni tanto aggiungevano al discorso una fandonia in modo da rendere il lavoro di Carmela più difficile. Appena arrivarono sul ponte che regala una bella vista della grande chiesa del paese dall’alto, rimasero tutte e tre sbalordite, c’era un matrimonio e la piazza di fronte alla chiesa era tutta piena di persone belle, sorridenti e vestite a festa. Concettina allora divenne ancora più contenta, i suoi occhi si illuminarono e una lacrima di gioia scivolò giù per il viso. Anche Mari Ciccia lacrimò, ma le sue lacrime erano lacrime di dolore per suo marito, l’aveva perso da qualche mese e ancora il ricordo era molto accesso. Petra invece era contentissima, nella folla distinse gli sposi, e già si immaginò il suo futuro con Gino li in mezzo sorridenti. Carmela non era per niente sorpresa ovviamente già era a conoscenza del matrimonio e sapeva tutto sul matrimonio si trattava di una fuitina 10. Carmela era venuta apposta per vedere come le persone si erano vestite, studiare la gente e conoscere altre storie per dissetare la sua irrefrenabile voglia di conoscenza altrui. Buongiorno Zia Concetta! Ma comu ti sei vestita e sistemata bene! Sei troppo bella questa mattina! Your browser does not support the audio element. ↩︎ Buongiorno Petra! oggi, tu sei la più bella del paese! Insieme possiamo fare la sfilata di moda! Your browser does not support the audio element. ↩︎ Ora possiamo uscire, sei troppo bella! Your browser does not support the audio element. ↩︎ Hai Hai! Che bella giornata di sole questa mattina…ma Petra dimmi che hai fatto ieri sera? Mi hanno detto che sei uscita con qualcuno. Your browser does not support the audio element. ↩︎ E’ il figlio di Mimma e Pippo defunto e degno di rispetto. Your browser does not support the audio element. ↩︎ E che hai fatto? racconta… Your browser does not support the audio element. ↩︎ Non puoi capire, è venuto a prendermi fino a casa! e arrivato ed era tutto sistemato con la sua vespa bianca. Mi davvero troppo bello, con i capelli e la camicia perfetti… Your browser does not support the audio element. ↩︎ Ieri alla putia (negozio tipico siciliano dove in genere si vende qualsiasi cosa) è entrata una signora*, Gina (la proprietaria della putia), mi disse che non l’aveva mai vista. Era anziana e strana, aveva uno stile particolare, portava un cappello come quello che si vede alla televisione. Parlava in italiano molto bene. Come parlano le persone che studiano tanto, sicuramente avrà conseguito anche la scuola superiori. Una volta che entrò, ha guardato tutto il negozio e fece tante domande. Comprò tutto da ago e filo, fino agli attrezzi da lavoro. Ha comprato così tante cose che Gina, la proprietaria del negozio, neanche nel mese migliore guadagna così tanto. Appena uscita, Gina si mise a ballare e a cantare dalla contentezza e chiamò suo figlio Filippo e lo mandò a seguire la signora in modo da capire da dove proveniva. Filippo, raccontò che fece tutto il giro dei negozi del paese e comprò tantissime cose, Riempi il carrello e si comprò anche un altro carrello e lo riempì. Tutti i venditori erano sorpresi di questa cosa e mi raccontarono tutti la stessa cosa. Era troppo strano. Ma la sapete una cosa. Filippo la seguì fino a casa e indovinare dove andò….A casa della Furba! E’ una combinazione oppure è proprio lei? E chi lo sa? Chi la conosce! Mai nessuno l’ha vista in faccia.* Your browser does not support the audio element. ↩︎ Lo sapete che c’è, anche io devo andare a messa oggi, vengo con voi così vi accompagno. Your browser does not support the audio element. ↩︎ Fuitina: fuga repentina che identifica l’allontanamento di una coppia di giovani aspiranti coniugi dai rispettivi nuclei familiari di appartenenza, allo scopo di rendere esplicita (o far presumere) l’avvenuta consumazione di un atto sessuale completo. ↩︎
Noi programmatori abbiamo diverse competenze e tanti pattern che utilizziamo, ma credo che la cosa che maturiamo nel tempo e ci accomuna di più è l’ottimizzazione delle risorse per risparmiare tempo. Tutti sanno che il tempo è la cosa più importante! Lo dice lo stesso modo di dire: il tempo è denaro. Il detto si basa su tempo umano. Il tempo umano deve essere utilizzato come valore aggiunto per la creatività, invenzione, innovazione costruzione e implementazione. Noi programmatori siamo fortunati, abbiamo a disposizione un’altra unità di misura del tempo, oggigiorno tendente quasi all’infinito, che possiamo e dobbiamo sfruttare al massimo e questo tempo e il tempo macchina. Un tempo si facevano fatigare gli schiavi poi i cavalli, i muli, i motori oggi ci siamo evoluti, culturalmente e mentalmente, Gli schiavi di quest’era sono le macchine. Per macchine intendo i nostri pc, o meglio ancora i nostri server, il cloud o i robot. Ho deciso di scrivere questo articolo per riportare delle tecniche utili per efficientare il nostro lavoro e di conseguenza ottenere più tempo. La mia tecnica principale da utilizzare per ottenere questo risultato è: appuntare gli step manuali che compiamo per fare un compito ricercare possibili soluzioni alternative riflettere su come possiamo automatizzare ogni singolo step mettere tutto assieme ripartire dal punto 1 se il nostro tempo d’impiego non è ancora = 0 per l’attività scelta Un esempio Spesso per velocizzare il nostro lavoro scriviamo degli script, questi script sono necessari altrimenti dovremmo scrivere n istruzioni di comandi. Giornalmente o mensilmente o dopo aver fatto qualcos’altro, dobbiamo eseguirli. Quindi apriamo il terminale e lanciamo lo/gli script. Per evitare di lanciare lo script a mano, che implica ricordarci, trovare lo script e lanciarlo, possiamo procedere nel seguente modo: se si tratta di qualcosa che va eseguito con una schedulazione fissa possiamo schedularlo utilizzando uno scheduler in linux possiamo fare un cron oppure possiamo utilizzare atrigger per eseguire la schedulazione nel caso in cui il nostro script sia su un server a cui non abbiamo accesso alla schedulazione Se invece non c’è una schedulazione fissa e dobbiamo eseguirlo al bisogno Possiamo utilizzare una scorciatoia da tastiera. In linux ma anche su Windows e molto semplice settare una una scorciatoia. Nel mio caso usando Ubuntu basta andare sotto *impostazioni *-> scorciatoie da tastiera e aggiungiamo il nostro nuovo parametro che conterà il path al nostro script e la scorciatoia desiderata. Come potete vedere nella seguente immagine, io lo uso ad esempio per lanciare in debug il mio blog e successivamente per fare il deploy: per quanto questo possa sembrare bello e può aiutarci a renderci più efficienti, con due tasti, il gioco è fatto, si può sempre ottimizzare. Voi direte, che altro puoi fare per ottimizzare questo? e perchè? Il perchè è semplice, per poterlo eseguire ho necessariamente bisogno di avere la mia macchina a disposizione e accesa. Quindi sarebbe meglio laddove possibile utilizzare sempre un server o il cloud in modo da fare tutto da remoto in modo così da poter avere più controllo e soprattutto non dipendere da una macchina. Oggi ci sono tanti strumenti che abbiamo a disposizione per eseguire script o creare piccole pagine web o piccole applicazioni o siti vetrina. Il piano gratuito di questi servizi, mette a disposizione tanti strumenti fra i quali: Spazio hosting. Spazio database. Funzioni per micro servizi Container Macchine virtuali e chi più ne ha più ne metta C’è ne sono così tanti che a volte tendiamo a non vederli, nonostante siano sotto il nostro naso. Quelli che utilizzo io maggiormente, sono: Firebase Google Cloud Amazon AWS Google Script L’ultimo servizio a differenza degli altri non è un servizio cloud ma semplicemente un motore di funzioni con schedulazione o attivazione in funzione dei servizi Google. Sono molto integrate con il nostro account Google e se lo usiamo come mail principale ci darà davvero delle grandi potenzialità per automatizzare: invii di mail sincronizzazione di dati editing di documenti chiamare API esterne memorizzare dati in excel ecc ecc… è brutto dire ecc ecc ma davvero, l’elenco sarebbe troppo lungo, vi permette di automatizzare tutto ciò che è Google. Gli altri tre sono i primi tre migliori provider Cloud. Per cominciare io consiglio di usare Firebase perchè mette a disposizione davvero tanti servizi e se si tratta di primo inzio con il cloud, l’accesso con Firebase, credo sia più semplice e con una linea di apprendimento più rapida. Conclusioni Cerchiamo di fare più attenzione a quello che ogni facciamo senza pensare. Se ogni giorno facciamo una cosa ripetitiva, anche se ci mettiamo davvero poco a farla, teniamola in considerazione e pensiamoci. Dopo anni le cose ripetitive possono diventare tante e rischiamo di saturare tutto il nostro tempo col fare solo cose ripetitive. Vi lascio e vi saluto con una frase che mi è venuta adesso. Probabilmente non ha senso o forse è già di qualcuno ma io non lo so. Meglio creare un sistema che ci risolve un problema che risolvere il problema stesso!
We as programmers have different skills and we use lot of patterns, but I think the thing that we mature over time and we have more in common is the resources optimization for time saving. Everyone knows that time is the most important thing! The same way of saying it: time is money. The saying is based on human time. Human time must be used as an additional value for creativity, invention, construction innovation and implementation. We programmers are lucky, we have another measurement time unit, nowadays almost tending to infinity. This is machine time and we have to make the most of it. Once we used slaves, then horses, mules, engines motor. Today we have evolved, culturally and mentally. The slaves of this era are machines. By machines I mean our PCs, or better yet our servers, the cloud or robots. I decided to write this article to report useful techniques to make our work more efficient and consequently have more time. My main technique to be used to obtain this result is: write down the manual steps that we perform to do a task search for possible alternative solutions reflect on how we can automate each single step put everything together starting from point 1 if our time for the task is not equal to 0 An example Often to speed up our work we write scripts, these scripts are necessary otherwise we would have to write n command statements. Daily or monthly or after doing something else, we have to do it. So let’s open the terminal and run the scripts. To avoid launching manual the script, which implies remembering, finding the script and launching it, we can proceed in the following way: if it is something that must be done with a fixed date Schedule it using a scheduler With linux, we can make a cron We can use atrigger to execute the scheduling in the event that our script is on a server to which we do not have access to server side If instead there is no fixed scheduling date and we have to execute it as needed We can use a keyboard shortcut. In linux but also on Windows it is very simple to set a shortcut. In my case using Ubuntu just go under *settings *-> keyboard shortcuts and add our new parameter that will count the path to our script and the desired shortcut. As you can see in the following image, I use it for example to debug my blog and then to deploy: as beautiful as it may seem and can help us make us more efficient, with two buttons, you’re done, yes can always optimize. Will you say, what else you can do to optimize this? and why? The reason is simple, in order to run it I necessarily need to have my machine available and turned on. So it would be better where you can always use a server or the cloud in order to do everything remotely so that you can have more control and above all not depend on a local machine. Today there are many tools available for us to run scripts or create small web pages or small applications or showcase sites, the free plan of these services, provides many tools including: Hosting space Database space Functions for microservices Container Virtual machines and whoever has more put them There are so many that sometimes we tend not to see them, despite being under our noses. The ones I use most are: Firebase Google Cloud Amazon AWS Google Script The last service unlike the others is not a cloud service but simply an engine of functions that can be scheduled or activated in relation on google service. They are very integrated with our Google account and it will really give great potential to automate: mailing data synchronization document editing calling external API storing data in excel etc etc … it’s bad to say etc etc but really, the list would be too long, it allows you to automate everything which is Google. The other three are the top three best cloud providers. To begin with I recommend using Firebase because it offers so many services and it is simler and with a faster learning line. Conclusions We have to pay more attention to what we all do without thinking on it. If we do something repetitive every day, even if we take very little time to do it, let’s take it into consideration and think about it. After years, repetitive things can become many and we risk saturating all our time by doing only repetitive things. I leave you and greet you with a phrase that has come to me now. It probably doesn’t make sense or maybe it’s already of someone’s but I don’t know. It is better to create a system that solves a problem than to solve the problem itself!
Con questo articolo voglio spiegare come poter fare una app per il proprio canale personale youtube in 10 minuti. Io ho sviluppato in questo modo l’app Italiano con Eli, creata per il canale Italiano con Eli. Mentre stavo sviluppato l’app ho deciso di generalizzare la soluzione in modo da poter essere utilizzata da chiunque per recuperare qualsiasi video dato un determinato link del corrispettivo canale Youtube. L’applicazione completa e funzionante è disponibile al seguente link github. Potete fare un Fork o un Clone, usarla così com’è oppure modificandola come meglio vi pare. Se vi piace potete anche mettere un like. Configurazione necessaria Unica cosa da fare per poterla utilizzare è configurare la connessione alle API di Youtube in modo da poter utilizzare la ricerca dei vostri video del canale. Per una corretta configurazione potete seguire i seguenti step: Accedere al Google Api Console con il vostro account Google se non lo avete, create un nuovo account. Andare sotto progetti e cliccate su NUOVO PROGETTO, Immetti il nome e aspetta che google crei il progetto. Clicca sul link delle Google API nella libreria delle API e cerca oppure seleziona le api youtube come evidenziato nella foto di seguito dopo clicca abilità e vai sotto la voce di menu credenziali Seleziona Youtube Api v3, seleziona dati pubblici e conferma Adesso Google creerà la tua chiave di accesso l’api key recuperata al punto 6 dovrà essere inserita all’interno dell’applicazione e sostituita nella pagina main.dart al posto della key come segue: static String key = ""; // ** ENTER YOUTUBE API KEY HERE ** static String channelid = "UC_8mNVpafplqHNy85No4O2g"; // ** ENTER YOUTUBE CHANNEL ID HERE ** il channel id deve fare riferimento al vostro canale youtube. Lo potete semplicemente estrarre dal link del vostro canale youtube, prendendo la parte finale del link: La configurazione è finita, avete appena creato la vostra nuova App per il vostro canale Youtube Yehaa! Qualche cenno al codice Per agevolare lo sviluppo dell’applicazione ho usato i seguenti due plugin che potete trovate nella pagina dei plugin per flutter flutter packages i plugin utilizzati sono i seguenti: youtube player Flutter youtube Api il codice dell’applicazione è molto semplice e ho usato una struttura di suddivisione pagine abbastanza chiara: Model contiene il modello dei dati recuperati dalle youtube Api. Adesso la classe fa anche qualcosa in più, c’è un metodo che recupera i dati da youtube e li inserisce in una lista di youtube data. Usa un sistema che fa il recupero dei dati solo se non sono presenti in cache usando la flutter shared preference. Questo meccanismo è molto utile in quanto le Api di Google sono a pagamento in funzione dell’utilizzo. Utilizzando la cache risparmierete drasticamente in chiamate. se non avete mai usato la cache in flutter potete leggere quest’altro mio articolo: flutter shared preferences in profondità. Pages contiene le pagine dell’APP home page category page pagina di play video UI contiene un widget per rappresentare la lista dei video. L’ho creato a parte come widget per avere a disposizione una view da poter utilizzare sia nella pagina home che category. In questo modo, potrei usarlo anche in altre pagine in futuro. Main.dart altro non fa che inizializzare la classe del model, caricare i dati di youtube e passarli alle rispettive pagine. Utilizzando questa struttura il codice è molto semplice anche da manutenere ad esempio la pagina home contiene solo il seguente codice: import 'package:flutter/material.dart'; import 'package:youtubeapichannel/Model/YoutubeData.dart'; import 'package:youtubeapichannel/UI/YoutubeVideoList.dart'; YoutubeData youtubeData; class Home extends StatefulWidget { Home(YoutubeData inyoutubeData) { youtubeData = inyoutubeData; } @override _HomeState createState() => new _HomeState(); } class _HomeState extends State<Home> { callAPI() async { setState(() {print('UI Updated'); }); } @override void initState() { super.initState(); //callAPI(); } @override Widget build(BuildContext context) { if(youtubeData!=null) return new YoutubeVideoList(youtubeData.getHomeList()); else return new Container(child:new Text("problem loading data from youtube!")); } } Quindi riceve semplicemente in input la classe contenente la lista dei video e richiama il plugin YoutubeVideoList che sta sotto la UI per rappresentare i video sotto forma di lista di video youtube. Unica pecca, è che quando l’ho realizzata non ero tanto esperto e ho deciso di passare i dati come parametro. Oggi flutter mette a disposizioni tecniche migliori per passare i dati da una view ad un altra, in effetti dovrei fare un po' di refactor, ma oggi non ne ho voglia.
With this article I want to explain how to build an app for your personal youtube channel in 10 minutes. I did this basic project to create Italiano con Eli app, created for the YouTube channel Italiano con Eli. While I was developing the app I decided to generalize the solution so that it can retrieve any video to any Youtube channel. The complete and working application is available at the following link github. You can make a Fork or a Clone or use it as it or by modifying it as you like. If you like you can also put a like to my git project. Required configuration The only thing to do to be able to use it is to configure the connection to the Youtube API so that you can use the search for your channel videos. For a correct configuration you can follow the following steps: Log in to the Google Api Console with your Google Account if you don’t have it, create a new account. Go under projects and click on NEW PROJECT, enter the name and wait for google to create project. Click on the link in the Google API library and search or select the youtube API as highlighted in the photo below after click ability and go under the credentials menu item Select Youtube Api v3, select public data and confirm Now Google will create your api key the api key recovered in step 6 will be inserted within the application and replaced the page main.dart instead of the key as follows: static String key = ""; // ** ENTER YOUTUBE API KEY HERE ** static String channelid = "UC_8mNVpafplqHNy85No4O2g"; // ** ENTER YOUTUBE CHANNEL ID HERE ** the channel id must refer to your youtube channel. You can simply extract it from the link of your youtube channel, taking the final part of the link: The configuration is finished, you have just created your App for your personal Youtube channel! Yehaa! Some references to the code To facilitate the development of the application I used the following two flutter plugins that you can find on flutter packages pages. The plugins used are the following: youtube player Flutter youtube API the application code is very simple and I have given apage division structure fairly clear: Model contains the model of data recovered from API youtube. Now the class also does something more, there is a method that also retrieves the data from youtube and inserts it in the model and some method to recover the data. There is also a system that recovers data only if it is not cached using the shared preference flutter. This mechanism is very useful as the Google API is paid according to the use. Using the cache will save you drastically in calls. if you have never used the cache in flutter you can read this mine other article: flutter shared preferences in depth. Pages contains the APP pages home page category page play video page UI contains a widget to represent the list of videos. I created it separately as a widget to create a view that can be used on both the home page and category and I could also use it in other pages in the future. **Main.dart ** just to initialize view, the model class, load the youtube data and pass it to the respective pages. Using this structure, the code is very simple to maintain, for example the home page contains only the following code: import 'package:flutter/material.dart'; import 'package:youtubeapichannel/Model/YoutubeData.dart'; import 'package:youtubeapichannel/UI/YoutubeVideoList.dart'; YoutubeData youtubeData; class Home extends StatefulWidget { Home(YoutubeData inyoutubeData) { youtubeData = inyoutubeData; } @override _HomeState createState() => new _HomeState(); } class _HomeState extends State<Home> { callAPI() async { setState(() {print('UI Updated'); }); } @override void initState() { super.initState(); //callAPI(); } @override Widget build(BuildContext context) { if(youtubeData!=null) return new YoutubeVideoList(youtubeData.getHomeList()); else return new Container(child:new Text("problem loading data from youtube!")); } } Then simply receive the class containing the list of videos as input and call the YoutubeVideoList plugin that is under the UI to represent them in the form of a list of YouTube videos. The only flaw is that when I made it I was not so expert and I decided to pass the data as a parameter. Today flutter offers better technical provisions for passing data from one view to another, in fact I should make an update and do the usual refactor.
Nell’articolo shared preferences in depth, ho spiegato come utilizzare la shared preferences in Flutter. In questo articolo voglio invece mostrarvi una classe che ho ideato per semplificare l’utilizzo. Ho creato questa classe per l’esigenza di dover memorizzare i dati degli utenti. Ad esempio l’ho usata in Send to Kindle per memorizzare una lista di mail. L’ho usata anche per memorizzare la lista dei token e dati d’acquisto dell’utente. In generale questa struttura può essere utilizzata laddove serve lavorare con una lista di stringhe. Nell’articolo precedente, ho già mostrato come fare a memorizzare una lista di stringhe in local cache. Questa classe però rende l’utilizzo ancora più semplici e aggiunge dei metodi che ci permettono di lavorare in maniera più rapida. Come prima cosa, ho creato il metodo per salvare una lista il local cache nel seguente modo: class CashedUserData { static Future<void> _writes = Future.value(); //add list by key reference in local cache static Future<void> addlist(String kPrefKey, List<String> stringList) async{ _writes = _writes.then((void _) => _doAddList(kPrefKey,stringList)); return _writes; } } static Future<void> _doAddList(String kPrefKey, List<String> list) async { List<String> cached = await load(kPrefKey); cached.addAll(list); await (await SharedPreferences.getInstance()).setStringList(kPrefKey, cached); } Successivamente ho creato un altro metodo per recuperare la lista, inoltre ho messo qui i controlli del caso se empty o null: static Future<List<String>> load(String kPrefKey) async { return (await SharedPreferences.getInstance()).getStringList(kPrefKey) ?? []; } Man mano che la usavo ho avuto la necessità di dover aggiungere altri metodi. Metodi per: Cancellare la lista. Inserire una stringa per volta. Fare l’override della lista. Verificare se un elemento esiste. Prendere il primo elemento della lista. Salvare un elemento in top alla lista. In questo modo la classe alla fine è diventata così: class CashedUserData { static Future<void> _writes = Future.value(); static Future<void> overrideListData(String kPrefKey, List<String> stringList) async{ _writes = _writes.then((void _) => _doOverrideListData(kPrefKey,stringList)); return _writes; } static Future<void> addlist(String kPrefKey, List<String> stringList) async{ _writes = _writes.then((void _) => _doAddList(kPrefKey,stringList)); return _writes; } static Future<void> save(String kPrefKey, String id) async{ if(await CashedUserData.exist(kPrefKey, id)) return; _writes = _writes.then((void _) => _doSave(kPrefKey,id)); return _writes; } static Future<void> saveAsFirst(String kPrefKey, String id) async{ List<String> actuaList = await load(kPrefKey); if(actuaList!=null && actuaList.length == 0 ) return save(kPrefKey,id); if(actuaList.contains(id)) actuaList.remove(id); if(actuaList.length>1) actuaList.removeAt(0); List<String> newlist = new List(); newlist.add(id); newlist.addAll(actuaList); _writes = _writes.then((void _) => _doOverrideListData(kPrefKey,newlist)); return _writes; } static Future<void> delete(String kPrefKey, String id) { _writes = _writes.then((void _) => _doDelete(kPrefKey,id)); return _writes; } static Future<String> getFirst(String kPrefKey) async { List<String> dataList = (await SharedPreferences.getInstance()).getStringList(kPrefKey); return (dataList!=null && dataList.length>0) ? dataList[0] : ""; } static Future<List<String>> load(String kPrefKey) async { return (await SharedPreferences.getInstance()).getStringList(kPrefKey) ?? []; } static Future<bool> exist(String kPrefKey, String id) async { List<String> userDataList = (await SharedPreferences.getInstance()).getStringList(kPrefKey) ?? []; return (userDataList!=null && userDataList.length > 0) ? userDataList.contains(id) : false; } static Future<void> _doSave(String kPrefKey, String id) async { List<String> cached = await load(kPrefKey); cached.add(id); await (await SharedPreferences.getInstance()).setStringList(kPrefKey, cached); } static Future<void> _doDelete(String kPrefKey, String id) async { List<String> cached = await load(kPrefKey); cached.remove(id); await (await SharedPreferences.getInstance()).setStringList(kPrefKey, cached); } static Future<void> _doAddList(String kPrefKey, List<String> list) async { List<String> cached = await load(kPrefKey); cached.addAll(list); await (await SharedPreferences.getInstance()).setStringList(kPrefKey, cached); } static Future<void> _doOverrideListData(String kPrefKey, List<String> list) async { await (await SharedPreferences.getInstance()).setStringList(kPrefKey, list); } static update(String kPrefKey, String currentItem, String kindlemail) async{ List<String> actuaList = await load(kPrefKey); if(actuaList!=null && actuaList.length > 0 ) { try{actuaList[actuaList.indexOf(currentItem)] = kindlemail;} catch (e) {} } await overrideListData(kPrefKey, actuaList); } } Facendo così ho semplificato molto e reso più semplice i punti di chiamata. Inoltre se dovesse cambiare il plugin shared preference utilizzato, c’è un unico posto dove dover intervenire.
In the article shared preferences in depth, I explained how to use shared preferences in Flutter. In this article I want to show you a class that I designed to simplify its use. I created this class out of the need to store user data. For example, I used it in Send to Kindle to store a mail list. I also used it to store the user’s token list and to purchase data. Overall, this structure can be used where there is the need of working with a list of strings. In my previous article, I have already shown how to store a list of strings in local cache. This class, however, makes it even easier to use and adds methods that allow us to work faster. First, I created the method to save a local cache list as follows: class CashedUserData { static Future<void> _writes = Future.value(); //add list by key reference in local cache static Future<void> addlist(String kPrefKey, List<String> stringList) async{ _writes = _writes.then((void _) => _doAddList(kPrefKey,stringList)); return _writes; } } static Future<void> _doAddList(String kPrefKey, List<String> list) async { List<String> cached = await load(kPrefKey); cached.addAll(list); await (await SharedPreferences.getInstance()).setStringList(kPrefKey, cached); } Then I created another method to retrieve the list, I have also put the appropriate checks here, if empty or null: static Future<List<String>> load(String kPrefKey) async { return (await SharedPreferences.getInstance()).getStringList(kPrefKey) ?? []; } As I used it, I needed to add more methods. Methods to: Clear the list. Enter one string at a time Override the list Check if an element exists Take the first item on the list. Save an item at the top of the list. In this way the class eventually became like this: class CashedUserData { static Future<void> _writes = Future.value(); static Future<void> overrideListData(String kPrefKey, List<String> stringList) async{ _writes = _writes.then((void _) => _doOverrideListData(kPrefKey,stringList)); return _writes; } static Future<void> addlist(String kPrefKey, List<String> stringList) async{ _writes = _writes.then((void _) => _doAddList(kPrefKey,stringList)); return _writes; } static Future<void> save(String kPrefKey, String id) async{ if(await CashedUserData.exist(kPrefKey, id)) return; _writes = _writes.then((void _) => _doSave(kPrefKey,id)); return _writes; } static Future<void> saveAsFirst(String kPrefKey, String id) async{ List<String> actuaList = await load(kPrefKey); if(actuaList!=null && actuaList.length == 0 ) return save(kPrefKey,id); if(actuaList.contains(id)) actuaList.remove(id); if(actuaList.length>1) actuaList.removeAt(0); List<String> newlist = new List(); newlist.add(id); newlist.addAll(actuaList); _writes = _writes.then((void _) => _doOverrideListData(kPrefKey,newlist)); return _writes; } static Future<void> delete(String kPrefKey, String id) { _writes = _writes.then((void _) => _doDelete(kPrefKey,id)); return _writes; } static Future<String> getFirst(String kPrefKey) async { List<String> dataList = (await SharedPreferences.getInstance()).getStringList(kPrefKey); return (dataList!=null && dataList.length>0) ? dataList[0] : ""; } static Future<List<String>> load(String kPrefKey) async { return (await SharedPreferences.getInstance()).getStringList(kPrefKey) ?? []; } static Future<bool> exist(String kPrefKey, String id) async { List<String> userDataList = (await SharedPreferences.getInstance()).getStringList(kPrefKey) ?? []; return (userDataList!=null && userDataList.length > 0) ? userDataList.contains(id) : false; } static Future<void> _doSave(String kPrefKey, String id) async { List<String> cached = await load(kPrefKey); cached.add(id); await (await SharedPreferences.getInstance()).setStringList(kPrefKey, cached); } static Future<void> _doDelete(String kPrefKey, String id) async { List<String> cached = await load(kPrefKey); cached.remove(id); await (await SharedPreferences.getInstance()).setStringList(kPrefKey, cached); } static Future<void> _doAddList(String kPrefKey, List<String> list) async { List<String> cached = await load(kPrefKey); cached.addAll(list); await (await SharedPreferences.getInstance()).setStringList(kPrefKey, cached); } static Future<void> _doOverrideListData(String kPrefKey, List<String> list) async { await (await SharedPreferences.getInstance()).setStringList(kPrefKey, list); } static update(String kPrefKey, String currentItem, String kindlemail) async{ List<String> actuaList = await load(kPrefKey); if(actuaList!=null && actuaList.length > 0 ) { try{actuaList[actuaList.indexOf(currentItem)] = kindlemail;} catch (e) {} } await overrideListData(kPrefKey, actuaList); } } By doing this I simplified the call points. Furthermore, if the plugin shared preference used changed, there would only be one place where to take action.
Prerequisiti Questo articolo richiede una discreta conoscenza dei seguenti argomenti che potete approfondire ai seguenti link: Firebase Remote Config Flutter Shared Preferences Come mai vi parlo di questi due plugin che sembrerebbero non avere niente a che fare l’uno con l’altro? Con questa guida, vi illustrerò come trarre beneficio da questi due plugin e come risparmiare usando i servizi gratuiti offerti da Firebase! :) Nell’app Italiano con Eli, creata per il canale Italiano con Eli, ho creato un progetto su Firebase, in modo da poter utilizzare il remote config e altre potenzialità che Firebase mette a disposizione. L’app si occupa di mostrare una lista video da canale youtube, se interessati potete scaricare il codice sorgente su github, potete utilizzarlo con qualsiasi canale youtube. Per rendere l’app Italiano con Eli più originale ho pensato di aggiungere ai video delle domande sugli argomenti trattati nei video. Avevo quindi bisogno di un posto dove salvare i dati delle domande. Con Firebase, la cosa più logica e anche più diretta sarebbe quella di usare Realtime Database oppure Firestore. Io ho pensato di utilizzare remote config! Sì, avete capito bene! Remote config! Il primo pensiero che vi sta saltando in mente in questo momento sarà “questo è pazzo!” e il secondo “ma perchè?” condivido pienamente con voi il vostro primo pensiero… c’è un pò di pazzia nella scelta adottata. Per quanto riguarda il perché invece, posso rispondere come segue: non avevo molto tempo a disposizione remote config è gratis e non ha limitazione di utilizzo si può dare accesso al servizio a una persona esterna le altre scelte mi avrebbero portato a dover costruire anche un’interfaccia back end per il censimento delle domande Conoscevo già remote config e sarei stato più rapido nell’implementazione Ma come funziona un servizio non nato per questo lavoro? Funziona benissimo, devo dire che mi ha sorpreso la reattività che dimostra l’app e il sistema che ho messo in piedi. Per farlo funzionare così bene però ho dovuto mettere assieme i due plugin nel modo che vi spiego di seguito. All’interno della console di remote config vado a inserire un nuovo parametro per ogni video. Nel campo nome inserisco l’id del video di youtube e all’interno del parametro un json contenente le domande e le risposte così strutturato: { "Playlist": "PLsrqydfBIVzy9IMGwSQieTVeSWC7cRCnN", "Questions": [ { "question": "How do you say how much is it??: ", "answare": [ "Quanto costa?", "Quanto costano? ", "Quanto costi?" ] }, { "question": "How do you say how much are they?: ", "answare": [ "Quanto costano?", "Quanto costate?", "entrambe/both" ] }, { "question": "Choose the correct option", "answare": [ "Quanto costa questo computer? 300 Euro", "quanto computer ti costa? 300 Euro", "Quanto costano questo computer?" ] }, { "question": "Quanto costa? is", "answare": [ "singolare", "plurale" ] } ] } Inoltre firebase mette a disposizione un inserimento facilitato che effettua anche la validazione del json, questo ci permette di editare più facilmente e non commettere errori. Nell’app Flutter ho poi costruito l’oggetto Quiz che conterrà l’id di youtube, una lista di Domande e la Domanda conterrà una lista di Risposte. Con vari metodi utili di gestione e costruttore per deserializzazione dei dei dati Json come segue: class Answer { final String answerText; final bool isCorrect; Answer(this.answerText, this.isCorrect); } class Question { final String question; final List<Answer> answers; Question(this.question, this.answers); } class Quiz { List<Question> _questions; int _currentIndex = -1; int _score = 0; String _videoId; Quiz.fromJson(String videoId, String jsonInput){ _videoId = videoId; _questions = new List<Question>(); var jsonQuestions = json.decode(jsonInput); jsonQuestions['Questions'].forEach((quest) { List<Answer> answers = new List<Answer>(); bool iscorrect=true; quest['answare'].forEach((ans) { //mettere la prima a true answers.add( new Answer(ans, iscorrect) ); iscorrect=false; }); answers.shuffle(); _questions.add( new Question( quest['question'], answers) ); }); } Quiz(this._questions) { _questions.shuffle(); } List<Question> get questions => _questions; int get length => _questions.length; int get questionNumber => _currentIndex + 1; int get score => _score; Question get nextQuestion { _currentIndex++; if (_currentIndex >= length) return null; return _questions[_currentIndex]; } Question get prevQuestion { _currentIndex--; if (_currentIndex < 0) return null; return _questions[_currentIndex]; } String get correctAnsware { return _questions[_currentIndex].answers.where((x) => x.isCorrect == true).first.answerText; } void answer(bool isCorrect) { if (isCorrect) _score++; } saveScore() async { SharedPreferences preferences = await SharedPreferences.getInstance(); preferences.setInt("OK"+_videoId, this.score); preferences.setInt("KO"+_videoId, this.length - this.score ); //preferences.setString(_videoId, "{'questions':'"+ this.length.toString() +"', 'score':'"+ this.score.toString() +"'}"); } } Per prima cosa, l’App recupera la lista dei video tramite le API youtube, successivamente recupera il relativo json da remote config utilizzando l’id del video e lo passa al costruttore dell’oggetto che lo deserializza ed il gioco è fatto! Una volta ultimata l’app mi sono accorto però che youtube mette a disposizione un numero limitato di chiamate API giornaliere e oltretutto ho pensato che fosse inutile che l’App, ogni volta che viene aperta, debba richiamare le API Youtube. I video oltretutto non cambiano tanto in quanto questo canale produce un nuovo video a settimana. Per risolvere il problema delle chiamate API Youtube e rendere il tutto più reattivo ho deciso di salvare tutto in una lista di stringhe json in cache utilizzando proprio la Shared Preferences. Infine ho aggiunto un parametro nella remote config contente una data. In questo modo ho fatto un meccanismo che mi permette di aggiornare i contenuti nei dispositivi solo se la data è più recente rispetto a quella memorizzata in cache del singolo dispositivo stesso: class YoutubeData { RemoteConfig remoteConfig; String apikey; List<YoutubeDto> allYoutubeVideoList = []; List<YoutubeDto> getHomeList() { return List<YoutubeDto>.from(allYoutubeVideoList.where((item) => item.kind == "video" && item.id != item.channelId )); } List<YoutubeDto> allCategory() { return List<YoutubeDto>.from(allYoutubeVideoList.where((item) => item.kind == "playlist" && item.id != item.channelId )); } List<YoutubeDto> getCategoryVideoList(String plylistID) { return List<YoutubeDto>.from(allYoutubeVideoList.where((item) => item.kind == "video" && item.id != item.channelId && item.playlist == plylistID )); } YoutubeData (this.apikey, this.remoteConfig); checkAndLoad() async { SharedPreferences preferences = await SharedPreferences.getInstance(); List<String> videoIDList = preferences.getStringList("VideoIdList"); DateTime lastUpdateDate,updateDate; String date = remoteConfig.getString("UpdateDatetime"); updateDate = DateTime.parse(date); if(preferences.getString("LastUpdateDatetime")!=null && preferences.getString("LastUpdateDatetime").isNotEmpty) lastUpdateDate = DateTime.parse(preferences.getString("LastUpdateDatetime")); else lastUpdateDate = DateTime(2010); if( (videoIDList==null || videoIDList.length==0) || updateDate.isAfter(lastUpdateDate)) { await loadYtDataAndStoreInSharedPref(); preferences.setString("LastUpdateDatetime", DateTime.now().toString() ); } await loadLocalDataAndAddQuestion(); } loadYtDataAndStoreInSharedPref() async { YoutubeAPI ytApi = new YoutubeAPI(apikey, maxResults: 50); List<YT_API> ytResult = await ytApi.channel( remoteConfig.getString('ChannelId') ); //salviamo il conteggio dei video in shared pref SharedPreferences preferences = await SharedPreferences.getInstance(); preferences.setInt("TotVideo", ytResult.length ); //salviamo json youtube video in shared pref e lista video YoutubeDto youtubedto = new YoutubeDto(); List<String> videoIdList = new List<String>(); for(var result in ytResult) { videoIdList.add( result.id); String json = jsonEncode( youtubedto.yApitoJson(result) ); preferences.setString( result.id, json ); } preferences.setStringList("VideoIdList", videoIdList ); } //aggiungiamo le domande agli oggetti youtube precedentemente salvati loadLocalDataAndAddQuestion() async { SharedPreferences preferences = await SharedPreferences.getInstance(); List<String> videoIDList = preferences.getStringList("VideoIdList"); allYoutubeVideoList = new List<YoutubeDto>(); int cntQuestion=0; for(String videoID in videoIDList) { String jsonYoutube = preferences.getString(videoID); Map userMap = jsonDecode(jsonYoutube); YoutubeDto youtubeDao = new YoutubeDto.fromJson(userMap); youtubeDao.questionJson = remoteConfig.getString( "_" + videoID.replaceAll("-", "_trattino_") ); if( youtubeDao.questionJson != null && youtubeDao.questionJson.isNotEmpty ) { cntQuestion = cntQuestion + 'question'.allMatches(youtubeDao.questionJson).length; youtubeDao.playlist = json.decode( youtubeDao.questionJson)['Playlist']; } allYoutubeVideoList.add(youtubeDao); } preferences.setInt("TotQuestion", cntQuestion ); } } Qui di seguito potete vedere un esempio di come funziona:
Prerequisites This article requires a good knowledge of the following topics that you can study at the following links: Firebase Remote Config Flutter Shared Preferences Why are we talking about these two plugins that would see having nothing in common? i With this guide I will show you how to benefit from this two plugins and how to save money using the free services offered by Firebase! :) In the talian with Eli app, created for the youtube channel Italian with Eli, I created a project on Firebase, so you can use the remote config and other potential that Firebase makes available for you. The app takes care of showing a video list from the youtube channel, if interested you can download the source code on github, you can use it with any youtube channel. To make the Italian with Eli app more original I thought of adding questions to the videos on the topics covered in the videos. So I needed a place to save the question data. With Firebase, the most logical and also most direct thing would be to use Realtime Database or Firestore. I thought of using remote config! Yes you got it right remote config! You will be thinking “this guy is crazy” and “why is he doing that?” I fully share with you this thought… there is a bit of madness in the choice made. The reason why I thought of using remote config is the following: I didn’t have much time available remote config is free and has no limitation of use you can give access to the service to an external person the other choices would have led me to have to also build a back end interface to allow the census of the questions I had already used remote config and I would have been faster in the implementation. But how does a service not born with this scope work? It works very well indeed, I must say that I was surprised by the responsiveness that shows the app and the system that I put up. To make it work so well, however, I had to put the two plugins together in the way that I explain below. Inside the remote config console I’m going to insert a new parameter for each video. In the name field I insert the id of the youtube video and inside the parameter a json containing the questions and answers structured as follows: { "Playlist": "PLsrqydfBIVzy9IMGwSQieTVeSWC7cRCnN", " Questions ": [ { " question ":" How do you say how much is it ??: ", " answare ": [ " How much does it cost? ", " How much does it cost? ", " How much does it cost? " ] }, { "question": "How do you say how much are they ?:", "answare": [ "How much do they cost?", "How much do you cost?", "both / both" ] }, { "question" : "Choose the correct option", "answare": [ "How much does this computer cost? 300 Euros", "How much computer does it cost? 300 Euros", "How much does this computer cost?" ] }, { "question": "How much does it cost? is", "answare": [ "singular", "plural" ] } ] } In addition, firebase provides an easy insertion that also performs json validation, this allows us to edit more easily and not to make mistakes. In the Flutter app I then built the Quiz object which contains the youtube id, a list of Questions and the Question will contain a list of Answers. With various useful management and constructor methods for deserializing Json data as follows: class Answer { final String answerText; final bool isCorrect; Answer (this.answerText, this.isCorrect); } class Question { final String question; final List <Answer> answers; Question (this.question, this.answers); } class Quiz { List <Question> _questions; int _currentIndex = -1; int _score = 0; String _videoId; Quiz.fromJson (String videoId, String jsonInput) { _videoId = videoId; _questions = new List <Question> (); var jsonQuestions = json.decode (jsonInput); jsonQuestions ['Questions']. forEach ((quest) { List <Answer> answers = new List <Answer> (); bool iscorrect = true; quest ['answare']. forEach ((ans) { // put the first to true answers.add (new Answer (ans, iscorrect)); iscorrect = false; }); answers.shuffle (); _questions.add (new Question (quest ['question'], answers)); }); } Quiz (this._questions) { _questions.shuffle (); } List <Question> get questions => _questions; int get length => _questions.length; int get questionNumber => _currentIndex + 1; int get score => _score; Question get nextQuestion { _currentIndex ++; if (_currentIndex> = length) return null; return _questions [_currentIndex]; } Question get prevQuestion { _currentIndex--; if (_currentIndex <0) return null; return _questions [_currentIndex]; } String get correctAnsware { return _questions [_currentIndex] .answers.where ((x) => x.isCorrect == true) .first.answerText; } void answer (bool isCorrect) { if (isCorrect) _score ++; } saveScore () async { SharedPreferences preferences = await SharedPreferences.getInstance (); preferences.setInt ("OK" + _ videoId, this.score); preferences.setInt ("KO" + _ videoId, this.length - this.score); //preferences.setString(_videoId, "{'questions': '" + this.length.toString () + "', 'score': '" + this.score.toString () + "'}"); } } The app first retrieves the list of videos via the youtube API, then retrieves the relative json from remote config using the video id and passes it to the constructor of the object that deserialize and you’re done. Once the app was completed, however, I realized that youtube provides a limited number of daily API calls. Moreover I thought it was useless every time I opened the app that it had to call the Youtube API. The videos, moreover, do not change as much as the channel makes a new video a week. To solve the problem of Youtube API calls and make everything more responsive, I decided to save everything in a list of cached json strings using the Shared Preferences. Finally I added a parameter in the remote config containing a date. In this way I made a mechanism that allows me to update the contents on the devices only if the date is more recent than the one stored in the cache of the single device itself: class YoutubeData { RemoteConfig remoteConfig; String apikey; List <YoutubeDto> allYoutubeVideoList = []; List <YoutubeDto> getHomeList () { return List <YoutubeDto> .from (allYoutubeVideoList.where ((item) => item.kind == "video" && item.id! = Item.channelId)); } List <YoutubeDto> allCategory () { return List <YoutubeDto> .from (allYoutubeVideoList.where ((item) => item.kind == "playlist" && item.id! = Item.channelId)); } List <YoutubeDto> getCategoryVideoList (String plylistID) { return List <YoutubeDto> .from (allYoutubeVideoList.where ((item) => item.kind == "video" && item.id! = Item.channelId && item.playlist == plylistID)); } YoutubeData (this.apikey, this.remoteConfig); checkAndLoad () async { SharedPreferences preferences = await SharedPreferences.getInstance (); List <String> videoIDList = preferences.getStringList ("VideoIdList"); DateTime lastUpdateDate, updateDate; String date = remoteConfig.getString ("UpdateDatetime"); updateDate = DateTime.parse (date); if (preferences.getString ("LastUpdateDatetime")! = null && preferences.getString ("LastUpdateDatetime"). isNotEmpty) lastUpdateDate = DateTime.parse (preferences.getString ("LastUpdateDatetime")); else lastUpdateDate = DateTime (2010); if ((videoIDList == null || videoIDList.length == 0) || updateDate.isAfter (lastUpdateDate)) { await loadYtDataAndStoreInSharedPref (); preferences.setString ("LastUpdateDatetime", DateTime.now (). toString ()); } await loadLocalDataAndAddQuestion (); } loadYtDataAndStoreInSharedPref () async { YoutubeAPI ytApi = new YoutubeAPI (apikey, maxResults: 50); List <YT_API> ytResult = await ytApi.channel (remoteConfig.getString ('ChannelId')); // save the video count in shared pref SharedPreferences preferences = await SharedPreferences.getInstance (); preferences.setInt ("TotVideo", ytResult.length); // save json youtube video in shared pref andvideo list YoutubeDtoyoutubedto = new YoutubeDto (); List <String> videoIdList = new List <String> (); for (var result in ytResult) { videoIdList.add (result.id); String json = jsonEncode (youtubedto.yApitoJson (result)); preferences.setString (result.id, json); } preferences.setStringList ("VideoIdList", videoIdList); } // add the questions to the previously saved youtube objects loadLocalDataAndAddQuestion () async { SharedPreferences preferences = await SharedPreferences.getInstance (); List <String> videoIDList = preferences.getStringList ("VideoIdList"); allYoutubeVideoList = new List <YoutubeDto> (); int cntQuestion = 0; for (String videoID in videoIDList) { String jsonYoutube = preferences.getString (videoID); Map userMap = jsonDecode (jsonYoutube); YoutubeDto youtubeDao = new YoutubeDto.fromJson (userMap); youtubeDao.questionJson = remoteConfig.getString ("_" + videoID.replaceAll ("-", "_trattino_")); if (youtubeDao.questionJson! = null && youtubeDao.questionJson.isNotEmpty) { cntQuestion = cntQuestion + 'question'.allMatches (youtubeDao.questionJson) .length; youtubeDao.playlist = json.decode (youtubeDao.questionJson) ['Playlist']; } allYoutubeVideoList.add (youtubeDao); } preferences.setInt ("TotQuestion", cntQuestion); } } Below you can see an example of how it works:
Il nome scelto da Google questa volta è molto comprensivo ed esaustivo. Firebase remote config è una configurazione salvata in remoto, in questo caso su Google Cloud. La configurazione è accessibile in modifica nella console di Firebase, alla voce di menù Firebase remote config che si trova in fondo, molto in fondo, nell’ultima sezione secondo me un pò nascosta. Prerequisiti: creare un account su Firebase utilizzando il nostro account google creare un app, io ho usato flutter collegare la nostra app al codice sorgente con la chiave json Facendo questo potremmo utilizzare tutti i servizi Firebase, ci sono tanti tutorial online e Firebase stesso mette a disposizione wizard con step per guidarvi alla corretta implementazione di Firebase con la vostra app.Possiamo usare Firebase Remote Config per memorizzare tutto quello che la nostra app necessità come parametri di configurazioni. Per prima cosa, dobbiamo individuare e mettere in configurazione ciò che pensiamo possa cambiare nel tempo, in questo modo avremo delle configurazioni nell’app modificabili in tempo reale da remoto in modo da non dover rieffettuare un nuovo deploy dell’app stessa. Un esempio può essere il caso in cui stiamo utilizzando un Api per recuperare dei dati, l’url base delle api potrebbe cambiare nel tempo. Un altro esempio più concreto che utilizzo io nell’app send to kindle è un parametro che contiene il numero di azioni prima di mostrare una pubblicità. In questo modo se gli utenti si lamentano per le troppe pubblicità posso cambiarlo in tempo reale con il solo cambio di un parametro da interfaccia web a costo zero. Il sistema di Firebase funziona davvero alla grande, c’è dietro un sistema push-lazy che utilizza i dati internet per recuperare le configurazioni solo se davvero abbiamo cambiato qualcosa lato server e quindi abbiamo pubblicato una modifica ad uno o più parametri. L’aggiornamento dei parametri avviene in real time e quindi la modifica sarà subito disponibile nell’app. I parametri possono essere anche associati a delle condizioni. Ad esempio un parametro può essere utilizzato solo per gli utenti di una certa età di un determinato paese o lingua a un determinato orario. Figata no? Vantaggi Controllo da remoto istantaneo, semplice e centralizzato. Modifica App in tempo reale. Diminuisce la necessità di dover effettuare un nuovo deploy. i parametri possono essere combinati con delle condizioni. Come lo uso io nelle mie app? Di seguito voglio mostrarvi un esempio di implementazione. In questo modo ho centralizzato la parte di inizializzazione e creazione dei parametri in modo che siano più accessibili. Ho deciso di utilizzare un singleton in modo da poterlo usare in tutte le view e in tutte le classi per cui ne ho bisogno, implementato nel seguente modo: import 'package:firebase_remote_config/firebase_remote_config.dart'; class Configuration { int _link_sent_before_ads; int _max_send_limit_size = 24000000; int get linkSentBeforeAds => _link_sent_before_ads; int get maxSendLimitSize => _max_send_limit_size; int get maxSendLimitSizeMb => (_max_send_limit_size/1000000).round(); static Configuration get instance => _instance; static final Configuration _instance = Configuration._privateConstructor(); Configuration._privateConstructor() { //initializeRemoteConfig(); } loadConfig(RemoteConfig _remoteConfig) async { _link_sent_before_ads = _remoteConfig.getString('link_sent_before_ads').isEmpty ? _link_sent_before_ads : int.tryParse(_remoteConfig.getString('link_sent_before_ads')); _max_send_limit_size = _remoteConfig.getString('max_send_limit_size').isEmpty ? _max_send_limit_size : int.tryParse(_remoteConfig.getString('max_send_limit_size')); } initializeRemoteConfig() async{ RemoteConfig remoteConfig = await RemoteConfig.instance; try { //set some default params in case of crash await remoteConfig.setConfigSettings(RemoteConfigSettings(debugMode: false)); await remoteConfig.setDefaults(<String, dynamic>{ 'service_api_conversion_1': 'https://ebook-converter.app/calibre/ebook-convert', }); // Using default duration to force fetching from remote server. await remoteConfig.fetch(expiration: const Duration(seconds: 0)); await remoteConfig.activateFetched(); } on FetchThrottledException catch (exception) { // Fetch throttled. print(exception); } catch (exception) { print('Unable to fetch remote config. Cached or default values will be used'); } await this.loadConfig(remoteConfig); } } potrà essere infine utilizzato molto semplicemente nel seguente modo: //call this just first time in app initialization await Configuration.instance.initializeRemoteConfig(); Print(Configuration.instance.maxSendLimitSize);
What is shared_preferences where, when and how much to use it? The shared_preference is a plugin that allows you to save data app dedicated memory, the folder that will contain this data is named in Android with the package name of your app, e.g. com.companyname.appname. The data that we can store in this space with this plugin are data of these types: String Integer Bool List It should be noted that these parameters are not persistent, they remain in the area dedicated to the app therefore are eliminated when the app is uninstalled or if the app cache data is removed. So, in general it is right to store non-essential preference data so that it is not a tragedy if they have been lost. A classic example can be the app theme color light or dark or the order of menu title. How to use? Saving: import 'package:shared_preferences/shared_preferences.dart'; SharedPreferences _preferences = await SharedPreferences.getInstance(); _preferences.setString("theme", "dark"); _preferences.setInt("countaccess", 2); Recovery: import 'package:shared_preferences/shared_preferences.dart'; SharedPreferences _preferences = await SharedPreferences.getInstance(); _preferences.getString("theme"); _preferences.getInt("countaccess"); At first we might think that it is of little use since in a string we cannot store a lot of information. In fact, if we think about it in a string, we can also store a lot but really a lot by using a json, below an example: import 'package:shared_preferences/shared_preferences.dart'; SharedPreferences _preferences = await SharedPreferences.getInstance(); _preferences.setString("{"name":"Andrea","surname":"Luciano","age":31,"sex":"M","SurnameCouldBeName":true}"); In this example I put SurnameCouldBeName as a field because my last name is also a first name. All those who don’t know me always ask me “Luciano is your name or surname?” And every time I would like to do something like that: We can therefore create a collection of people using a list of strings instead of the single string as follows: import 'package:shared_preferences/shared_preferences.dart'; SharedPreferences _preferences = await SharedPreferences.getInstance(); List<String> users = new List<String>(); users.add("{"name":"Andrea","surname":"Luciano","age":31,"sex":"M","SurnameCouldBeName":true}"); users.add("{"name":"elisa","surname":"luciano","age":32,"sex":"F","SurnameCouldBeName":false}"); _preferences.setStringList("users", users); This could finally be mapped into an object in order to access the data simply and finally show it to the interface: Class UserDto() { String name, surname, age, sex, SurnameCouldBeName; UserDto.fromJson(Map<String, dynamic> json) : name= json[name], surname= json[surname], age= json[age], sex= json[sex], SurnameCouldBeName = json[SurnameCouldBeName]; } SharedPreferences _preferences = await SharedPreferences.getInstance(); List<UserDto> UserObj = new List<UserDto>; List<String> users = _preferences.getStringList("users"); for(String userjson in users) { Map userMap = jsonDecode(userjson); UserDto = new UserDto.fromJson(userMap); print(UserDto.name); UserObj.add(UserDto); } Think about the possible benefits and applications of this. It can be used to store data returned by an API. In this way, the application would be more efficient and also can work offline without therefore having to use network data for recovering data from the server.
Che cos’è la shared_preferences dove, quando e quanto utilizzarla? La shared_preference è un plugin che vi permette di salvare dei dati in memoria dedicata all’app, la cartella che li conterrà prende il nome in android con il nome del pacchetto della vostra app es com.nomeazienda.nomeapp. I dati che possiamo memorizzare all’interno di questa memoria con questo plugin sono dei seguenti tipi: string intero bool List E’ da notare che questi parametri non sono persistenti, rimangono nell’area dedicata all’app e quindi vengono eliminate nel momento in cui l’app viene disinstallata oppure se vengono rimossi i dati della cache dell’app. Quindi in genere è doveroso memorizzare dati di preferenze non indispensabili in modo che non sia una tragedia se dovessero essere persi. Un esempio classico può essere come voglio il tema light o dark oppure l’ordine degli oggetti nel menu. Come si usa? Salvataggio: import 'package:shared_preferences/shared_preferences.dart'; SharedPreferences _preferences = await SharedPreferences.getInstance(); _preferences.setString("theme", "dark"); _preferences.setInt("countaccess", 2); Recupero: import 'package:shared_preferences/shared_preferences.dart'; SharedPreferences _preferences = await SharedPreferences.getInstance(); _preferences.getString("theme"); _preferences.getInt("countaccess"); In un primo momento potremmo pensare che è una cosa che serve a poco in quanto in una stringa non possiamo memorizzare tante informazioni. In realtà, se ci pensiamo bene, in una stringa possiamo anche memorizzare molto ma davvero molto semplicemente utilizzando un json, di seguito esempio: import 'package:shared_preferences/shared_preferences.dart'; SharedPreferences _preferences = await SharedPreferences.getInstance(); _preferences.setString("{"name":"Andrea","surname":"Luciano","age":31,"sex":"M","SurnameCouldBeName":true}"); In questo esempio ho messo come campo SurnameCouldBeName in quanto il mio cognome è anche un nome. Tutti quelli che non mi conoscono mi chiedono sempre “Luciano è il nome o il cognome?” E ogni volta vorrei fare qualcosa del genere: Possiamo quindi creare una collezione di persone utilizzando, al posto della singola stringa, una lista di stringhe come segue: import 'package:shared_preferences/shared_preferences.dart'; SharedPreferences _preferences = await SharedPreferences.getInstance(); List<String> users = new List<String>(); users.add("{"name":"Andrea","surname":"Luciano","age":31,"sex":"M","SurnameCouldBeName":true}"); users.add("{"name":"elisa","surname":"luciano","age":32,"sex":"F","SurnameCouldBeName":false}"); _preferences.setStringList("users", users); Questo infine potrebbe essere mappato in un oggetto in modo da accedere ai dati più semplicemente ed infine mostrarli nell’interfaccia: Class UserDto() { String name, surname, age, sex, SurnameCouldBeName; UserDto.fromJson(Map<String, dynamic> json) : name= json[name], surname= json[surname], age= json[age], sex= json[sex], SurnameCouldBeName = json[SurnameCouldBeName]; } SharedPreferences _preferences = await SharedPreferences.getInstance(); List<UserDto> UserObj = new List<UserDto>; List<String> users = _preferences.getStringList("users"); for(String userjson in users) { Map userMap = jsonDecode(userjson); UserDto = new UserDto.fromJson(userMap); print(UserDto.name); UserObj.add(UserDto); } Pensate ai possibili vantaggi e alle possibili applicazioni. Può essere utilizzato per memorizzare dati restituiti da un API. Con questo approccio, l’applicazione è più efficiente e potrebbe funzionare anche offline senza quindi dover utilizzare sempre la rete dati per recuperare i dati dal server.
The name chosen by Google this time is very comprehensive and exhaustive. Firebase remote config is a remotely saved configuration, in this case on Google Cloud. This configuration is accessible to insert and update in the Firebase console, under the menu item Firebase remote config which is located at the very bottom, in the last section, in my opinion a little hidden. Prerequisites: create an account on Firebase using our google account create an app, I used flutter connect our app to the source code using json key file By doing this we could use all the Firebase services, there are many online tutorials and Firebase itself provides wizards with steps to guide you to the correct implementation of Firebase with your app. We can use Firebase Remote Config to store everything our app needs as configuration parameters. First of all, We must identify and configure what we think will change over time. In this way we will have configurations in the app that can be modified in real time remotely so as not to have to carry out a new app deployment. An example may be the case in which we are using an API to recover data, the base url may change over time. Another more down to earth-example that I use in the send to kindle app is a parameter that contains the number of actions before showing an advertisement. In this way, if users complain about too many advertisements, I can change it in real time with just changing a parameter into the web interface at no cost. The Firebase system works really well, there is a push-lazy system behind it that uses internet data to retrieve the configurations only if we have really changed something on the server side and therefore we have published a change to one or more parameters. The updating of the parameters takes place in real time so it changes immediately the behavior, state or data in app as real time. Parameters can also be associated with conditions. For example, a parameter can only be used for users of a certain age in a certain country or language at a specific time. Cool! isn’t it? Advantages Instant and simple remote control. Real-time app customization. It decreases the need for a new deployment. Parameters can be combined with conditions. How do I use it in my apps? Below I want to show you an example of implementation. In this way I centralized the initialisation and parameter creation part so that they are more accessible. I decided to use a singleton so that I can use it in all the views and all the classes I need it, implemented in the following way: import 'package:firebase_remote_config/firebase_remote_config.dart'; class Configuration { int _link_sent_before_ads; int _max_send_limit_size = 24000000; int get linkSentBeforeAds => _link_sent_before_ads; int get maxSendLimitSize => _max_send_limit_size; int get maxSendLimitSizeMb => (_max_send_limit_size/1000000).round(); static Configuration get instance => _instance; static final Configuration _instance = Configuration._privateConstructor(); Configuration._privateConstructor() { //initializeRemoteConfig(); } loadConfig(RemoteConfig _remoteConfig) async { _link_sent_before_ads = _remoteConfig.getString('link_sent_before_ads').isEmpty ? _link_sent_before_ads : int.tryParse(_remoteConfig.getString('link_sent_before_ads')); _max_send_limit_size = _remoteConfig.getString('max_send_limit_size').isEmpty ? _max_send_limit_size : int.tryParse(_remoteConfig.getString('max_send_limit_size')); } initializeRemoteConfig() async{ RemoteConfig remoteConfig = await RemoteConfig.instance; try { //set some default params in case of crash await remoteConfig.setConfigSettings(RemoteConfigSettings(debugMode: false)); await remoteConfig.setDefaults(<String, dynamic>{ 'service_api_conversion_1': 'https://ebook-converter.app/calibre/ebook-convert', }); // Using default duration to force fetching from remote server. await remoteConfig.fetch(expiration: const Duration(seconds: 0)); await remoteConfig.activateFetched(); } on FetchThrottledException catch (exception) { // Fetch throttled. print(exception); } catch (exception) { print('Unable to fetch remote config. Cached or default values will be used'); } await this.loadConfig(remoteConfig); } } It can finally be used very simply in the following way: //call this just first time in app initialization await Configuration.instance.initializeRemoteConfig(); Print(Configuration.instance.maxSendLimitSize);
Gino apri gli occhi, quella mattina si svegliò un po’ turbato di ciò che era avvenuto la sera prima ma soprattutto con un brutto pensiero, forse aveva fatto un incubo. Sollevò lo sguardo, guardò l’orologio e la sua faccia si scurì immediatamente. In un attimo aveva realizzato che era giovedì e doveva fare una commissione. Non aveva la minima voglia di fare questa commissione e rabbrividì al solo pensiero. Si girò d’altro lato sperando di addormentarsi nuovamente e di svegliarsi il giorno dopo. Chiuse a malapena una palpebra quando sentì una voce dal profondo e non era un incubo, purtroppo, era sua mamma che urlava: Gino svigghiati chi iè taddu. Ti ricoddi chi m’ha spicciari da cosa a posta? U sai chi io non ci pozz’ annari! 1 Quindi, era tutto vero. Sua mamma aveva bisogno di lui, ma la commissione alla posta Gino non l’aveva mai mandata giù, la faceva controvoglia e lo metteva di malumore. Allora risponde alla mamma: mamma fammi dommiri, fammi dommiri autri 10 minuti! Poi mi siddiu e sugnu stancu e u sai che na suppottu Naiu Valia! 2 la mamma gli urlò subito contro insistentemente fin tanto che Gino decise di alzarsi. Scese le scale con i capelli e la faccia stropicciata, prese il caffè e lì accanto trovò le carte, carte che doveva portare alla posta. Le guardò, poi si mise a giocare con la tazzina di caffè mettendola vicino agli occhi in modo da fare sparire le carte dal suo sguardo. Alla fine si rassegnò, si alzò, prese le carte e uscì. Attraversò il vialetto e si ritrovò di fronte alla piazza del paese. In piazza a distanza vide due donne, già da lontano riconobbe Cammela a cuttighiara 3 che parlava con una certa veemenza che la caratterizzava. Stava raccontando alle vicine la disavventura proprio di Gino del giorno prima “Scinni Papà”. Carmela non appena lo vide arrivare, cambiò subito discorso, tanto sapeva tutto di tutti e aveva tanti cuttigghi a disposizione da raccontare. Gino si sentì un po’ osservato mentre passava e percepì in sottofondo un po’ di sghignazzi. Una volta in piazza, vide Don Gianni che stava lì seduto ad aspettare che il tempo passasse. Don Gianni salutò Gino con molto rispetto e un bel sorriso affettuoso, erano molto amici i due. Gino ricambiò. Poco più avanti Ciccio invece gli disse: Ma tò patri a unni u lassasti? Aoggi nun scinniu? 4 chiaramente una provocazione, una presa in giro poco velata da parte di Ciccio. A quel punto Gino capì che si riferiva al giorno precedente e allora rispose a tono: Ciccio Birra ma picchì non ti bivi na birra e ti cucchi? 5 Proseguì però senza fermarsi, non voleva problemi quella mattina, già ne aveva tanti, la sua missione principale era la commissione in posta. Superata la piazza, si ritrova di fronte alla posta. Già in lontananza poteva sentire la puzza di sigaretta misto profumo d’arancia e di cocco di Naiu Valia e già gli veniva un senso di vomito. Non appena arrivò all’ingresso la vide. Naiu Valia era lì, sicca sicca, con i capelli scuri, la faccia scura, vestita di nero, come sempre, a non fare nulla. Aveva quello sguardo rilassato ma anche minaccioso e incavolato. Allora Gino entra in silenzio e lei subito a tono: cu non mori si rividi! Ma cu ti porta? C’ha fari? 6 Gino rispose dicendo che doveva fare due raccomandate, prendere un pacchetto…. non lo fece neanche finire di parlare che rispose Ma chi ti sbigghiasti ora? tutto oggi a fari? iè tardu, su i 10, no sai chi io a fari a pausa? Si voi mi poi spettari ca, poi quannu tornu videmu chi putemu fari. 7 Gino non aveva voglia di problemi quella mattina allora rispose che andava bene e si sedette. Aspettò lì seduto sino alle 11 ma niente, di Naiu Valia nessuna traccia. Allora si fece una passeggiata e andò a bere un po’ d’acqua al lavatoio (posto utilizzato una volta per lavare i panni). Una volta ritornato vide due vecchiette dentro che dovevano ritirare la pensione e Naiu Valia che era appena tornata. Gino allora si avvicinò e Naiu Valia lo richiamò subito Gino ma che fai? non vedi che c’è gente. Devi aspettare il tuo turno e prendi il numerino. Mica siamo al mercato qui! allora Gino stava per innervosirsi ma sapeva che non era il caso e non poteva perchè se tornava a casa senza aver sbrigato le commissioni poi sentila la mamma. Allora disse: va bene aspetto e prese il numerino. Naiu Valia, cominciò a parlare con le vecchiette di tutto, sembrava di essere davvero al mercato. Cominciarono dal l’uncinetto per finire con le ricette di cucina siciliane. Gino non ne potè più, era stanco e si addormentò. Non appena aprì gli occhi vide che non c’era più nessuno ed era finalmente arrivato il suo turno. Allora si avvicinò al bancone, lì di fronte a Naiu Valia, lì pronto con tutte le sue carte e le sue cose da sbrigare, dice: è il mio turno adesso giusto? Naiu valia rispose: Ma beddu chi ti sbigghiasti ora! Non vidi chi ie menzionnu. Io annari a manciari. Tranquillu….rilassati i poi fari dumani! tantu ca fari? 8 Se siete arrivati a leggere fin qui avete capito perchè l’addetta della poste aveva come soprannome Naiu Valia. Questa storia ovviamente è un’ esasperazione. Non vuole essere provocatoria verso gli uffici postali né verso gli addetti postali né contro il sud o il nord, è solo una storia, prendetela così com’è e state tranquilli e rilassatevi! Traduzioni e audio: Gino svegliati che è tardi, ti ricordi che mi devi sbrigare quelle commissioni all’ufficio postale? Lo sai che io non posso andarci! Your browser does not support the audio element. ↩︎ Mamma fammi dormire! fammi dormire altri 10 minuti! Poi mi scoccio e sono stanco e lo sai che non sopporto l’addetta della posta! Your browser does not support the audio element. ↩︎ cuttighiara persona che ama sparlare di altre persone Your browser does not support the audio element. ↩︎ *Ma dove hai lasciato tuo papà? Oggi non è sceso con te? *Your browser does not support the audio element. ↩︎ Francesco detto Ciccio Birra perchè non ti bevi una birra e vai a letto? Your browser does not support the audio element. ↩︎ Chi non muore si rivede! Ma chi ti ha consigliato di venire? Che cosa devi fare? Your browser does not support the audio element. ↩︎ Ma ti sei svegliato ora? Devi fare tutto oggi? è tardi, sono le 10, non lo sai che io alle 10 devo faccio la pausa? Se vuoi puoi aspettarmi qui. Quando trono vediamo che cosa riusciamo a sbrigare. Your browser does not support the audio element. ↩︎ Ma bello, ti sei appena svegliato! Non vedi che è mezzogiorno. Io devo andare a pranzare. Stai tranquillo e rilassati, le puoi sbrigare domani le commissioni. Tanto che cosa hai da fare? Your browser does not support the audio element. ↩︎
An Happy man It was dark and quiet. A slight fishy smell was perceived on the nose. A rustle in the distance like when the sea beats on the shoreline. It was a beautiful evening. Gino, like every evening, was there, ready on his white Vespa, with a bright smile on his face and proud of himself. The wind lifts and moves its curly curls. At a certain point a man approaches, gives him a package and tells him Piazzale Dante 26. He replies, yes sir! He puts the package in the trunk, starts his Vespa with a sharp blow to the pedal. A burst and a strong discharge of gasoline comes out of the exhaust pipe. Gino breathes intensely, loves that strong smell of burnt mixed petrol with a background of the sea, makes him feel alive. He takes off on the twenty-fourth of May, and goes down fast, darting through the few cars he encounters. As he travels his way, he thinks of his children Pino and Matilde, who are about to go to bed at the moment. Then he bends over the wasp with a gentle movement, as if he were cradling his children. Arriving at the square, he stops, takes the package and heads for number 26. A beautiful family home. Drin Drin! A woman answers the intercom telling him to enter in. Gino enters, the woman asks him: Do you want something? Shall I make you a coffee? Gino replies, Thanks lady but I never drink coffee in the evening! So get a cookie I just made them! the woman reiterates. Gino takes a biscuit, they say goodbye and returns to his Vespa. As he returns, he finishes the last piece of the biscuit and thinks… Ho my god how good are these biscuits! and smiles. A busy girl At the same time, somewhere else, a little further but not too far, Alessia, a woman with all her hair shaved, a nice tattoo that comes out of her white T-shirt, is there waiting for something. There is no smell all covered by the smog of the big city. While waiting, he looks at his smartphone, answers a message, when suddenly he hears the number 16. He jumps up and suddenly turns, takes a few steps and heads towards the restaurant entrance. Come in and say Here I am! A parcel is given to him. He takes it and heads for his new electric bike. Hop on, attach the smartphone to the handlebar and start browsing which shows him where to go for package delivery. Then it rushes through the traffic, heavy traffic at that hour. It is found in the midst of machines and scooters that go beyond her and make fur continuously. During the journey she doesn’t think at anything, she can’t wait to make the last delivery, the evening ends and she can finally go home. Be careful where you go! the scream of a taxi driver. Alessia nervously replies Idiot, a little respect for cyclists! Finally at the delivery point, he reads the name on the smartphone, rings the intercom. Someone replies saying inside 6 staircase 2 third floor. Then attach her new bike to a pole, travel the way as if it were a maze. Arrived, she sees a very silent man, he was there waiting for her. Alessia delivers the package and greets him. The man returns the greeting and immediately closes the door. Alessia going out thinks that sad man…An insensible drone An insensible drone In the same instant of time, somewhere else, much further away, a drone is there, still, the wind does not scratch it, it does not move, it is also waiting. There is a metallic smell in the air. When all of a sudden a package rolls off a conveyor belt and hooks to the drone. Immediately afterwards,is heard Bip Bip followed by Sfruet Sfruet. The propellers start spinning with perfect synchrony, a slight hum is heard. The drone begins to rise from the ground with the packet attached below. Fly fast and look up at the city, and think … ha no! can not think! He goes towards something, he doesn’t know what, for him the position x. Once arrived, it descends slowly until it lands. A series of checks and a message starts from his soul depth. He is waiting. At a certain point a girl with golden ringlets approaches her, caresses him … Not exactly she puts her hand down so that the dorne recognizes her. The drone greets her cordially* Buonasera Marta* and gives her the package. The girl takes her package and runs away without saying goodbye. Poor drone, a little sad … Ha no! true can not be sad! Restart its propellers and return to base.
Un uomo felice Era buio e silenzioso. Un lieve odore di pesce si percepiva all’olfatto. Un fruscio in lontananza come quando il mare batte sulla battigia. Quella sera era proprio una bella serata. Gino, come tutte le sere si trovava lì, pronto sulla sua vespa bianca, con un sorriso smagliante in faccia e fiero di sé. Il vento solleva e smuove i suoi boccoli ricci. Ad un certo punto un uomo si avvicina, gli consegna un pacchetto e gli dice Piazzale Dante 26. Lui risponde, sì signore! Mette il pacchetto nel baule, mette in moto la sua vespa con un colpo secco alla pedalina. Uno scoppio e un forte scarico di benzina proviene fuori dal tubo di scappamento. Gino, respira intensamente, ama quel forte odore di benzina misto bruciato con sottofondo di mare, lo fa sentire vivo. Imbocca Via Ventiquattro Maggio, e va giù veloce sfrecciando in mezzo alle poche macchine che incontra. Mentra percorre il suo tragitto, pensa ai suoi bambini Pino e Matilde, che in questo momento stanno per essere messi a letto. Allora lui si piega sulla vespa con movimento soave, come se stesse cullando i suoi bambini. Arrivato alla piazza, si ferma, prende il pacchetto e si dirige verso il numero 26. Una bella casa familiare. Drin Drin! Una donna risponde al citofono dicendogli di accomodarsi. Allora Gino entra consegnandogli il pacchetto. La donna gli chiede: Vuoi qualcosa? Ti faccio un caffè? Gino risponde, Grazie signora ma non bevo mai caffè la sera! Allora prendi un biscotto, li ho appena fatti! la donna ribadisce. Gino prende un biscotto, si salutano e ritorna alla sua vespa. Mentre torna, finisce l’ultimo pezzetto del biscotto e pensa…mamma mia quanto sono buoni sti biscotti e sorride. Una donna impegnata Nello stesso istante, da qualche altra parte, un pò più lontano ma non troppo, Alessia, donna con i capelli tutti rasati, un bel tatuaggio che viene fuori dalla sua magliettina bianca, è lì ferma ad aspettare qualcosa. Non si sente alcun odore, è tutto coperto dallo smog della grande città. Mentre aspetta, guarda il suo smartphone, risponde ad un messaggio, quando ad un tratto sente chiamare il numero 16. Salta su e si gira di colpo, fa qualche passo e si dirige verso l’ingresso del ristorante. Entra e dice Eccomi! Gli viene consegnato un pacco. Lo prende e si dirige verso la sua nuova bici elettrica. Salta su, attacca lo smartphone al manubrio e avvia la navigazione che gli mostra dove andare per effettuare la consegna del pacchetto. Allora sfreccia in mezzo al traffico, traffico intenso a quell’ora. Si trova in mezzo a macchine e motorini che la superano e le fanno il pelo di continuo. Durante il tragitto non pensa a niente, non vede l’ora di fare le ultime consegna, la serata finisce e finalmente può tornare a casa. Sta attenta a dove vai! l’urlo di un tassista. Alessia risponde con nervosismo Imbecille, un pò di rispetto per i ciclisti! Finalmente arriva al punto di consegna, legge il nome sullo smartphone, suona al citofono. Qualcuno risponde dicendo interno 6 scala 2, terzo piano. Allora attacca la sua bici nuova ad un palo, percorre il tragitto come se fosse un labirinto. Arrivata, vede un uomo molto silenzioso, è lì ad attenderla. Alessia consegna il pacco e lo saluta. L’uomo ricambia il saluto e richiude subito la porta. Alessia uscendo pensa che uomo triste… Un drone insensibile Nello stesso istante di tempo, da qualche altra parte, molto più lontano, un drone è lì, fermo, il vento non lo scalfisce, non si muove, anche lui aspetta. Si sente un odore metallico nell’aria. Quando ad un tratto un pacchetto rotola giù da un nastro trasportatore e si aggancia al drone. Subito dopo, si sente Bip Bip seguito da Sfruet Sfruet. Le eliche cominciano a girare con una sincronia perfetta, si sente un lieve ronzio. Il drone comincia a sollevarsi da terra con il pacchetto sotto attaccato. Vola veloce e in alto osserva la città, e pensa… ah no! non può pensare! Si dirige verso qualcosa, non sa bene cosa, per lui la posizione x. Una volta arrivato, scende piano piano fino ad atterrare. Una serie di controlli e un messaggio parte dalla sua profondità dell’anima. Rimane in attesa. Ad un certo punto una bambina dai boccoli d’oro, si avvicina, lo accarezza…Non esattamente appoggia la mano affinché il dorne la riconosca. Il drone la saluta cordialmente Buonasera Marta e le consegna il pacchetto. La bambina prende il suo pacchetto e corre via senza salutarlo. Povero drone, un po triste…ah no! vero! non può essere triste! Riavvia le sue eliche e torna alla base.
Noi programmatori abbiamo diverse competenze e tanti pattern che utilizziamo, ma credo che la cosa che maturiamo nel tempo e ci accomuna di più è l’ottimizzazione delle risorse per risparmiare tempo. Tutti sanno che il tempo è la cosa più importante! Lo dice lo stesso modo di dire: il tempo è denaro. Il detto si basa su tempo umano. Il tempo umano deve essere utilizzato come valore aggiunto per la creatività, invenzione, innovazione costruzione e implementazione. Noi programmatori siamo fortunati, abbiamo a disposizione un’altra unità di misura del tempo, oggigiorno tendente quasi all’infinito, che possiamo e dobbiamo sfruttare al massimo e questo tempo e il tempo macchina. Un tempo si facevano fatigare gli schiavi poi i cavalli, i muli, i motori oggi ci siamo evoluti, culturalmente e mentalmente, Gli schiavi di quest’era sono le macchine. Per macchine intendo i nostri pc, o meglio ancora i nostri server, il cloud o i robot. Ho deciso di scrivere questo articolo per riportare delle tecniche utili per efficientare il nostro lavoro e di conseguenza ottenere più tempo. La mia tecnica principale da utilizzare per ottenere questo risultato è: appuntare gli step manuali che compiamo per fare un compito ricercare possibili soluzioni alternative riflettere su come possiamo automatizzare ogni singolo step mettere tutto assieme ripartire dal punto 1 se il nostro tempo d’impiego non è ancora = 0 per l’attività scelta Un esempio Spesso per velocizzare il nostro lavoro scriviamo degli script, questi script sono necessari altrimenti dovremmo scrivere n istruzioni di comandi. Giornalmente o mensilmente o dopo aver fatto qualcos’altro, dobbiamo eseguirli. Quindi apriamo il terminale e lanciamo lo/gli script. Per evitare di lanciare lo script a mano, che implica ricordarci, trovare lo script e lanciarlo, possiamo procedere nel seguente modo: se si tratta di qualcosa che va eseguito con una schedulazione fissa possiamo schedularlo utilizzando uno scheduler in linux possiamo fare un cron oppure possiamo utilizzare atrigger per eseguire la schedulazione nel caso in cui il nostro script sia su un server a cui non abbiamo accesso alla schedulazione Se invece non c’è una schedulazione fissa e dobbiamo eseguirlo al bisogno Possiamo utilizzare una scorciatoia da tastiera. In linux ma anche su Windows e molto semplice settare una una scorciatoia. Nel mio caso usando Ubuntu basta andare sotto *impostazioni *-> scorciatoie da tastiera e aggiungiamo il nostro nuovo parametro che conterà il path al nostro script e la scorciatoia desiderata. Come potete vedere nella seguente immagine, io lo uso ad esempio per lanciare in debug il mio blog e successivamente per fare il deploy: per quanto questo possa sembrare bello e può aiutarci a renderci più efficienti, con due tasti, il gioco è fatto, si può sempre ottimizzare. Voi direte, che altro puoi fare per ottimizzare questo? e perchè? Il perchè è semplice, per poterlo eseguire ho necessariamente bisogno di avere la mia macchina a disposizione e accesa. Quindi sarebbe meglio laddove possibile utilizzare sempre un server o il cloud in modo da fare tutto da remoto in modo così da poter avere più controllo e soprattutto non dipendere da una macchina. Oggi ci sono tanti strumenti che abbiamo a disposizione per eseguire script o creare piccole pagine web o piccole applicazioni o siti vetrina. Il piano gratuito di questi servizi, mette a disposizione tanti strumenti fra i quali: Spazio hosting. Spazio database. Funzioni per micro servizi Container Macchine virtuali e chi più ne ha più ne metta C’è ne sono così tanti che a volte tendiamo a non vederli, nonostante siano sotto il nostro naso. Quelli che utilizzo io maggiormente, sono: Firebase Google Cloud Amazon AWS Google Script L’ultimo servizio a differenza degli altri non è un servizio cloud ma semplicemente un motore di funzioni con schedulazione o attivazione in funzione dei servizi Google. Sono molto integrate con il nostro account Google e se lo usiamo come mail principale ci darà davvero delle grandi potenzialità per automatizzare: invii di mail sincronizzazione di dati editing di documenti chiamare API esterne memorizzare dati in excel ecc ecc… è brutto dire ecc ecc ma davvero, l’elenco sarebbe troppo lungo, vi permette di automatizzare tutto ciò che è Google. Gli altri tre sono i primi tre migliori provider Cloud. Per cominciare io consiglio di usare Firebase perchè mette a disposizione davvero tanti servizi e se si tratta di primo inzio con il cloud, l’accesso con Firebase, credo sia più semplice e con una linea di apprendimento più rapida. Conclusioni Cerchiamo di fare più attenzione a quello che ogni facciamo senza pensare. Se ogni giorno facciamo una cosa ripetitiva, anche se ci mettiamo davvero poco a farla, teniamola in considerazione e pensiamoci. Dopo anni le cose ripetitive possono diventare tante e rischiamo di saturare tutto il nostro tempo col fare solo cose ripetitive. Vi lascio e vi saluto con una frase che mi è venuta adesso. Probabilmente non ha senso o forse è già di qualcuno ma io non lo so. Meglio creare un sistema che ci risolve un problema che risolvere il problema stesso!
We as programmers have different skills and we use lot of patterns, but I think the thing that we mature over time and we have more in common is the resources optimization for time saving. Everyone knows that time is the most important thing! The same way of saying it: time is money. The saying is based on human time. Human time must be used as an additional value for creativity, invention, construction innovation and implementation. We programmers are lucky, we have another measurement time unit, nowadays almost tending to infinity. This is machine time and we have to make the most of it. Once we used slaves, then horses, mules, engines motor. Today we have evolved, culturally and mentally. The slaves of this era are machines. By machines I mean our PCs, or better yet our servers, the cloud or robots. I decided to write this article to report useful techniques to make our work more efficient and consequently have more time. My main technique to be used to obtain this result is: write down the manual steps that we perform to do a task search for possible alternative solutions reflect on how we can automate each single step put everything together starting from point 1 if our time for the task is not equal to 0 An example Often to speed up our work we write scripts, these scripts are necessary otherwise we would have to write n command statements. Daily or monthly or after doing something else, we have to do it. So let’s open the terminal and run the scripts. To avoid launching manual the script, which implies remembering, finding the script and launching it, we can proceed in the following way: if it is something that must be done with a fixed date Schedule it using a scheduler With linux, we can make a cron We can use atrigger to execute the scheduling in the event that our script is on a server to which we do not have access to server side If instead there is no fixed scheduling date and we have to execute it as needed We can use a keyboard shortcut. In linux but also on Windows it is very simple to set a shortcut. In my case using Ubuntu just go under *settings *-> keyboard shortcuts and add our new parameter that will count the path to our script and the desired shortcut. As you can see in the following image, I use it for example to debug my blog and then to deploy: as beautiful as it may seem and can help us make us more efficient, with two buttons, you’re done, yes can always optimize. Will you say, what else you can do to optimize this? and why? The reason is simple, in order to run it I necessarily need to have my machine available and turned on. So it would be better where you can always use a server or the cloud in order to do everything remotely so that you can have more control and above all not depend on a local machine. Today there are many tools available for us to run scripts or create small web pages or small applications or showcase sites, the free plan of these services, provides many tools including: Hosting space Database space Functions for microservices Container Virtual machines and whoever has more put them There are so many that sometimes we tend not to see them, despite being under our noses. The ones I use most are: Firebase Google Cloud Amazon AWS Google Script The last service unlike the others is not a cloud service but simply an engine of functions that can be scheduled or activated in relation on google service. They are very integrated with our Google account and it will really give great potential to automate: mailing data synchronization document editing calling external API storing data in excel etc etc … it’s bad to say etc etc but really, the list would be too long, it allows you to automate everything which is Google. The other three are the top three best cloud providers. To begin with I recommend using Firebase because it offers so many services and it is simler and with a faster learning line. Conclusions We have to pay more attention to what we all do without thinking on it. If we do something repetitive every day, even if we take very little time to do it, let’s take it into consideration and think about it. After years, repetitive things can become many and we risk saturating all our time by doing only repetitive things. I leave you and greet you with a phrase that has come to me now. It probably doesn’t make sense or maybe it’s already of someone’s but I don’t know. It is better to create a system that solves a problem than to solve the problem itself!
There are many ways to secure our functions. This topic is not clear when you start using the Cloud Function and it is also constantly changing. I decided to implement my own method that uses the basic and simplest authentication, the Basic Auth. It certainly is not the safest method but it was enough for my purpose. The only flaw is that the function has to be added and implemented in every Cloud function that we make.This implementation then gives us the possibility to call our function from anywhere we want to by simply adding as a parameter of the request the user and the password encrypted in the Basic Auth. To simplify the verification, I created a function that I place in each Cloud Function. This function does the parsing of the request data, verifies if there is a basic auth and, if present, it checks whether the user and password data match. Here is the code I used: var compare = require('tsscmp') const checkAuth = async() => { // parse login and password from headers const b64auth = (req.headers.authorization || '').split(' ')[1] || '' const strauth = new Buffer(b64auth, 'base64').toString() const splitIndex = strauth.indexOf(':') const login = strauth.substring(0, splitIndex) const password = strauth.substring(splitIndex + 1) //function to validate credentials using https://www.npmjs.com/package/tsscmp //Prevents timing attacks using Brad Hill's Double HMAC pattern to perform secure string comparison function check (name, pass) { var valid = true // Simple method to prevent short-circut and use timing-safe compare valid = compare(name, 'user') && valid valid = compare(pass,'password') && valid return valid } if (!check(login, password)) { res.statusCode = 401 res.setHeader('WWW-Authenticate', 'Basic realm="example"') res.end('Access denied') } } Then we can use the function within the method called by our Cloud Function in the following way: exports.helloWorld = async (req, res) => { await checkAuth(); let message = req.query.message || req.body.message || 'Hello World!'; res.status(200).send(message); }; And that’s it, our functions are now safe! Not much but it’s a starting point. So in the end if our system is breached,it is not a big problem, in this function we only return Hello World! How do we instead call our function with Flutter? We simply have to create the Basic Auth with user and password and add it to the request as follows: var basicAuth = 'Basic '+base64Encode(utf8.encode(user:password')); http.Response response = await http.get(apiUrl, headers: <String, String>{'authorization': basicAuth}); print(response.statusCode); //200 print(response.body ); //Hello world! This method is not one of the best, however, it is very fast to implement and also gives us an advantage because it can be used in the same way from all Cloud providers. Finally, we can recall it from anywhere using any language, and it does not force us to use json keys that could be displayed. The only problem I had was having the username and password inside the code that I didn’t really like. I solved it by putting these parameters within the firebase configurations. If you are interested in this, you can read this article Flutter 2 indispensable plugins with great potential.
Ci sono tanti modi per poter mettere al sicuro le funzioni. Questo argomento non è chiarissimo dal primo momento in cui si utilizzano le funzioni in Cloud ed è inoltre in continuo cambiamento. Io ho deciso di implementare un mio metodo che utilizza la blasonata e più semplice autenticazione: la Basic Auth. Sicuramente non è la più sicura ma bastava al mio scopo. Unica pecca è che la funzione deve essere aggiunta e implementata in ogni funzione Cloud che andiamo a creare. In questo modo, possiamo chiamare la funzione implementata da dove vogliamo semplicemente aggiungendo come parametro della request user e password criptati nella Basic Auth. Per semplificare la verifica, ho creato, una funzione che inserisco all’interno di ogni Cloud Function. Questa funzione si occupa di fare il parsing dei dati della request, verificare se presente una basic auth e se presente verifica se i dati di user e password corrispondono. Di seguito il codice utilizzato: var compare = require('tsscmp') const checkAuth = async() => { // parse login and password from headers const b64auth = (req.headers.authorization || '').split(' ')[1] || '' const strauth = new Buffer(b64auth, 'base64').toString() const splitIndex = strauth.indexOf(':') const login = strauth.substring(0, splitIndex) const password = strauth.substring(splitIndex + 1) //function to validate credentials using https://www.npmjs.com/package/tsscmp //Prevents timing attacks using Brad Hill's Double HMAC pattern to perform secure string comparison function check (name, pass) { var valid = true // Simple method to prevent short-circut and use timing-safe compare valid = compare(name, 'user') && valid valid = compare(pass,'password') && valid return valid } if (!check(login, password)) { res.statusCode = 401 res.setHeader('WWW-Authenticate', 'Basic realm="example"') res.end('Access denied') } } Successivamente possiamo utilizzare la funzione all’interno del metodo chiamato della nostra Cloud Function nel seguente modo: exports.helloWorld = async (req, res) => { await checkAuth(); let message = req.query.message || req.body.message || 'Hello World!'; res.status(200).send(message); }; Ed il gioco è fatto, le nostre funzioni adesso sono al sicuro. Mica tanto! ma è un inizio. Tanto alla fine se dovessero superare la nostra autenticazione, non sarebbe un grosso problema. In questa funzione restituiamo solo Hello World! Come facciamo invece a richiamare la nostra funzione con Flutter? Dobbiamo semplicemente creare la Basic Auth con user e password e aggiungerla alla request come segue: var basicAuth = 'Basic '+base64Encode(utf8.encode(user:password')); http.Response response = await http.get(apiUrl, headers: <String, String>{'authorization': basicAuth}); print(response.statusCode); //200 print(response.body ); //Hello world! Questo metodo non è uno dei migliori però è molto rapido implementarlo e inoltre ci dà un vantaggio perché può essere utilizzato allo stesso modo per tutti i provider cloud. Infine, possiamo richiamarlo dappertutto utilizzando qualsiasi linguaggio, e non costringe a doversi portare dietro chiavi json che potrebbero essere esposte. L’unico problema che ho avuto è stato quello di avere nel codice scolpiti user e password che non trovavo gradevoli. Ho risolto questo problema mettendo questi parametri all’interno delle configurazioni di firebase. Se siete interessati potete leggere questo articolo Flutter 2 plugin indispensabili dalle grandi potenzialità
Giuseppe te ca 1 euro, vammi a cattari na buttigghia i pommarola chi stamatina ma scuddai ma pigghiu a putia. Cu restu a cattiti chiddu chi voi, basta chi non ti catti vularie. Si proprio te na cattari, a cattiti i carruba chi su boni. 1 Questa frase sembra molto normale oggi, va be in sicilia ovviamente, ma un un tempo, non molto lontano, quando la globalizzazione, la tecnologia e commercializzazione dei prodotti non era così forte come ai giorni odierni, la frase non reggeva. Non tutti avevano la possibilità di comprare una bottiglia di pomodoro. E allora come si faceva? non si mangiava la famosissima e ricorrente pasta alla norma? Magari all’epoca non era così ricorrente era un piatto della domenica. No non se ne faceva a meno e per non farlo mancare a tavola, quasi tutti si adoperavano a fare le bottiglie in casa, in garage oppure in giardino. Giuseppe era molto ubbidiente e tornò a casa con la passata e le carrube. La passata che diede alla mamma, gli ricordò la storia di buttugghi i pummorola allora disse alla mamma, se poteva raccontarle la storia di quando lei faceva le bottiglie in casa. La mamma dapprima stentò a raccontarla, aveva già raccontato la storia delle bottiglie si tante volte che non aveva voglia, ma davanti alla faccia del figlio Giuseppe che esprimeva in uno sguardo “dai dai mamma ti prego raccontamela per favore..lo sai che mi piace tanto..” Allora la mamma non ne può fare a meno e comincia la storia. La preparazione delle bottiglie di pomodoro era una ricorrenza annuale, era una cosa bellissima e per noi tutti nella famiglia era come se fosse una festa. La notte precedente alla preparazione delle bottiglie di pomodoro, non riuscivo a pigliare sonno per l’agitazione e per la voglia di fare le bottiglie. La mattina prestissimo, prima ancora dell’alba, mi posizionai li in terrazza ad aspettare il rientro del nonno. Quel momento lo ricordo sempre, mi luccicavano gli occhi a vedere la lapa i Puddicinu arrivare carica di pomodori, tutti rossi ma rossi, rossi belli tutti luccicanti. Non so se perchè ero piccina oppure perché i pomodori erano i casa 2, riuscivo a percepire l’odore già da quella distanza. Di corsa balzavo giù per le scale urlando come una forsennata Oggi facemu i buttigghi!! Oggi facemu i buttigghi!! una volta lì davanti alle cassette, ne prendevo sempre un paio, li toccavo, il odoravo… ummm che buoni!!! Ii adoravo. Gli davo una sciacquata veloce e li mangiavo così sani sani. Nel cortile la nonna preparava tutte le bottiglie di vetro, bottiglie recuperate da innumerevoli bevute di birra. Una per una, le insaponava e metteva in ammollo nella bagnarola. Noi ragazze, le davamo una mano, le immergevamo nelle bagnarole in modo da rimuovere bene tutto il detersivo e così da lavarle bene bene. Ogni tanto di nascosto arrivavano i cugini che ci bagnavano con la pompa, noi per giocare poi bagnavamo loro. Era divertentissimo ridevamo e correvamo come i pazzi. La nonna se ci vedeva si biliava e ci urlava contro Viditi chiddu ca aviti a fari, si vi sciuppiati poi vi dugnu i supra. Oggi amu a travagghiari amo a fari i buttigghi, e si cuntinuati accusi finisci chi vi sciuppiati e namu a ricurdari a iunnata. 3 infine tutti bagnati finivamo di sciacquare le bottiglie rimaste. Lasciavamo tutte le bottiglie, tantissime bottiglie al sole tutte luccicanti a riposare e asciugare. Ma i giochi con l’acqua non finivano qui! Subito dopo si cominciava con il lavaggio delle cassette dei pomodori. buttavamo i pomodori dentro le bagnarole, li lasciavamo in ammollo per un pò e poi uno per uno li prendevamo e riponevamo dentro le cassette. La nonna e le zie nel frattempo, preparavano un gran fuocherello acceso per terra e sopra al fuoco c’era la più grande casseruola mai vista, io anche se piccola, ero molto alta, superavo il metro e venti ma ciò nonostante, non arrivavo a vedere dentro la casseruola. Per vedere dentro, dovevo salire su una scaletta. Mi piaceva guardare i pomodori mentre cuocevano, era bello tutto quei pomodori che strofinavano tra loro, tanto calore e bollore che usciva e un profumo intenso di pomodoro. I pomodori più piccoli ogni tanto saltavano in alto per il tanto bollore. Con un grande arnese, che io da piccola lo vedevo come il più grande cuppino che si è fuso ad uno scolapasta, venivano afferrati i pomodori pronti, quelli più pronti, pronti perché stavano a galla, lì pronti ad uscire. Una volta afferrati, li facevano rotolare giù per la macchina mangia pomodori. La macchina mangia pomodori, stava sotto il controllo della zia Pizzitta. Lei si occupava di mettere in moto la macchina, fermarla se bloccata e aiutava i pomodori a essere spremuti per bene. Li aiutava con un mattarello, li spingeva giù per il canale e man mano che scendevano, gli diceva: entrate entrate, mettetevi in fila e non spingete ci state tutti e a breve sarete uniti per sempre. Un bel succo rosso vivo, molto concentrato, caldo e intenso, colava giù dalla macchina e finiva in una grande pentola. Da un altra estremità della macchina, uscivano le bucce e Pizzitta, di tanto intanto si occupava di passarle nuovamente nella macchina. La postazione successiva era proprio la mia e della zia Maria. Io mi occupavo di prendere le bottiglie dalle ceste, di staccare le foglie dal grande mazzo di basilico e di mettere due foglioline all’interno di ogni bottiglia. Mi piaceva tanto questo compito perchè sentivo l’odore intenso del basilico sulle mani. La zia Maria, prendeva la bottiglia da me preparate e la metteva sotto il rubinetto, girava la valvola e fuoriusciva il caldo succo di pomodoro. Lasciava aperto il rubinetto fintanto che la bottiglia non si fosse ben riempita di succo di pomodoro. Lo zio Tano, con grande maestria e tanta attenzione, prendeva le bottiglie molto calde per via del sugo che fumava fin fuori dal collo delle bottiglie, le piazzava sotto un’altra postazione. Prendeva un tappo e lo attaccava ad una calamita di un altro macchinario, poi con decisione e fermezza girava una grossa manovella fintanto che il tappo non fosse ben attaccato alla bottiglia. Faceva una veloce verifica in modo da testare che fosse ben chiusa e dava l’ok a Giorgo che si occupava di prendere la bottiglia e riporla nella cassetta. Giorgio non era di famiglia ma era come se lo fosse. Noi lo facevamo stare li a svolgere il suo compito. Sebbene fosse un piccolo compito, lo faceva tanto contento. Adesso le bottiglie di pomodoro, dopo tutti questi step seguiti con cura sono pronte!! Giuseppe era ancora molto attento e sentendo la false e sapendo che la storia non finisce così subito ribatte dai mamma lo so che non finisce così, continua a raccontare. E allora la mamma riprende: è vero, Il bello deve ancora arrivare. Si prendevano dei grandi barili ex barili di petrolio davvero enormi sempre molto più alti di me. Lo zio o la nonna con l’aiuto di una scala, si calavano dentro a testa in giù e pian piano ci mettevano dentro le bottiglie di pomodoro, disposte una accanto all’altro non proprio attaccate ma vicine, poi sopra uno strato di panni e pezze vecchi e così via. Quando si giungeva alla fine, sopra l’ultimo strato di pezze, si mettevano delle pietre per tenere il tutto fermo. Infine si versava dentro l’acqua con una pompa che dal basso saliva fino in cima, era importante che fosse riempito tutto fino all’orlo. A questo punto, proprio sotto i grandi barili, venivano accesi altri grandi fuochi. Dopo un pò di ore, il tempo di mettere apposto tutta l’attrezzatura, si spegnevano i fuochi e si lasciavano dentro le bottiglie tutta la notte a raffreddare e a riposare. Nta notti a Nonna non pigghiava sonnu, si girava e si vutava, stava cu penseri pi buttigghi. Giuseppe u sai picchì a nonna aviva u penseri e buttighie? 4 disse la mamma. Picchi na vota, succidiu nu macello, i buttigghi non furunu chiusi pi come si devi e nta nanzi a notti, i buttighi scuppiarunu e na ranni botta si sintiu! 5 rispose subito Giuseppe a tono. Si bravo, fecero una botta fortissima e la nonna saltò in aria. Quella notte fortunatamente era andato tutto bene, nessuna bottiglia scoppiò. L’indomani mattina, tirarono fuori tutte le bottiglie una per una ed erano tutte integre, troppo belle e troppo buone. Riponemmo le bottiglie all’interno delle cassette che poi posizionammo all’interno di grandi scaffali. Che bello tutte le bottiglie per l’anno per la famiglia erano pronte. Ne prendemmo subito un paio e preparammo una pasta al pomodoro per tutti. Quella pasta, la ricordo ancora, aveva il sapore dei pomodori ma anche di tutti noi e di tutti gli sforzi fatti per produrre la passata. Dai Giuseppe è pronto, adesso mangiamoci la nostra pasta al pomodoro e immaginiamo di averla fatta con le nostre mani. Dai Giuseppe è pronto, adesso mangiamoci la nostra pasta al pomodoro e immaginiamo di averla fatta. Traduzioni: Giuseppe tieni 1 euro, vai a comprare una bottiglia di pomodoro, questa mattina me la sono scordata di comprarla alla bottega. Con il resto dei soldi, comprati tutto quello che vuoi, però non ti comprare porcherie. Se proprio te le devi comprare, compra le carrube che sono più sane. Your browser does not support the audio element. ↩︎ prodotti a kilometro 0, prodotti in campagna familiare. ↩︎ Prestate attenzione a quello che fate, se vi fate male poi io vi do altre botte. Oggi dobbiamo lavorare, dobbiamo fare le bottiglie di pomodoro, se continuate così, rischiate di farvi male e poi finisce che finiamo all’ospedale e ci ricorderemo per sempre di questa giornata sventurata. Your browser does not support the audio element. ↩︎ Durante la notte la nonna non prendeva sonno. si girava e rigirava, stava con il pensiero alle bottiglie. Giuseppe lo sai perchè la nonna aveva il pensiero alle bottiglie? Your browser does not support the audio element. ↩︎ Perché una volta è successo che non erano state chiuse bene e allora durante la notte sono scoppiate, facendo un gran fracasso Your browser does not support the audio element. ↩︎
Bicchieri svuotati dall’alcol e sparsi di qua e di là su un tavolo colmo di disordine. I bicchieri, sono pieni di menta e lime ormai secchi con uno stretto intreccio che li lega insieme. Posaceneri pieni colmi di cicche, cenere e carta bruciata ancora lievemente fumante. Una forte puzza di fumo presente in tutta la stanza. Amici e gente che non conosco, dormono non molto lontano da me, alcuni sono schiacciati uno sull’altro tutti presi da un forte sonno profondo, pesantemente sprofondati in divani e cuscini che reggono il peso e si deformano per rendere più confortevole il viaggio. Mi sento bene un lieve giramento di testa ma bene, qualcosa è successo ma in quel momento non ricordo nulla. Sorrido e sprofondo ancora in un altro sonno profondo. Riapro gli occhi e vedo i posaceneri che cominciano a muoversi, vanno verso il pattume dove vengono svuotati da Petra. Dal posacenere cade di tutto ma nonostante il posacenere sia trasparente non permette di vedere attraverso. I posaceneri vengono sbattuti per fare cadere la cenere residua, ma rimangono comunque neri, di un nero scuro scuro che mi accende un ricordo, il nero del nero di seppia della pasta mangiata la sera prima, che buona! Faccio forza per tirare su il braccio pendente giù dal divano e scorgo un cuoricino sull’avambraccio con una scritta si senti lu cori chi batti. Sai aunni truvarmi. 1 Sono molto perplesso, non ricordo ma sento qualcosa e mi piace. Una scimmia entra dalla finestra era stata spalancata per via della puzza di fumo. La scimmia, comincia ad urlare e fa un chiasso della madonna, quasi tutti si svegliano, si alzano di colpo ed escono fuori in giardino. Io salto in piedi di colpo e faccio dei movimenti ondulatori avvicinandomi verso la scimmietta, cerca ndo di farla uscire dalla finestra. Ma la scimmia non ne vuole sapere, prende un mela e me la scaglia contro poi prende un arancia e la scaglia sempre contro di me con più potenza e cattiveria, continua così con tutto il cestino di frutta. Io dopo aver preso la prima mela in faccia prendo un bastone e come in fruit ninja cerco di colpire la frutta mandandola a destra e a sinistra. Frutta che vola in tutta casa e muri che si sporcano di succhi vari. Appena la frutta finisce, cerco di avvicinarmi alla scimmia aprendo le braccia in cenno di pace, allora le dico scimmietta camo a fari, non vidi chi semu a menzu a na strada. Magari tu ta mettiri a fari burdellu. 2 Allora la scimmia comincia a parlare e io sono ancora più perplesso e dice ma chi ci fai ca, senti lu cori e vai a circari a fimmina chi ieri sira t’apriu lu cori. 3 A quel punto ho realizzato che stava tutto nella mia testa, le scimmie possono anche lanciare la frutta ma non credo parlino e poi che ci fa una scimmia in sicilia? I ricordi ancora non mi aiutano, penso che un caffè possa aiutarmi, allora mi alzo e sento un dolore al ginocchio, mi accende un ricordo dei piccoli kart della gara organizzata la sera prima. io troppo grosso, non ci stavo nel kart e allora mi sbattevano le ginocchia sulla carrozzeria del kart. Prendo la caffettiera del caffè e mentre cerco di aprirla, vedo un’altra scritta sull’altro braccio non ti fari pigghiari du sonnu! vidi chi ie ionnu! vidi chi ionnu! 4 ma che minkia è successo ieri notte penso… Fiuuuubrururburur bu sento il rumore del caffè, è salito, spengo il fornello e mi verso una grande tazza, ne avevo proprio bisogno. Il rumore e probabilmente l’odore sveglia anche Pippo che si avvicina un pò storto e dice Minkia comu ni divirtemmu aieri sira…chi c’è u cafè? 5 Faccio un cenno con la testa allora si versa anche lui una tazza e si accende subito una sigaretta. Gli chiedo se si ricordava qualcosa della donna che avevo conosciuto la sera prima, ma niente, non ricordava nulla e rideva come un matto guardando le scritte che avevo addosso. Perlustrai attorno in cerca di qualcosa e vidi teli da mare bagnati, mi ricordai del bagno di mezzanotte dopo i kart. Ad un tratto vidi dei biglietti, biglietti di un circo, ricordavo davvero poco sul circo, però pensai alla scimmia di poco fa e magari poteva esserci qualcosa di collegato con la mia strana immaginazione. Allora mi guardai attorno presi i miei averi e dissi Minni vaiu ou circu. Coccaduno vole a veniri o vi siddiati? 6 sai come quando butti lì qualcosa e l’unica cosa che senti è l’eco di ciò che hai detto. Ecco, la scena è stata proprio questa. Buttai un cenno di saluto a caso a cui ovviamente nessuno rispose mi girai e mi incamminai verso l’uscita. Una volta fori, pensai a come andare. Il circo non era lontanissimo e avevo voglia di riflettere ma soprattutto smaltire tutto l’alcol che avevo assunto la notte prima, allora decisi di andare a piedi. Man mano che camminavo mi ricordai che comunque non avevo alcun mezzo con me in quanto era passato Pippo a prendermi in motorino la sera prima. Rosso e bianco era il grande capannone centrale che si intravedeva già in lontananza, schiamazzi e urla di bambini, fuochi e versi di animali si sentivano da più vicino. Ero stanco e non volevo perdere troppo tempo, allora pensai a cosa potevo fare per accedere ma soprattutto per ricevere risposte. Mi avvicinai all’ingresso e fui subito attratto da una signora bella tutta truccata con colori molto vivaci, i capelli tutti intrecciati che formavano una torre che finiva a punta e su una stellina luminosa. Mi avvicinai, non sapevo cosa dire allora improvvisai: Buongiorno signora ieri mi sono scordato la giacchetta qui, non è che l’avete vista? La signora tirò fuori un sorriso a 27 denti di cui più della metà splendevano d’oro e d’argento e rispose Fosse solu a giacchetta chi ti scurdati aieri sira! Ieri voi carusi, facistu nu maciello! 7 Allora vista la mala figura, mi sentii a mio agio a parlare sinceramente di ciò che ero venuto a fare, la donna mi prese in simpatia anche perchè ieri l’avevamo fatta ridere parecchio allora mi concesse di entrare a dare un occhiata in giro, mi disse di andare al bar dove avrei potuto trovare risposte e disse, spetta u figghiolo ca scimmia. 8 Entrai e andai alla ricerca del bar dove ordinai un caffè mentre attendevo il ragazzo con la scimmia. Poco dopo aver finito il caffè arrivò il ragazzo con la scimmietta. Non appena arrivato, la scimmietta mi corse addosso e mi strofino la testa sul braccio con dolcezza come per salutarmi. Io ricambiai e mi ricordai che durante la notte avevamo giocato assai insieme, le lanciavo noci, nucidde e nocitte e la scimmietta le prendeva al volo e le mangiava. Nel frattempo Marcello mi raccontò tutto della serata trascorsa lì al circo e io man mano avevo dei ricordi un pò più nitidi. Avevo messo la mano nella bocca del leone e non avevo avuto paura. In questo momento solo al ricordo mi salì un gelido formicolio di paura. In questo istante molto probabilmente, non l’avrei mai fatto. Filippa si chiamava la ragazza con cui avevo parlato la notte prima e faceva parte delle danzatrici volanti. Mi ricordai che avevo avuto una scossa notevole guardandola lì, sul filo mentre danzava era di un soave indescrivibile. Marcello, mi disse che subito dopo lo spettacolo avevamo parlato tanto ed eravamo stati molto vicini. Non ricordavo bene però sentivo qualcosa. Allora chiesi dove potevo trovarla e mi disse che a quest’ora stava sempre al capannone centrale ad allenarsi. MI diressi verso il capannone e nel contempo cercavo di ricordarmi, il cuore mi batteva ero leggermente agitato non sapevo che dire che fare. Ad un tratto la vidi e la riconobbi, stava proprio di fronte al capannone stava facendo una pausa, man mano che mi avvicinavo i ricordi diventano sempre più chiari, mi ricordai dei capelli neri ricci degli occhi sempre neri molto penetranti, mi apparve tutto chiaro. Però una volta lì davanti ero imbarazzatissimo e non sapevo che fare, preso dall’imbarazzo e da qualcosa ma non so bene cosa, mi avvicinai e la bacia in bocca. Lei inizialmente mi allontanò e rimase un po scossa ma poi si prese e cominciò a baciarmi e ci legammo insieme per un pò. Dopo qualche istante, ci allontanammo, giusto per vederci in faccia. Lei stava per dire qualcosa allora io la interruppi e le mostrai il braccio. Braccio in cui la notte prima era stata scritto il messaggio d’amore. Il volto di lei, si trasformò in un punto interrogativo, era perplessa come per dire ma che minkia è? Molto probabilmente non era la donna della sera prima pensai. Diventai rosso, non sapevo che fare, pensai che ormai qualcosa fosse nato. Nato a caso ma forse non veramente a caso e comunque poteva non avere importanza. Un attimo dopo realizzai che il tutto non aveva senso e pensai di girarmi e scappare via, via da quell’imbarazzo da quella scena troppo strana, in effetti avevo appena baciato una completa sconosciuta così a caso. Sono ancora in tempo! stavo per girarmi e filare via quando ad un tratto lei fece un sorriso, sorriso strano che voleva dire qualcosa, un po malizioso, si avvicinò e mi baciò ancora più intensamente come se mi conoscesse. Ma boh io come al solito non ho capito niente, ero un pò stanco e assonnato, ho guardato l’altro braccio e ho riletto non ti fari pigghiari du sonnu! vidi chi ie ionnu! vidi chi ionnu! allora spinto da questa frase e dall’impeto del bacio, mi svegliai di colpo e continuai a baciarla per tanto, tantissimo tempo. Eravamo come completi sconosciuti dove, l’unica parola scambiata era stato un bacio, a volte un bacio può dire ed esprimere più di mille parole. Traduzioni: Se senti il cuore che batte, sai dove trovarmi. ↩︎ Scimmietta che sta succedendo, non vedi che siamo già a disagio. Devi contribuire anche tu a fare traffico. ↩︎ Ma che ci fai ancora qui. senti il tuo cuore e vai a cercare la donna che ieri sera ti ha aperto il suo cuore. ↩︎ Non ti fare prendere dal sonno. Vedi che è giorno, vedi che è giorno. ↩︎ Perbacco, come ci siamo divertiti ieri sera… che c’è il caffè? ↩︎ Vado al circo. Qualcuno vuole venire o vi scocciate? ↩︎ Fosse solo la giacchetta che ti sei scordato ieri sera! Ieri voi ragazzi, avete fatto un chiasso esagerato! ↩︎ Aspetta il ragazzo con la scimmia. ↩︎
Erano le 4.30 e Pierluigi aveva ancora gli occhi chiusi. Era sommerso in un sogno profondo. il sogno di un gioco, guardie e ladri. Pierluigi, era alla ricerca di un posto posizionato in alto, un luogo dove avrebbe potuto godere di una buona visione con multiple vie di fuga. Scelse la terrazza di Floride, una terrazza enorme che offriva una vista sulla piazza centrale del paese. Da lì poteva vedere tutta la piazza e seguire le guardie che avevano appena finito di contare e stavano per andare alla ricerca dei ladri. Un sobbalzo lo svegliò aprì leggermente gli occhi e vide a bettula 1 del nonno. L’aveva usata come cuscino e gli aveva biascicato addosso mentre dormiva. Il sobbalzo era stato causato da Stella la cavalla del nonno che si stava destreggiando nella dura salita che portava alla campagna. Cooooocoocode sentì, un colpo di ali e la bettola iniziò a muoversi. Pierluigi, aveva sonno e non si fece domande sul perché la bettola si muoveva ed emetteva versi di gallina, chiuse gli occhi e riprese a dormire. Appena arrivarono in campagna, si fermarono di fronte all’orto. Scesero dal cavallo, il nonno disse Sta Iddina non è chiu bona a nenti. Non fa qiu ovae non è bona mancu più fari u brodu, c’amo a tagghiari a testa. 2 allora il nonno si girò verso Pierluigi e gli diede un coltello dal manico di legno molto affilato, l’aveva affilato la mattina con tanta cura. Pigghia stu cuteddu, cià fari nun tagghiu nte cannarozzi. Te ca. 3 Pierluigi aveva solo 9 anni era ingenuo e perplesso non capiva il perché di questa azione e non si spiegava perché doveva essere proprio lui a farla. Era molto impaurito dalla gallina. Il nonno la prese tenendola per le ali e tenendogli la testa e gli disse fozza tagghia! allora Pierluigi si avvicinò e avvicinò il coltello alla gola della galliana, la sfiorò, non appena vide il sangue, rabbrividì e si fermò. Il nonno cercò di riprendere il coltello per terminare la gallina. Ad un tratto la gallina gli sfuggì di mano e cominciò ad emettere versi smorzati. Si mise a correre sguazzando sangue di qua e di la. Il sangue schizzò su Pierluigi e sul nonno e sul prato, Sembrava una scena splatter come nei film di Tarantino. Cercarono di acchiappare la gallina ma gli sfuggì, dopo un pò la gallina si stancò e cominciò a barcollare qua e la. Il nonno in un balzo l’afferrò e con un colpo secco gli recise la gola, mentre lo fece, si sentì un ultimo stridulo coccodè che mise fine alle sofferenze della povera gallina. Allora la prese e la appese ad un palo vicino all’orto e disse ora voggiu a vidiri si tornunu staceddi da mala sorti! 4 tutta questa crudeltà per far scappare gli uccellini che in quei giorni mangiavano il raccolto. Il nonno aveva fatto uno spaventapasseri ma non era stato sufficiente. Ci teneva molto alla sua campagna aveva fatto tanti sacrifici e avrebbe fatto qualsiasi cosa per raccoglierne i frutti. Pierluigi era un po scandalizzato dalla scena a cui aveva assistito. Un attimo dopo, si mise al lavoro, doveva mbvirare 5, spostando la suca di rasola a rasola. Mise la suca sotto un grande albero di pesche e si sedette ad aspettare. Ad un tratto, gli calò la palpebra e si trovò di fronte le guardie che stavano salendo dalle scale principali. Allora Pierluigi, scappò verso l’altra uscita della terrazza ma un’altra guardia arrivò velocemente su dalle stesse scale. Una guardia disse ormai sei un trappola, arrenditi. Pierluigi, ci teneva a non essere catturato e avrebbe fatto di tutto, allora si sporse dalla terrazza mentre i due si avvicinavano. Fu lì che capì che poteva farcela, col cuore in gola e la guardia ad un soffio, flush si tuffó dalla terrazza. Durante il volo emise un gemito di follia, ammortizzó la caduta facendo delle capriole sul prato morbido che lo aiutò ad attenuare la caduta. Si chiuse sì tanto in se stesso che successe una cosa insolita, emesse una flatulenza e subito dopo sentì qualcosa che spinse nelle mutande proprio in prossimità del sedere. Non era paura ma causato dalla contrazione che fece il suo corpo. Era piccolo e non sentì alcun dolore, si alzò e continuò a correre fino a casa verso il bagno per finire ciò che era cominciato. Ad un tratto mentre si lavó le mani, sentì l’acqua, la sentì fino ai piedi si svegliò e vide una pozza enorme, si era scordato di spostare la suca e il pesco era pieno colmo d’acqua. Allora prese la suca e di corsa la sposto nella rasula sotto. Dopo un po' arrivo il nonno, Pierluigi era stanco e aveva fame chiese al nonno che ore sono? Il nonno alzò lo sguardo e guardò il sole e disse iè menzionnu ora ni calamu e annamu a manciari. Iai fami? Te docu manciati na pessica. 6 Pierluigi accenno un sospiro di sollievo, domandava spesso che ore erano al nonno, era affascinato da come sapesse sempre l’ora esatta senza l’uso dell’orologio. Il nonno preparò con le foglie dei fichi d’india ei limoni marci u pastrozzu 7 un secchio di mangime che diede alle pecore, andavano pazze per u pastrozzu. Il nonno prese il cavallo e si incamminarono verso casa. Mentre scendevano Pierluigi guardò la campagna ed in lontananza vide la gallina che stava lì, illuminata dal sole, riusciva a vedere chiazze di sangue rosso, qualche goccia che colava piano piano giù. Si sentiva un po' in colpa per la gallina, ma il pensiero svanì un attimo dopo arrivati a casa di fronte a una gran tavola bandita con tutta la famiglia lì pronti a pranzare. Bértula o bisaccia è un manufatto tessile comunemente diffuso su tutto il territorio regionale. Il suo uso ha origini antichissime ed è fortemente legata alla vita quotidiana. In ambito contadino, viene utilizzato per trasportare cibo come frutta, verdura o latte e uova. Oppure anche per utensili. ↩︎ Questa gallina non produce più niente. Non fa più le uova e non è più buona neanche per fare il brodo, dobbiamo tagliarle la testa. ↩︎ Prendi questo coltello e coltello, devi fare un taglio in gola. Tieni ↩︎ Ora voglio vedere se tornano ancora questi uccelli che portano solo guai ↩︎ Mbvirare annaffiare le piante, utilizzato sia con l’utilizzo della suca (canna, pompa) oppure anche tramite canalette a scorrimento d’acqua. ↩︎ E’ mezzogiorno, tra poco torniamo a casa e andiamo a mangiare. Hai Fame? Tieni mangiati una pesca. ↩︎ U pastrozzu composto di limoni e aranci, lasciati sotto un telone e sotto al sole, rimangono lì per giorni, settimane a macerare, in modo da comporre questa melma da un odore molto distinguibile e forte. Viene utilizzato come mangime per gli animali, ha ottimi valori nutrizionali. ↩︎
Din..Don..Din..Don.. Era Domenica mattina, le campane suonarono la prima volta alle 8 la mattina, il suono era molto forte e riecheggia in tutto il paese, quella domenica come tutte le domeniche a quell’ora quasi tutti dormivano compreso Gino. La vecchietta Concettina invece era sveglia da prima dell’alba, sprizzava di gioia e tanta energia, era il suo unico giorno di uscita. Come ogni domenica, Concettina, stava seguendo la sua routine domenicale, si stava preparando a festa! Concettina, dopo aver fatto una colazione molto abbondante, aveva bisogno di tante energie di domenica, si attinge ad effettuare tutte le sue preparazioni. Una doccia molto lunga per sentirsi ben profumata e sveglia, dopo si preparava i capelli, facendo tanti boccoli raccolti uno per uno per comporre come un mazzo di tulipani. Nonostante l’età Concettina aveva ancora tanti capelli, tutti neri. Faceva il colore pochissime volte. Il nero dei suoi capelli era sempre molto forte e acceso. Di domenica, usava legare i capelli con nastro di colore azzurro e rosa e ci faceva sopra un bel fiocco. La faccia tutta incipriata per coprire le occhiaie e un pò i solchi degli anni, pesanti rughe che nonostante la cipria, restavano comunque visibili. Sulle labbra, passava un rossetto, di un rosso ma di un rosso, molto forte che accendeva tutta la faccia e delineava un gran sorriso. Sorriso che aveva sempre stampato in faccia, un sorriso vissuto, che esprime molta serenità e bontà. Scorreva tutti i vestiti migliori, li guardava, li riguardava, li girava, ma alla fine sceglieva quasi sempre il suo vestito preferito. Un vestito bello a quadri nero bianco e grigio. Molto classico ed elegante. Ci attaccava sopra la sua spilla d’oro, spilla di un delfino che aveva ricevuto da sua mamma e a sua volta, sua mamma l’aveva ricevuta da sua Mamma Concetta. Aggiungeva, una collana di perle e due orecchini di perle per dare un tocco ancor più di eleganza. Prendeva la sua borsetta e la riempiva di tutto ciò che poteva essergli utile, un fazzoletto bianco, tutto ricamato da lei stessa, con le sue iniziali PC, un foulard nel caso facesse freschetto, un ventaglio nel caso facesse caldo e un ombrello nel caso piovesse. Pensava proprio a tutto e aveva tanto tempo per prepare tutto al minimo dettaglio. Una volta pronta, si posizionava nel suo posto preferito, posto in cui passava le mattine e i pomeriggi, in genere stava lì, seduta sul balcone a non fare nulla, guardare le macchine passare, i bambini giocare, le foglie cadere e il tempo passare. Invece oggi, aveva una cosa da fare. Stava lì seduta ad aspettare Petra. Petra era una ragazza giovane, figlia di Pippo, cugino di terzo grado di Concetta. Petra era molto affezionata a Concetta e ogni domenica, andava a prenderla a casa e l’accompagnava in chiesa, Concetta faceva fatica a camminare per i suoi dolori di artrosi dovuti all’età. Petra arrivò puntuale alle 10 be un ora prima dall’inizio della messa. Al suo arrivo di nuovo Din..Don..Din..Don.. le campane suonarono di nuovo a festa. Concetta abitava circa 150 mt di distanza dalla Chiesa, generalmente una persona giovane e in salute ci mette circa un minuto a percorrere questo tratto, invece Concetta e Petra uscivano almeno un ora prima, si la vecchietta Concetta aveva qualche acciacco ma non era per quello che uscivano così prima bensì perchè Concetta aveva tanto tempo a disposizione e quello era il suo giorno e voleva goderselo al massimo. Petra una volta arrivata, salutava Concetta. Buongiorno Zia Concetta! Ma comu ti parasti! Si troppu bedda stamatina! 1. Concetta ricambia con tanta gioia e un gran sorriso Buongiorno Petra! oggi, tu si a chiu bedda du paesi! Putemu fari a sfilata! 2. Petra aveva portato un fiore come tutte le domeniche, raccolto dal suo giardino. Si avvicinò a Concetta e lo mise sotto al fiocco e disse. Ora putemu nesciri si troppu bedda! 3. Allora prese la borsetta e insieme si avviarono piano piano verso le scale. Concetta si appoggiò a Petra da un lato e al corrimano in legno dall’altro lato. Faceva scivolare la mano sullo scorrimano, ma benché si fidasse molto di Petra, non si fidava delle sue gambe quindi, stringeva molto forte lo scorrimano che provocava uno stridio derivante dall’attrito della sua mano sudata che scivolava sullo scorrimano. Una volta finite le scale, Concetta si sedeva sul bisolo per fare una piccola pausa. Concetta una volta seduta disse Hai Hai! Chi bella iunnata i suli sta matina…ma Petra dimmi chi facisti a ieri sira? mi dissiru chi niscisti con cocca adunu. 4 Petra rispose ieri niscia cu Ginu. Ie un bravu figghiolo! allora subito Concetta ribatte: E cu ie Gino? I cui è figghiu? Petra ci penso un attimo, poi rispose: ie u figghiu i Mimma e i Pippu a bonanima sua. 5 Concetta subito curioso ribatte e chi facisti? cunta cunta…6 Allora concetta comincia: ma chi nì sai mi vinni a pigghiari fino a casa! ruvao tutto allicchittato supra a so vespa..Matri tuttu troppu beddu chi capiddi sistemati ie na camicia pulita… 7 Non finì di raccontare la storia che arrivò Mari Ciccia. Le tre si scambiarono affettuosi saluti e si incamminarono verso la messa. Appena partiti sentirono ancora Din..Don..Din..Don.. l’ultima ora, le campane suonavano ogni 15 minuti. Gino questa volta fece un sussulto sentendo le campane ma poi si girò dall’altra parte e continuò a dormire. Camminarono per ben 15 minuti tirati facendo qualche pausa qua e là e si ritrovarono in piazza. Una volta in piazza, camminarono ancora più piano in modo da farsi vedere da tutti. Si sedettero proprio nella panchina centrale della piazza, tutti le guardarono, loro salutarono e si misero a chiacchierare con tutti, quando ancora Din..Don..Din..Don… Ad un tratto, si avvicinò Cammela a cuttigghiara era sempre in cerca di qualcuno a cui raccontare i suoi curtigghiu ma soprattutto ascoltare altri curtigghi in modo da riempire il suo tempo, allenare la sua parlantina. Una volta lì davanti Buongiorno signore, ma come siamo belle questa mattina! le tre, ricambiarono il saluto. Carmela utilizzava sempre la stessa strategia, inizialmente buttava giù una serie di complimenti e ringraziamenti vari in modo da attrarre l’attenzione e fare sentire bene il suo pubblico, poi quando tutte pendevano dalle sue labbra partiva con la sua storia, inizia sempre dalla storia più in voga del momento, poi se vedeva esserci un ricambio allora andava giù più pesante fino agli scoop che manco i muri conoscevano. E fidatevi lei ne sapeva davvero tante, il suo lavoro era quello ed era assatanata dal saperne sempre di più, sapere tutto di tutti. Quel giorno esordì con la storia da Fubba (la furba). La Fubba, era una vecchietta che non si vedeva mai, stava sempre chiusa in casa e non si sapeva niente di lei, non si sapeva cosa facesse, come vivesse e se fosse ancora viva. Si parlava di lei spesso, era l’unica di cui nessuno aveva filtro. Si raccontavano tante storie brutte e belle ma la maggior parte di queste vedeva sempre la donna in maniera cattiva vincente e furba. Ieri a putia ntrasiu na signora, Gina (la proprietaria della putia), mi dissi…chi mai l’avia vista. Iera vecchia, ma strana, aviva nu stile tuttu particulari cu nu cappeddu a tipo chiddi chi si vidunu nta televisione e parrava italianu. Ma Italianu bonu, comu parrano chiddi chi studiano assai, sicuru chi fici magari i superiori. Na vota chi ntrasiu, vaddau tutti i cosi e ci fici 50 mila dumanni, e da ago e filo si cattao fino a attrezzi i travagghiu. Proprio strana sta vecchia. Si cattau si tanti cosi. Gina mi dissi chi manco nto misi migghiori fici tutto sto guadagnu. Appena nisciu Gina cuminciò a cantari e ballari da contentizza e chiamò subito so figghiu Filippò e u mannau mi ci va appresso pi capiri da unni nisciu sta vecchia. Allora Filippu ci cuntao chi si fici u giro i tutti l’attività du paisi. Cattò, tutto cattau, inchiu u carrello si cattò magari nautru carrelo e u ghinchì. Tutti aierie ieruno sbarruati i sta cosa e tutti i vindanti mi cuntaruno a stissa cosa chi era troppu stranuuu. Ma a sapiti na cosa? Filippu a nsucutau fino a casa e ndovinati aunnni ruvao…A casa da Fubba! Cumbinazioni ie fossi o ie proprio a Fubba? E cu sapi e cui a cunusci! mai nuddu a vidiu nta facci! 8 La storia era quasi finita quando fu interrotta dalle campane Din..Don..Din..Don..Din..Don..Din..Don.. che suonavano di più per segnalare che mancava solo l’ultima quarto d’ora all’inizio della messa. Allora le tre ringraziarono Carmela per la storia e si incamminarono verso la chiesa. Carmela non aveva tratto alcun guadagno da quello scambio allora si avvicinò e disse, u sapiti chi c’è, magari io a nari a missa oggi, vengu con vui così vi ncumpagnu fino a ghiesa. 9 In questo modo, nel tragitto poteva ascoltare le loro discussioni. Le tre lo sapevano, conoscevano bene Carmela, ma era inutile cercare di evitarla o non parlare. Allora si misero in marcia e ripresero a chiacchierare lo stesso però ogni tanto aggiungevano al discorso una fandonia in modo da rendere il lavoro di Carmela più difficile. Appena arrivarono sul ponte che regala una bella vista della grande chiesa del paese dall’alto, rimasero tutte e tre sbalordite, c’era un matrimonio e la piazza di fronte alla chiesa era tutta piena di persone belle, sorridenti e vestite a festa. Concettina allora divenne ancora più contenta, i suoi occhi si illuminarono e una lacrima di gioia scivolò giù per il viso. Anche Mari Ciccia lacrimò, ma le sue lacrime erano lacrime di dolore per suo marito, l’aveva perso da qualche mese e ancora il ricordo era molto accesso. Petra invece era contentissima, nella folla distinse gli sposi, e già si immaginò il suo futuro con Gino li in mezzo sorridenti. Carmela non era per niente sorpresa ovviamente già era a conoscenza del matrimonio e sapeva tutto sul matrimonio si trattava di una fuitina 10. Carmela era venuta apposta per vedere come le persone si erano vestite, studiare la gente e conoscere altre storie per dissetare la sua irrefrenabile voglia di conoscenza altrui. Buongiorno Zia Concetta! Ma comu ti sei vestita e sistemata bene! Sei troppo bella questa mattina! Your browser does not support the audio element. ↩︎ Buongiorno Petra! oggi, tu sei la più bella del paese! Insieme possiamo fare la sfilata di moda! Your browser does not support the audio element. ↩︎ Ora possiamo uscire, sei troppo bella! Your browser does not support the audio element. ↩︎ Hai Hai! Che bella giornata di sole questa mattina…ma Petra dimmi che hai fatto ieri sera? Mi hanno detto che sei uscita con qualcuno. Your browser does not support the audio element. ↩︎ E’ il figlio di Mimma e Pippo defunto e degno di rispetto. Your browser does not support the audio element. ↩︎ E che hai fatto? racconta… Your browser does not support the audio element. ↩︎ Non puoi capire, è venuto a prendermi fino a casa! e arrivato ed era tutto sistemato con la sua vespa bianca. Mi davvero troppo bello, con i capelli e la camicia perfetti… Your browser does not support the audio element. ↩︎ Ieri alla putia (negozio tipico siciliano dove in genere si vende qualsiasi cosa) è entrata una signora*, Gina (la proprietaria della putia), mi disse che non l’aveva mai vista. Era anziana e strana, aveva uno stile particolare, portava un cappello come quello che si vede alla televisione. Parlava in italiano molto bene. Come parlano le persone che studiano tanto, sicuramente avrà conseguito anche la scuola superiori. Una volta che entrò, ha guardato tutto il negozio e fece tante domande. Comprò tutto da ago e filo, fino agli attrezzi da lavoro. Ha comprato così tante cose che Gina, la proprietaria del negozio, neanche nel mese migliore guadagna così tanto. Appena uscita, Gina si mise a ballare e a cantare dalla contentezza e chiamò suo figlio Filippo e lo mandò a seguire la signora in modo da capire da dove proveniva. Filippo, raccontò che fece tutto il giro dei negozi del paese e comprò tantissime cose, Riempi il carrello e si comprò anche un altro carrello e lo riempì. Tutti i venditori erano sorpresi di questa cosa e mi raccontarono tutti la stessa cosa. Era troppo strano. Ma la sapete una cosa. Filippo la seguì fino a casa e indovinare dove andò….A casa della Furba! E’ una combinazione oppure è proprio lei? E chi lo sa? Chi la conosce! Mai nessuno l’ha vista in faccia.* Your browser does not support the audio element. ↩︎ Lo sapete che c’è, anche io devo andare a messa oggi, vengo con voi così vi accompagno. Your browser does not support the audio element. ↩︎ Fuitina: fuga repentina che identifica l’allontanamento di una coppia di giovani aspiranti coniugi dai rispettivi nuclei familiari di appartenenza, allo scopo di rendere esplicita (o far presumere) l’avvenuta consumazione di un atto sessuale completo. ↩︎
Gino apri gli occhi, quella mattina si svegliò un po’ turbato di ciò che era avvenuto la sera prima ma soprattutto con un brutto pensiero, forse aveva fatto un incubo. Sollevò lo sguardo, guardò l’orologio e la sua faccia si scurì immediatamente. In un attimo aveva realizzato che era giovedì e doveva fare una commissione. Non aveva la minima voglia di fare questa commissione e rabbrividì al solo pensiero. Si girò d’altro lato sperando di addormentarsi nuovamente e di svegliarsi il giorno dopo. Chiuse a malapena una palpebra quando sentì una voce dal profondo e non era un incubo, purtroppo, era sua mamma che urlava: Gino svigghiati chi iè taddu. Ti ricoddi chi m’ha spicciari da cosa a posta? U sai chi io non ci pozz’ annari! 1 Quindi, era tutto vero. Sua mamma aveva bisogno di lui, ma la commissione alla posta Gino non l’aveva mai mandata giù, la faceva controvoglia e lo metteva di malumore. Allora risponde alla mamma: mamma fammi dommiri, fammi dommiri autri 10 minuti! Poi mi siddiu e sugnu stancu e u sai che na suppottu Naiu Valia! 2 la mamma gli urlò subito contro insistentemente fin tanto che Gino decise di alzarsi. Scese le scale con i capelli e la faccia stropicciata, prese il caffè e lì accanto trovò le carte, carte che doveva portare alla posta. Le guardò, poi si mise a giocare con la tazzina di caffè mettendola vicino agli occhi in modo da fare sparire le carte dal suo sguardo. Alla fine si rassegnò, si alzò, prese le carte e uscì. Attraversò il vialetto e si ritrovò di fronte alla piazza del paese. In piazza a distanza vide due donne, già da lontano riconobbe Cammela a cuttighiara 3 che parlava con una certa veemenza che la caratterizzava. Stava raccontando alle vicine la disavventura proprio di Gino del giorno prima “Scinni Papà”. Carmela non appena lo vide arrivare, cambiò subito discorso, tanto sapeva tutto di tutti e aveva tanti cuttigghi a disposizione da raccontare. Gino si sentì un po’ osservato mentre passava e percepì in sottofondo un po’ di sghignazzi. Una volta in piazza, vide Don Gianni che stava lì seduto ad aspettare che il tempo passasse. Don Gianni salutò Gino con molto rispetto e un bel sorriso affettuoso, erano molto amici i due. Gino ricambiò. Poco più avanti Ciccio invece gli disse: Ma tò patri a unni u lassasti? Aoggi nun scinniu? 4 chiaramente una provocazione, una presa in giro poco velata da parte di Ciccio. A quel punto Gino capì che si riferiva al giorno precedente e allora rispose a tono: Ciccio Birra ma picchì non ti bivi na birra e ti cucchi? 5 Proseguì però senza fermarsi, non voleva problemi quella mattina, già ne aveva tanti, la sua missione principale era la commissione in posta. Superata la piazza, si ritrova di fronte alla posta. Già in lontananza poteva sentire la puzza di sigaretta misto profumo d’arancia e di cocco di Naiu Valia e già gli veniva un senso di vomito. Non appena arrivò all’ingresso la vide. Naiu Valia era lì, sicca sicca, con i capelli scuri, la faccia scura, vestita di nero, come sempre, a non fare nulla. Aveva quello sguardo rilassato ma anche minaccioso e incavolato. Allora Gino entra in silenzio e lei subito a tono: cu non mori si rividi! Ma cu ti porta? C’ha fari? 6 Gino rispose dicendo che doveva fare due raccomandate, prendere un pacchetto…. non lo fece neanche finire di parlare che rispose Ma chi ti sbigghiasti ora? tutto oggi a fari? iè tardu, su i 10, no sai chi io a fari a pausa? Si voi mi poi spettari ca, poi quannu tornu videmu chi putemu fari. 7 Gino non aveva voglia di problemi quella mattina allora rispose che andava bene e si sedette. Aspettò lì seduto sino alle 11 ma niente, di Naiu Valia nessuna traccia. Allora si fece una passeggiata e andò a bere un po’ d’acqua al lavatoio (posto utilizzato una volta per lavare i panni). Una volta ritornato vide due vecchiette dentro che dovevano ritirare la pensione e Naiu Valia che era appena tornata. Gino allora si avvicinò e Naiu Valia lo richiamò subito Gino ma che fai? non vedi che c’è gente. Devi aspettare il tuo turno e prendi il numerino. Mica siamo al mercato qui! allora Gino stava per innervosirsi ma sapeva che non era il caso e non poteva perchè se tornava a casa senza aver sbrigato le commissioni poi sentila la mamma. Allora disse: va bene aspetto e prese il numerino. Naiu Valia, cominciò a parlare con le vecchiette di tutto, sembrava di essere davvero al mercato. Cominciarono dal l’uncinetto per finire con le ricette di cucina siciliane. Gino non ne potè più, era stanco e si addormentò. Non appena aprì gli occhi vide che non c’era più nessuno ed era finalmente arrivato il suo turno. Allora si avvicinò al bancone, lì di fronte a Naiu Valia, lì pronto con tutte le sue carte e le sue cose da sbrigare, dice: è il mio turno adesso giusto? Naiu valia rispose: Ma beddu chi ti sbigghiasti ora! Non vidi chi ie menzionnu. Io annari a manciari. Tranquillu….rilassati i poi fari dumani! tantu ca fari? 8 Se siete arrivati a leggere fin qui avete capito perchè l’addetta della poste aveva come soprannome Naiu Valia. Questa storia ovviamente è un’ esasperazione. Non vuole essere provocatoria verso gli uffici postali né verso gli addetti postali né contro il sud o il nord, è solo una storia, prendetela così com’è e state tranquilli e rilassatevi! Traduzioni e audio: Gino svegliati che è tardi, ti ricordi che mi devi sbrigare quelle commissioni all’ufficio postale? Lo sai che io non posso andarci! Your browser does not support the audio element. ↩︎ Mamma fammi dormire! fammi dormire altri 10 minuti! Poi mi scoccio e sono stanco e lo sai che non sopporto l’addetta della posta! Your browser does not support the audio element. ↩︎ cuttighiara persona che ama sparlare di altre persone Your browser does not support the audio element. ↩︎ *Ma dove hai lasciato tuo papà? Oggi non è sceso con te? *Your browser does not support the audio element. ↩︎ Francesco detto Ciccio Birra perchè non ti bevi una birra e vai a letto? Your browser does not support the audio element. ↩︎ Chi non muore si rivede! Ma chi ti ha consigliato di venire? Che cosa devi fare? Your browser does not support the audio element. ↩︎ Ma ti sei svegliato ora? Devi fare tutto oggi? è tardi, sono le 10, non lo sai che io alle 10 devo faccio la pausa? Se vuoi puoi aspettarmi qui. Quando trono vediamo che cosa riusciamo a sbrigare. Your browser does not support the audio element. ↩︎ Ma bello, ti sei appena svegliato! Non vedi che è mezzogiorno. Io devo andare a pranzare. Stai tranquillo e rilassati, le puoi sbrigare domani le commissioni. Tanto che cosa hai da fare? Your browser does not support the audio element. ↩︎
Scendi papà! disse Gino seduto sulla sua vespa una volta arrivato nella piazza centrale del paese. Non sentendolo scendere allora ripeté dai..scendi papà! Ma niente, allora una terza volta scendi pa.. lo ripetè girandosi, ma il papà non c’era! Allora cominciò ad impallidire e divenne bianco, un tutt’uno con il bianco della sua tanto amata Vespa. Sfortunatamente per Gino quel giorno era la festa del paese di Sant’Antonio e la piazza era piena colma di gente. Tutti, in piazza, si girarono a guardare la scena e cominciarono a ridere. Gino dal bianco divenne rosso, poi girò la vespa era molto preoccupato per il papà allora tornó indietro alla sua ricerca. Dal paese scese giù in velocità cercando di guardare in tutte le direzioni, era agitato e man mano che scendeva e non lo trovava si agitava sempre di più. Una volta arrivato al paese adiacente vide un vecchietto seduto a bordo strada su una sedia bianca, in silenzio che osservava. Gino si avvicinò e gli chiese se aveva visto suo papà. Lui rispose 153, 24, 7 e 1. Gino non capì allora proseguì per la strada e dopo poco, finalmente trovo tanta gente, tutti attorno a soccorrere il papà. Hai hai! Urlava il padre. Chi figghiu sciaguratu chi iaiu! Nenti vali! mancu a Vespa sapi puttari! Gino quella mattina aveva commesso due grandi errori. Quello di aver fatto cadere il padre dalla vespa senza accorgersene, ma anche di essersene accorto troppo tardi e davanti a troppa gente. Il secondo fu il problema più grosso, che lo segnó a vita. Da quel momento la sua storia tramite il cuttigghio, fu raccontata a tutti in paese poi nel paese vicino e così via in tutta la città. Fintanto che tutti non arrivarono a conoscere la disavventura di Gino. Fu un attimo che Gino fu soprannominato “scinni papà!” Fortunatamente Gino, non andava troppo veloce con la Vespa allora suo papà, se la cavò con solo qualche piccola ammaccatura e un’abrasione lungo tutto il braccio. Gino non se n’era accorto poichè quella mattina era sommerso di pensieri, pensava alle commissioni che lo attendevano l’indomani. Ma alla fine, cosa voleva dire il vecchietto saggio a bordo strada? Era lì da tutta la mattina e aveva visto passare 153 macchine, 24 moto ape, 7 motorini e 1 solo stupido, si è fermato dopo essere passato più volte con e senza papà. Traduzioni: Chi figghiu sciaguratu chi iaiu! Nenti vali! mancu a Vespa sapi puttari! Che figlio sciagurato che ho! non vale nulla! non sa neanche guidare la sua vespa! cuttigghio sparlare di altre persone
Si cominciava ad intravedere qualche raggio di sole, che splendeva sui ciottoli bianchi della spiaggia. I piccoli vetrini creavano un riflesso ancora più forte, quasi abbagliante. In lontananza si intravedevano delle barchette che dondolavano in mare. Quella mattina l’acqua era molto calma, piano piano le piccole barchette si avvicinavano verso la riva e divennero pescherecci di media grandezza. Sul lungomare, una coda di lape (moto ape) in attesa. Gino sta ancora dormendo, ieri ha fatto consegne fino a tardi. Oggi u mari ie chinu i pisci! 1 si sente urlare in lontananza. La prima barchetta di un anziano pescatore attracca, un pescivendolo si avvicina e con il suo garzone comincia a scaricare cassette piene di pesce. Le trasportano sulla loro ape, di colore giallo sbiadito, ne aveva fatta di strada e ne aveva preso di sole quell’ape. Riempiono il cassone con le cassette piene di pesce fresco e ci misero sopra pezzi di ghiaccio per mantenerlo. Chi bellu pisci, pisci friscu!! pisci spada, sciabulata, aguglia, curstadelle io iaiu! 2 sentì in lontananza Gino, stava ancora dormendo e voleva tanto continuare a dormire ma oggi era martedì e come tutti i martedì puntuale passava Francu u pisciaru. Mu fai un chilu i curstadelle!! 3 sentì Gino urlare dalla casa accanto. il pescivendolo si ferma, scende e si avvicina alla bilancia, bilancia bellissima, enorme, con due piatti e un bilanciere dorato. Mette due pesi da mezzo chilo sopra un piatto della bilancia, sull’altro, mette un foglio e ci butta sopra a pugni le costardelle fintanto che i due vassoi non siano in equilibrio. Signura c’ha regalu! sulu sta junnata 5 euru o chilo! 4 Sempre urlando, in modo che anche le massaie vicine potessero sentire. Un cestino di vimini (u panaru) cala giù con una carrucola dal balcone. Il garzone prende i soldi dal cestino e ci mette le corstadelle, ci aggiunge due limoni e un pò di prezzemolo. Fortunatamente per Gino, quella mattina nessun altro tra i suoi vicini rispose all’offerta di Francu. Allora sentì il forte rumore dell’ape che piano, piano si allontanava. Il rumore stava per terminare e finalmente Gino avrebbe potuto riprendere a dormire. Banane, mulinciane, pumadoru, kiwi io iaio. Culi voli l’ovaaa! 5 il sonno di Gino fu presto interrotto da Pippo u viduraru. Viniti,viniti,accattati,accattati, tutta roba frisca iè! 6 Alla fine Gino come tutti i martedì, decise di alzarsi, anche se ancora presto e ancora assonnato, spalancò la finestra, un forte sole e tanto calore illuminò la sua giornata. Audio Siciliani Oggi u mari ie chinu i pisci! Your browser does not support the audio element. ↩︎ Chi bellu pisci, pisci friscu!! pisci spada, sciabulata, aguglia, curstadelle io iaiu! Your browser does not support the audio element. ↩︎ Mu fai un chilu i curstadelle!! Your browser does not support the audio element. ↩︎ Signura c’ha regalu! sulu sta junnata 5 euru o chilo! Your browser does not support the audio element. ↩︎ Banane, mulinciane, pumadoru, kiwi io iaio. Culi voli l’ovaaa! Your browser does not support the audio element. ↩︎ Viniti,viniti,accattati,accattati, tutta roba frisca iè! Your browser does not support the audio element. ↩︎
Giuseppe te ca 1 euro, vammi a cattari na buttigghia i pommarola chi stamatina ma scuddai ma pigghiu a putia. Cu restu a cattiti chiddu chi voi, basta chi non ti catti vularie. Si proprio te na cattari, a cattiti i carruba chi su boni. 1 Questa frase sembra molto normale oggi, va be in sicilia ovviamente, ma un un tempo, non molto lontano, quando la globalizzazione, la tecnologia e commercializzazione dei prodotti non era così forte come ai giorni odierni, la frase non reggeva. Non tutti avevano la possibilità di comprare una bottiglia di pomodoro. E allora come si faceva? non si mangiava la famosissima e ricorrente pasta alla norma? Magari all’epoca non era così ricorrente era un piatto della domenica. No non se ne faceva a meno e per non farlo mancare a tavola, quasi tutti si adoperavano a fare le bottiglie in casa, in garage oppure in giardino. Giuseppe era molto ubbidiente e tornò a casa con la passata e le carrube. La passata che diede alla mamma, gli ricordò la storia di buttugghi i pummorola allora disse alla mamma, se poteva raccontarle la storia di quando lei faceva le bottiglie in casa. La mamma dapprima stentò a raccontarla, aveva già raccontato la storia delle bottiglie si tante volte che non aveva voglia, ma davanti alla faccia del figlio Giuseppe che esprimeva in uno sguardo “dai dai mamma ti prego raccontamela per favore..lo sai che mi piace tanto..” Allora la mamma non ne può fare a meno e comincia la storia. La preparazione delle bottiglie di pomodoro era una ricorrenza annuale, era una cosa bellissima e per noi tutti nella famiglia era come se fosse una festa. La notte precedente alla preparazione delle bottiglie di pomodoro, non riuscivo a pigliare sonno per l’agitazione e per la voglia di fare le bottiglie. La mattina prestissimo, prima ancora dell’alba, mi posizionai li in terrazza ad aspettare il rientro del nonno. Quel momento lo ricordo sempre, mi luccicavano gli occhi a vedere la lapa i Puddicinu arrivare carica di pomodori, tutti rossi ma rossi, rossi belli tutti luccicanti. Non so se perchè ero piccina oppure perché i pomodori erano i casa 2, riuscivo a percepire l’odore già da quella distanza. Di corsa balzavo giù per le scale urlando come una forsennata Oggi facemu i buttigghi!! Oggi facemu i buttigghi!! una volta lì davanti alle cassette, ne prendevo sempre un paio, li toccavo, il odoravo… ummm che buoni!!! Ii adoravo. Gli davo una sciacquata veloce e li mangiavo così sani sani. Nel cortile la nonna preparava tutte le bottiglie di vetro, bottiglie recuperate da innumerevoli bevute di birra. Una per una, le insaponava e metteva in ammollo nella bagnarola. Noi ragazze, le davamo una mano, le immergevamo nelle bagnarole in modo da rimuovere bene tutto il detersivo e così da lavarle bene bene. Ogni tanto di nascosto arrivavano i cugini che ci bagnavano con la pompa, noi per giocare poi bagnavamo loro. Era divertentissimo ridevamo e correvamo come i pazzi. La nonna se ci vedeva si biliava e ci urlava contro Viditi chiddu ca aviti a fari, si vi sciuppiati poi vi dugnu i supra. Oggi amu a travagghiari amo a fari i buttigghi, e si cuntinuati accusi finisci chi vi sciuppiati e namu a ricurdari a iunnata. 3 infine tutti bagnati finivamo di sciacquare le bottiglie rimaste. Lasciavamo tutte le bottiglie, tantissime bottiglie al sole tutte luccicanti a riposare e asciugare. Ma i giochi con l’acqua non finivano qui! Subito dopo si cominciava con il lavaggio delle cassette dei pomodori. buttavamo i pomodori dentro le bagnarole, li lasciavamo in ammollo per un pò e poi uno per uno li prendevamo e riponevamo dentro le cassette. La nonna e le zie nel frattempo, preparavano un gran fuocherello acceso per terra e sopra al fuoco c’era la più grande casseruola mai vista, io anche se piccola, ero molto alta, superavo il metro e venti ma ciò nonostante, non arrivavo a vedere dentro la casseruola. Per vedere dentro, dovevo salire su una scaletta. Mi piaceva guardare i pomodori mentre cuocevano, era bello tutto quei pomodori che strofinavano tra loro, tanto calore e bollore che usciva e un profumo intenso di pomodoro. I pomodori più piccoli ogni tanto saltavano in alto per il tanto bollore. Con un grande arnese, che io da piccola lo vedevo come il più grande cuppino che si è fuso ad uno scolapasta, venivano afferrati i pomodori pronti, quelli più pronti, pronti perché stavano a galla, lì pronti ad uscire. Una volta afferrati, li facevano rotolare giù per la macchina mangia pomodori. La macchina mangia pomodori, stava sotto il controllo della zia Pizzitta. Lei si occupava di mettere in moto la macchina, fermarla se bloccata e aiutava i pomodori a essere spremuti per bene. Li aiutava con un mattarello, li spingeva giù per il canale e man mano che scendevano, gli diceva: entrate entrate, mettetevi in fila e non spingete ci state tutti e a breve sarete uniti per sempre. Un bel succo rosso vivo, molto concentrato, caldo e intenso, colava giù dalla macchina e finiva in una grande pentola. Da un altra estremità della macchina, uscivano le bucce e Pizzitta, di tanto intanto si occupava di passarle nuovamente nella macchina. La postazione successiva era proprio la mia e della zia Maria. Io mi occupavo di prendere le bottiglie dalle ceste, di staccare le foglie dal grande mazzo di basilico e di mettere due foglioline all’interno di ogni bottiglia. Mi piaceva tanto questo compito perchè sentivo l’odore intenso del basilico sulle mani. La zia Maria, prendeva la bottiglia da me preparate e la metteva sotto il rubinetto, girava la valvola e fuoriusciva il caldo succo di pomodoro. Lasciava aperto il rubinetto fintanto che la bottiglia non si fosse ben riempita di succo di pomodoro. Lo zio Tano, con grande maestria e tanta attenzione, prendeva le bottiglie molto calde per via del sugo che fumava fin fuori dal collo delle bottiglie, le piazzava sotto un’altra postazione. Prendeva un tappo e lo attaccava ad una calamita di un altro macchinario, poi con decisione e fermezza girava una grossa manovella fintanto che il tappo non fosse ben attaccato alla bottiglia. Faceva una veloce verifica in modo da testare che fosse ben chiusa e dava l’ok a Giorgo che si occupava di prendere la bottiglia e riporla nella cassetta. Giorgio non era di famiglia ma era come se lo fosse. Noi lo facevamo stare li a svolgere il suo compito. Sebbene fosse un piccolo compito, lo faceva tanto contento. Adesso le bottiglie di pomodoro, dopo tutti questi step seguiti con cura sono pronte!! Giuseppe era ancora molto attento e sentendo la false e sapendo che la storia non finisce così subito ribatte dai mamma lo so che non finisce così, continua a raccontare. E allora la mamma riprende: è vero, Il bello deve ancora arrivare. Si prendevano dei grandi barili ex barili di petrolio davvero enormi sempre molto più alti di me. Lo zio o la nonna con l’aiuto di una scala, si calavano dentro a testa in giù e pian piano ci mettevano dentro le bottiglie di pomodoro, disposte una accanto all’altro non proprio attaccate ma vicine, poi sopra uno strato di panni e pezze vecchi e così via. Quando si giungeva alla fine, sopra l’ultimo strato di pezze, si mettevano delle pietre per tenere il tutto fermo. Infine si versava dentro l’acqua con una pompa che dal basso saliva fino in cima, era importante che fosse riempito tutto fino all’orlo. A questo punto, proprio sotto i grandi barili, venivano accesi altri grandi fuochi. Dopo un pò di ore, il tempo di mettere apposto tutta l’attrezzatura, si spegnevano i fuochi e si lasciavano dentro le bottiglie tutta la notte a raffreddare e a riposare. Nta notti a Nonna non pigghiava sonnu, si girava e si vutava, stava cu penseri pi buttigghi. Giuseppe u sai picchì a nonna aviva u penseri e buttighie? 4 disse la mamma. Picchi na vota, succidiu nu macello, i buttigghi non furunu chiusi pi come si devi e nta nanzi a notti, i buttighi scuppiarunu e na ranni botta si sintiu! 5 rispose subito Giuseppe a tono. Si bravo, fecero una botta fortissima e la nonna saltò in aria. Quella notte fortunatamente era andato tutto bene, nessuna bottiglia scoppiò. L’indomani mattina, tirarono fuori tutte le bottiglie una per una ed erano tutte integre, troppo belle e troppo buone. Riponemmo le bottiglie all’interno delle cassette che poi posizionammo all’interno di grandi scaffali. Che bello tutte le bottiglie per l’anno per la famiglia erano pronte. Ne prendemmo subito un paio e preparammo una pasta al pomodoro per tutti. Quella pasta, la ricordo ancora, aveva il sapore dei pomodori ma anche di tutti noi e di tutti gli sforzi fatti per produrre la passata. Dai Giuseppe è pronto, adesso mangiamoci la nostra pasta al pomodoro e immaginiamo di averla fatta con le nostre mani. Dai Giuseppe è pronto, adesso mangiamoci la nostra pasta al pomodoro e immaginiamo di averla fatta. Traduzioni: Giuseppe tieni 1 euro, vai a comprare una bottiglia di pomodoro, questa mattina me la sono scordata di comprarla alla bottega. Con il resto dei soldi, comprati tutto quello che vuoi, però non ti comprare porcherie. Se proprio te le devi comprare, compra le carrube che sono più sane. Your browser does not support the audio element. ↩︎ prodotti a kilometro 0, prodotti in campagna familiare. ↩︎ Prestate attenzione a quello che fate, se vi fate male poi io vi do altre botte. Oggi dobbiamo lavorare, dobbiamo fare le bottiglie di pomodoro, se continuate così, rischiate di farvi male e poi finisce che finiamo all’ospedale e ci ricorderemo per sempre di questa giornata sventurata. Your browser does not support the audio element. ↩︎ Durante la notte la nonna non prendeva sonno. si girava e rigirava, stava con il pensiero alle bottiglie. Giuseppe lo sai perchè la nonna aveva il pensiero alle bottiglie? Your browser does not support the audio element. ↩︎ Perché una volta è successo che non erano state chiuse bene e allora durante la notte sono scoppiate, facendo un gran fracasso Your browser does not support the audio element. ↩︎
Bicchieri svuotati dall’alcol e sparsi di qua e di là su un tavolo colmo di disordine. I bicchieri, sono pieni di menta e lime ormai secchi con uno stretto intreccio che li lega insieme. Posaceneri pieni colmi di cicche, cenere e carta bruciata ancora lievemente fumante. Una forte puzza di fumo presente in tutta la stanza. Amici e gente che non conosco, dormono non molto lontano da me, alcuni sono schiacciati uno sull’altro tutti presi da un forte sonno profondo, pesantemente sprofondati in divani e cuscini che reggono il peso e si deformano per rendere più confortevole il viaggio. Mi sento bene un lieve giramento di testa ma bene, qualcosa è successo ma in quel momento non ricordo nulla. Sorrido e sprofondo ancora in un altro sonno profondo. Riapro gli occhi e vedo i posaceneri che cominciano a muoversi, vanno verso il pattume dove vengono svuotati da Petra. Dal posacenere cade di tutto ma nonostante il posacenere sia trasparente non permette di vedere attraverso. I posaceneri vengono sbattuti per fare cadere la cenere residua, ma rimangono comunque neri, di un nero scuro scuro che mi accende un ricordo, il nero del nero di seppia della pasta mangiata la sera prima, che buona! Faccio forza per tirare su il braccio pendente giù dal divano e scorgo un cuoricino sull’avambraccio con una scritta si senti lu cori chi batti. Sai aunni truvarmi. 1 Sono molto perplesso, non ricordo ma sento qualcosa e mi piace. Una scimmia entra dalla finestra era stata spalancata per via della puzza di fumo. La scimmia, comincia ad urlare e fa un chiasso della madonna, quasi tutti si svegliano, si alzano di colpo ed escono fuori in giardino. Io salto in piedi di colpo e faccio dei movimenti ondulatori avvicinandomi verso la scimmietta, cerca ndo di farla uscire dalla finestra. Ma la scimmia non ne vuole sapere, prende un mela e me la scaglia contro poi prende un arancia e la scaglia sempre contro di me con più potenza e cattiveria, continua così con tutto il cestino di frutta. Io dopo aver preso la prima mela in faccia prendo un bastone e come in fruit ninja cerco di colpire la frutta mandandola a destra e a sinistra. Frutta che vola in tutta casa e muri che si sporcano di succhi vari. Appena la frutta finisce, cerco di avvicinarmi alla scimmia aprendo le braccia in cenno di pace, allora le dico scimmietta camo a fari, non vidi chi semu a menzu a na strada. Magari tu ta mettiri a fari burdellu. 2 Allora la scimmia comincia a parlare e io sono ancora più perplesso e dice ma chi ci fai ca, senti lu cori e vai a circari a fimmina chi ieri sira t’apriu lu cori. 3 A quel punto ho realizzato che stava tutto nella mia testa, le scimmie possono anche lanciare la frutta ma non credo parlino e poi che ci fa una scimmia in sicilia? I ricordi ancora non mi aiutano, penso che un caffè possa aiutarmi, allora mi alzo e sento un dolore al ginocchio, mi accende un ricordo dei piccoli kart della gara organizzata la sera prima. io troppo grosso, non ci stavo nel kart e allora mi sbattevano le ginocchia sulla carrozzeria del kart. Prendo la caffettiera del caffè e mentre cerco di aprirla, vedo un’altra scritta sull’altro braccio non ti fari pigghiari du sonnu! vidi chi ie ionnu! vidi chi ionnu! 4 ma che minkia è successo ieri notte penso… Fiuuuubrururburur bu sento il rumore del caffè, è salito, spengo il fornello e mi verso una grande tazza, ne avevo proprio bisogno. Il rumore e probabilmente l’odore sveglia anche Pippo che si avvicina un pò storto e dice Minkia comu ni divirtemmu aieri sira…chi c’è u cafè? 5 Faccio un cenno con la testa allora si versa anche lui una tazza e si accende subito una sigaretta. Gli chiedo se si ricordava qualcosa della donna che avevo conosciuto la sera prima, ma niente, non ricordava nulla e rideva come un matto guardando le scritte che avevo addosso. Perlustrai attorno in cerca di qualcosa e vidi teli da mare bagnati, mi ricordai del bagno di mezzanotte dopo i kart. Ad un tratto vidi dei biglietti, biglietti di un circo, ricordavo davvero poco sul circo, però pensai alla scimmia di poco fa e magari poteva esserci qualcosa di collegato con la mia strana immaginazione. Allora mi guardai attorno presi i miei averi e dissi Minni vaiu ou circu. Coccaduno vole a veniri o vi siddiati? 6 sai come quando butti lì qualcosa e l’unica cosa che senti è l’eco di ciò che hai detto. Ecco, la scena è stata proprio questa. Buttai un cenno di saluto a caso a cui ovviamente nessuno rispose mi girai e mi incamminai verso l’uscita. Una volta fori, pensai a come andare. Il circo non era lontanissimo e avevo voglia di riflettere ma soprattutto smaltire tutto l’alcol che avevo assunto la notte prima, allora decisi di andare a piedi. Man mano che camminavo mi ricordai che comunque non avevo alcun mezzo con me in quanto era passato Pippo a prendermi in motorino la sera prima. Rosso e bianco era il grande capannone centrale che si intravedeva già in lontananza, schiamazzi e urla di bambini, fuochi e versi di animali si sentivano da più vicino. Ero stanco e non volevo perdere troppo tempo, allora pensai a cosa potevo fare per accedere ma soprattutto per ricevere risposte. Mi avvicinai all’ingresso e fui subito attratto da una signora bella tutta truccata con colori molto vivaci, i capelli tutti intrecciati che formavano una torre che finiva a punta e su una stellina luminosa. Mi avvicinai, non sapevo cosa dire allora improvvisai: Buongiorno signora ieri mi sono scordato la giacchetta qui, non è che l’avete vista? La signora tirò fuori un sorriso a 27 denti di cui più della metà splendevano d’oro e d’argento e rispose Fosse solu a giacchetta chi ti scurdati aieri sira! Ieri voi carusi, facistu nu maciello! 7 Allora vista la mala figura, mi sentii a mio agio a parlare sinceramente di ciò che ero venuto a fare, la donna mi prese in simpatia anche perchè ieri l’avevamo fatta ridere parecchio allora mi concesse di entrare a dare un occhiata in giro, mi disse di andare al bar dove avrei potuto trovare risposte e disse, spetta u figghiolo ca scimmia. 8 Entrai e andai alla ricerca del bar dove ordinai un caffè mentre attendevo il ragazzo con la scimmia. Poco dopo aver finito il caffè arrivò il ragazzo con la scimmietta. Non appena arrivato, la scimmietta mi corse addosso e mi strofino la testa sul braccio con dolcezza come per salutarmi. Io ricambiai e mi ricordai che durante la notte avevamo giocato assai insieme, le lanciavo noci, nucidde e nocitte e la scimmietta le prendeva al volo e le mangiava. Nel frattempo Marcello mi raccontò tutto della serata trascorsa lì al circo e io man mano avevo dei ricordi un pò più nitidi. Avevo messo la mano nella bocca del leone e non avevo avuto paura. In questo momento solo al ricordo mi salì un gelido formicolio di paura. In questo istante molto probabilmente, non l’avrei mai fatto. Filippa si chiamava la ragazza con cui avevo parlato la notte prima e faceva parte delle danzatrici volanti. Mi ricordai che avevo avuto una scossa notevole guardandola lì, sul filo mentre danzava era di un soave indescrivibile. Marcello, mi disse che subito dopo lo spettacolo avevamo parlato tanto ed eravamo stati molto vicini. Non ricordavo bene però sentivo qualcosa. Allora chiesi dove potevo trovarla e mi disse che a quest’ora stava sempre al capannone centrale ad allenarsi. MI diressi verso il capannone e nel contempo cercavo di ricordarmi, il cuore mi batteva ero leggermente agitato non sapevo che dire che fare. Ad un tratto la vidi e la riconobbi, stava proprio di fronte al capannone stava facendo una pausa, man mano che mi avvicinavo i ricordi diventano sempre più chiari, mi ricordai dei capelli neri ricci degli occhi sempre neri molto penetranti, mi apparve tutto chiaro. Però una volta lì davanti ero imbarazzatissimo e non sapevo che fare, preso dall’imbarazzo e da qualcosa ma non so bene cosa, mi avvicinai e la bacia in bocca. Lei inizialmente mi allontanò e rimase un po scossa ma poi si prese e cominciò a baciarmi e ci legammo insieme per un pò. Dopo qualche istante, ci allontanammo, giusto per vederci in faccia. Lei stava per dire qualcosa allora io la interruppi e le mostrai il braccio. Braccio in cui la notte prima era stata scritto il messaggio d’amore. Il volto di lei, si trasformò in un punto interrogativo, era perplessa come per dire ma che minkia è? Molto probabilmente non era la donna della sera prima pensai. Diventai rosso, non sapevo che fare, pensai che ormai qualcosa fosse nato. Nato a caso ma forse non veramente a caso e comunque poteva non avere importanza. Un attimo dopo realizzai che il tutto non aveva senso e pensai di girarmi e scappare via, via da quell’imbarazzo da quella scena troppo strana, in effetti avevo appena baciato una completa sconosciuta così a caso. Sono ancora in tempo! stavo per girarmi e filare via quando ad un tratto lei fece un sorriso, sorriso strano che voleva dire qualcosa, un po malizioso, si avvicinò e mi baciò ancora più intensamente come se mi conoscesse. Ma boh io come al solito non ho capito niente, ero un pò stanco e assonnato, ho guardato l’altro braccio e ho riletto non ti fari pigghiari du sonnu! vidi chi ie ionnu! vidi chi ionnu! allora spinto da questa frase e dall’impeto del bacio, mi svegliai di colpo e continuai a baciarla per tanto, tantissimo tempo. Eravamo come completi sconosciuti dove, l’unica parola scambiata era stato un bacio, a volte un bacio può dire ed esprimere più di mille parole. Traduzioni: Se senti il cuore che batte, sai dove trovarmi. ↩︎ Scimmietta che sta succedendo, non vedi che siamo già a disagio. Devi contribuire anche tu a fare traffico. ↩︎ Ma che ci fai ancora qui. senti il tuo cuore e vai a cercare la donna che ieri sera ti ha aperto il suo cuore. ↩︎ Non ti fare prendere dal sonno. Vedi che è giorno, vedi che è giorno. ↩︎ Perbacco, come ci siamo divertiti ieri sera… che c’è il caffè? ↩︎ Vado al circo. Qualcuno vuole venire o vi scocciate? ↩︎ Fosse solo la giacchetta che ti sei scordato ieri sera! Ieri voi ragazzi, avete fatto un chiasso esagerato! ↩︎ Aspetta il ragazzo con la scimmia. ↩︎
Erano le 4.30 e Pierluigi aveva ancora gli occhi chiusi. Era sommerso in un sogno profondo. il sogno di un gioco, guardie e ladri. Pierluigi, era alla ricerca di un posto posizionato in alto, un luogo dove avrebbe potuto godere di una buona visione con multiple vie di fuga. Scelse la terrazza di Floride, una terrazza enorme che offriva una vista sulla piazza centrale del paese. Da lì poteva vedere tutta la piazza e seguire le guardie che avevano appena finito di contare e stavano per andare alla ricerca dei ladri. Un sobbalzo lo svegliò aprì leggermente gli occhi e vide a bettula 1 del nonno. L’aveva usata come cuscino e gli aveva biascicato addosso mentre dormiva. Il sobbalzo era stato causato da Stella la cavalla del nonno che si stava destreggiando nella dura salita che portava alla campagna. Cooooocoocode sentì, un colpo di ali e la bettola iniziò a muoversi. Pierluigi, aveva sonno e non si fece domande sul perché la bettola si muoveva ed emetteva versi di gallina, chiuse gli occhi e riprese a dormire. Appena arrivarono in campagna, si fermarono di fronte all’orto. Scesero dal cavallo, il nonno disse Sta Iddina non è chiu bona a nenti. Non fa qiu ovae non è bona mancu più fari u brodu, c’amo a tagghiari a testa. 2 allora il nonno si girò verso Pierluigi e gli diede un coltello dal manico di legno molto affilato, l’aveva affilato la mattina con tanta cura. Pigghia stu cuteddu, cià fari nun tagghiu nte cannarozzi. Te ca. 3 Pierluigi aveva solo 9 anni era ingenuo e perplesso non capiva il perché di questa azione e non si spiegava perché doveva essere proprio lui a farla. Era molto impaurito dalla gallina. Il nonno la prese tenendola per le ali e tenendogli la testa e gli disse fozza tagghia! allora Pierluigi si avvicinò e avvicinò il coltello alla gola della galliana, la sfiorò, non appena vide il sangue, rabbrividì e si fermò. Il nonno cercò di riprendere il coltello per terminare la gallina. Ad un tratto la gallina gli sfuggì di mano e cominciò ad emettere versi smorzati. Si mise a correre sguazzando sangue di qua e di la. Il sangue schizzò su Pierluigi e sul nonno e sul prato, Sembrava una scena splatter come nei film di Tarantino. Cercarono di acchiappare la gallina ma gli sfuggì, dopo un pò la gallina si stancò e cominciò a barcollare qua e la. Il nonno in un balzo l’afferrò e con un colpo secco gli recise la gola, mentre lo fece, si sentì un ultimo stridulo coccodè che mise fine alle sofferenze della povera gallina. Allora la prese e la appese ad un palo vicino all’orto e disse ora voggiu a vidiri si tornunu staceddi da mala sorti! 4 tutta questa crudeltà per far scappare gli uccellini che in quei giorni mangiavano il raccolto. Il nonno aveva fatto uno spaventapasseri ma non era stato sufficiente. Ci teneva molto alla sua campagna aveva fatto tanti sacrifici e avrebbe fatto qualsiasi cosa per raccoglierne i frutti. Pierluigi era un po scandalizzato dalla scena a cui aveva assistito. Un attimo dopo, si mise al lavoro, doveva mbvirare 5, spostando la suca di rasola a rasola. Mise la suca sotto un grande albero di pesche e si sedette ad aspettare. Ad un tratto, gli calò la palpebra e si trovò di fronte le guardie che stavano salendo dalle scale principali. Allora Pierluigi, scappò verso l’altra uscita della terrazza ma un’altra guardia arrivò velocemente su dalle stesse scale. Una guardia disse ormai sei un trappola, arrenditi. Pierluigi, ci teneva a non essere catturato e avrebbe fatto di tutto, allora si sporse dalla terrazza mentre i due si avvicinavano. Fu lì che capì che poteva farcela, col cuore in gola e la guardia ad un soffio, flush si tuffó dalla terrazza. Durante il volo emise un gemito di follia, ammortizzó la caduta facendo delle capriole sul prato morbido che lo aiutò ad attenuare la caduta. Si chiuse sì tanto in se stesso che successe una cosa insolita, emesse una flatulenza e subito dopo sentì qualcosa che spinse nelle mutande proprio in prossimità del sedere. Non era paura ma causato dalla contrazione che fece il suo corpo. Era piccolo e non sentì alcun dolore, si alzò e continuò a correre fino a casa verso il bagno per finire ciò che era cominciato. Ad un tratto mentre si lavó le mani, sentì l’acqua, la sentì fino ai piedi si svegliò e vide una pozza enorme, si era scordato di spostare la suca e il pesco era pieno colmo d’acqua. Allora prese la suca e di corsa la sposto nella rasula sotto. Dopo un po' arrivo il nonno, Pierluigi era stanco e aveva fame chiese al nonno che ore sono? Il nonno alzò lo sguardo e guardò il sole e disse iè menzionnu ora ni calamu e annamu a manciari. Iai fami? Te docu manciati na pessica. 6 Pierluigi accenno un sospiro di sollievo, domandava spesso che ore erano al nonno, era affascinato da come sapesse sempre l’ora esatta senza l’uso dell’orologio. Il nonno preparò con le foglie dei fichi d’india ei limoni marci u pastrozzu 7 un secchio di mangime che diede alle pecore, andavano pazze per u pastrozzu. Il nonno prese il cavallo e si incamminarono verso casa. Mentre scendevano Pierluigi guardò la campagna ed in lontananza vide la gallina che stava lì, illuminata dal sole, riusciva a vedere chiazze di sangue rosso, qualche goccia che colava piano piano giù. Si sentiva un po' in colpa per la gallina, ma il pensiero svanì un attimo dopo arrivati a casa di fronte a una gran tavola bandita con tutta la famiglia lì pronti a pranzare. Bértula o bisaccia è un manufatto tessile comunemente diffuso su tutto il territorio regionale. Il suo uso ha origini antichissime ed è fortemente legata alla vita quotidiana. In ambito contadino, viene utilizzato per trasportare cibo come frutta, verdura o latte e uova. Oppure anche per utensili. ↩︎ Questa gallina non produce più niente. Non fa più le uova e non è più buona neanche per fare il brodo, dobbiamo tagliarle la testa. ↩︎ Prendi questo coltello e coltello, devi fare un taglio in gola. Tieni ↩︎ Ora voglio vedere se tornano ancora questi uccelli che portano solo guai ↩︎ Mbvirare annaffiare le piante, utilizzato sia con l’utilizzo della suca (canna, pompa) oppure anche tramite canalette a scorrimento d’acqua. ↩︎ E’ mezzogiorno, tra poco torniamo a casa e andiamo a mangiare. Hai Fame? Tieni mangiati una pesca. ↩︎ U pastrozzu composto di limoni e aranci, lasciati sotto un telone e sotto al sole, rimangono lì per giorni, settimane a macerare, in modo da comporre questa melma da un odore molto distinguibile e forte. Viene utilizzato come mangime per gli animali, ha ottimi valori nutrizionali. ↩︎
Din..Don..Din..Don.. Era Domenica mattina, le campane suonarono la prima volta alle 8 la mattina, il suono era molto forte e riecheggia in tutto il paese, quella domenica come tutte le domeniche a quell’ora quasi tutti dormivano compreso Gino. La vecchietta Concettina invece era sveglia da prima dell’alba, sprizzava di gioia e tanta energia, era il suo unico giorno di uscita. Come ogni domenica, Concettina, stava seguendo la sua routine domenicale, si stava preparando a festa! Concettina, dopo aver fatto una colazione molto abbondante, aveva bisogno di tante energie di domenica, si attinge ad effettuare tutte le sue preparazioni. Una doccia molto lunga per sentirsi ben profumata e sveglia, dopo si preparava i capelli, facendo tanti boccoli raccolti uno per uno per comporre come un mazzo di tulipani. Nonostante l’età Concettina aveva ancora tanti capelli, tutti neri. Faceva il colore pochissime volte. Il nero dei suoi capelli era sempre molto forte e acceso. Di domenica, usava legare i capelli con nastro di colore azzurro e rosa e ci faceva sopra un bel fiocco. La faccia tutta incipriata per coprire le occhiaie e un pò i solchi degli anni, pesanti rughe che nonostante la cipria, restavano comunque visibili. Sulle labbra, passava un rossetto, di un rosso ma di un rosso, molto forte che accendeva tutta la faccia e delineava un gran sorriso. Sorriso che aveva sempre stampato in faccia, un sorriso vissuto, che esprime molta serenità e bontà. Scorreva tutti i vestiti migliori, li guardava, li riguardava, li girava, ma alla fine sceglieva quasi sempre il suo vestito preferito. Un vestito bello a quadri nero bianco e grigio. Molto classico ed elegante. Ci attaccava sopra la sua spilla d’oro, spilla di un delfino che aveva ricevuto da sua mamma e a sua volta, sua mamma l’aveva ricevuta da sua Mamma Concetta. Aggiungeva, una collana di perle e due orecchini di perle per dare un tocco ancor più di eleganza. Prendeva la sua borsetta e la riempiva di tutto ciò che poteva essergli utile, un fazzoletto bianco, tutto ricamato da lei stessa, con le sue iniziali PC, un foulard nel caso facesse freschetto, un ventaglio nel caso facesse caldo e un ombrello nel caso piovesse. Pensava proprio a tutto e aveva tanto tempo per prepare tutto al minimo dettaglio. Una volta pronta, si posizionava nel suo posto preferito, posto in cui passava le mattine e i pomeriggi, in genere stava lì, seduta sul balcone a non fare nulla, guardare le macchine passare, i bambini giocare, le foglie cadere e il tempo passare. Invece oggi, aveva una cosa da fare. Stava lì seduta ad aspettare Petra. Petra era una ragazza giovane, figlia di Pippo, cugino di terzo grado di Concetta. Petra era molto affezionata a Concetta e ogni domenica, andava a prenderla a casa e l’accompagnava in chiesa, Concetta faceva fatica a camminare per i suoi dolori di artrosi dovuti all’età. Petra arrivò puntuale alle 10 be un ora prima dall’inizio della messa. Al suo arrivo di nuovo Din..Don..Din..Don.. le campane suonarono di nuovo a festa. Concetta abitava circa 150 mt di distanza dalla Chiesa, generalmente una persona giovane e in salute ci mette circa un minuto a percorrere questo tratto, invece Concetta e Petra uscivano almeno un ora prima, si la vecchietta Concetta aveva qualche acciacco ma non era per quello che uscivano così prima bensì perchè Concetta aveva tanto tempo a disposizione e quello era il suo giorno e voleva goderselo al massimo. Petra una volta arrivata, salutava Concetta. Buongiorno Zia Concetta! Ma comu ti parasti! Si troppu bedda stamatina! 1. Concetta ricambia con tanta gioia e un gran sorriso Buongiorno Petra! oggi, tu si a chiu bedda du paesi! Putemu fari a sfilata! 2. Petra aveva portato un fiore come tutte le domeniche, raccolto dal suo giardino. Si avvicinò a Concetta e lo mise sotto al fiocco e disse. Ora putemu nesciri si troppu bedda! 3. Allora prese la borsetta e insieme si avviarono piano piano verso le scale. Concetta si appoggiò a Petra da un lato e al corrimano in legno dall’altro lato. Faceva scivolare la mano sullo scorrimano, ma benché si fidasse molto di Petra, non si fidava delle sue gambe quindi, stringeva molto forte lo scorrimano che provocava uno stridio derivante dall’attrito della sua mano sudata che scivolava sullo scorrimano. Una volta finite le scale, Concetta si sedeva sul bisolo per fare una piccola pausa. Concetta una volta seduta disse Hai Hai! Chi bella iunnata i suli sta matina…ma Petra dimmi chi facisti a ieri sira? mi dissiru chi niscisti con cocca adunu. 4 Petra rispose ieri niscia cu Ginu. Ie un bravu figghiolo! allora subito Concetta ribatte: E cu ie Gino? I cui è figghiu? Petra ci penso un attimo, poi rispose: ie u figghiu i Mimma e i Pippu a bonanima sua. 5 Concetta subito curioso ribatte e chi facisti? cunta cunta…6 Allora concetta comincia: ma chi nì sai mi vinni a pigghiari fino a casa! ruvao tutto allicchittato supra a so vespa..Matri tuttu troppu beddu chi capiddi sistemati ie na camicia pulita… 7 Non finì di raccontare la storia che arrivò Mari Ciccia. Le tre si scambiarono affettuosi saluti e si incamminarono verso la messa. Appena partiti sentirono ancora Din..Don..Din..Don.. l’ultima ora, le campane suonavano ogni 15 minuti. Gino questa volta fece un sussulto sentendo le campane ma poi si girò dall’altra parte e continuò a dormire. Camminarono per ben 15 minuti tirati facendo qualche pausa qua e là e si ritrovarono in piazza. Una volta in piazza, camminarono ancora più piano in modo da farsi vedere da tutti. Si sedettero proprio nella panchina centrale della piazza, tutti le guardarono, loro salutarono e si misero a chiacchierare con tutti, quando ancora Din..Don..Din..Don… Ad un tratto, si avvicinò Cammela a cuttigghiara era sempre in cerca di qualcuno a cui raccontare i suoi curtigghiu ma soprattutto ascoltare altri curtigghi in modo da riempire il suo tempo, allenare la sua parlantina. Una volta lì davanti Buongiorno signore, ma come siamo belle questa mattina! le tre, ricambiarono il saluto. Carmela utilizzava sempre la stessa strategia, inizialmente buttava giù una serie di complimenti e ringraziamenti vari in modo da attrarre l’attenzione e fare sentire bene il suo pubblico, poi quando tutte pendevano dalle sue labbra partiva con la sua storia, inizia sempre dalla storia più in voga del momento, poi se vedeva esserci un ricambio allora andava giù più pesante fino agli scoop che manco i muri conoscevano. E fidatevi lei ne sapeva davvero tante, il suo lavoro era quello ed era assatanata dal saperne sempre di più, sapere tutto di tutti. Quel giorno esordì con la storia da Fubba (la furba). La Fubba, era una vecchietta che non si vedeva mai, stava sempre chiusa in casa e non si sapeva niente di lei, non si sapeva cosa facesse, come vivesse e se fosse ancora viva. Si parlava di lei spesso, era l’unica di cui nessuno aveva filtro. Si raccontavano tante storie brutte e belle ma la maggior parte di queste vedeva sempre la donna in maniera cattiva vincente e furba. Ieri a putia ntrasiu na signora, Gina (la proprietaria della putia), mi dissi…chi mai l’avia vista. Iera vecchia, ma strana, aviva nu stile tuttu particulari cu nu cappeddu a tipo chiddi chi si vidunu nta televisione e parrava italianu. Ma Italianu bonu, comu parrano chiddi chi studiano assai, sicuru chi fici magari i superiori. Na vota chi ntrasiu, vaddau tutti i cosi e ci fici 50 mila dumanni, e da ago e filo si cattao fino a attrezzi i travagghiu. Proprio strana sta vecchia. Si cattau si tanti cosi. Gina mi dissi chi manco nto misi migghiori fici tutto sto guadagnu. Appena nisciu Gina cuminciò a cantari e ballari da contentizza e chiamò subito so figghiu Filippò e u mannau mi ci va appresso pi capiri da unni nisciu sta vecchia. Allora Filippu ci cuntao chi si fici u giro i tutti l’attività du paisi. Cattò, tutto cattau, inchiu u carrello si cattò magari nautru carrelo e u ghinchì. Tutti aierie ieruno sbarruati i sta cosa e tutti i vindanti mi cuntaruno a stissa cosa chi era troppu stranuuu. Ma a sapiti na cosa? Filippu a nsucutau fino a casa e ndovinati aunnni ruvao…A casa da Fubba! Cumbinazioni ie fossi o ie proprio a Fubba? E cu sapi e cui a cunusci! mai nuddu a vidiu nta facci! 8 La storia era quasi finita quando fu interrotta dalle campane Din..Don..Din..Don..Din..Don..Din..Don.. che suonavano di più per segnalare che mancava solo l’ultima quarto d’ora all’inizio della messa. Allora le tre ringraziarono Carmela per la storia e si incamminarono verso la chiesa. Carmela non aveva tratto alcun guadagno da quello scambio allora si avvicinò e disse, u sapiti chi c’è, magari io a nari a missa oggi, vengu con vui così vi ncumpagnu fino a ghiesa. 9 In questo modo, nel tragitto poteva ascoltare le loro discussioni. Le tre lo sapevano, conoscevano bene Carmela, ma era inutile cercare di evitarla o non parlare. Allora si misero in marcia e ripresero a chiacchierare lo stesso però ogni tanto aggiungevano al discorso una fandonia in modo da rendere il lavoro di Carmela più difficile. Appena arrivarono sul ponte che regala una bella vista della grande chiesa del paese dall’alto, rimasero tutte e tre sbalordite, c’era un matrimonio e la piazza di fronte alla chiesa era tutta piena di persone belle, sorridenti e vestite a festa. Concettina allora divenne ancora più contenta, i suoi occhi si illuminarono e una lacrima di gioia scivolò giù per il viso. Anche Mari Ciccia lacrimò, ma le sue lacrime erano lacrime di dolore per suo marito, l’aveva perso da qualche mese e ancora il ricordo era molto accesso. Petra invece era contentissima, nella folla distinse gli sposi, e già si immaginò il suo futuro con Gino li in mezzo sorridenti. Carmela non era per niente sorpresa ovviamente già era a conoscenza del matrimonio e sapeva tutto sul matrimonio si trattava di una fuitina 10. Carmela era venuta apposta per vedere come le persone si erano vestite, studiare la gente e conoscere altre storie per dissetare la sua irrefrenabile voglia di conoscenza altrui. Buongiorno Zia Concetta! Ma comu ti sei vestita e sistemata bene! Sei troppo bella questa mattina! Your browser does not support the audio element. ↩︎ Buongiorno Petra! oggi, tu sei la più bella del paese! Insieme possiamo fare la sfilata di moda! Your browser does not support the audio element. ↩︎ Ora possiamo uscire, sei troppo bella! Your browser does not support the audio element. ↩︎ Hai Hai! Che bella giornata di sole questa mattina…ma Petra dimmi che hai fatto ieri sera? Mi hanno detto che sei uscita con qualcuno. Your browser does not support the audio element. ↩︎ E’ il figlio di Mimma e Pippo defunto e degno di rispetto. Your browser does not support the audio element. ↩︎ E che hai fatto? racconta… Your browser does not support the audio element. ↩︎ Non puoi capire, è venuto a prendermi fino a casa! e arrivato ed era tutto sistemato con la sua vespa bianca. Mi davvero troppo bello, con i capelli e la camicia perfetti… Your browser does not support the audio element. ↩︎ Ieri alla putia (negozio tipico siciliano dove in genere si vende qualsiasi cosa) è entrata una signora*, Gina (la proprietaria della putia), mi disse che non l’aveva mai vista. Era anziana e strana, aveva uno stile particolare, portava un cappello come quello che si vede alla televisione. Parlava in italiano molto bene. Come parlano le persone che studiano tanto, sicuramente avrà conseguito anche la scuola superiori. Una volta che entrò, ha guardato tutto il negozio e fece tante domande. Comprò tutto da ago e filo, fino agli attrezzi da lavoro. Ha comprato così tante cose che Gina, la proprietaria del negozio, neanche nel mese migliore guadagna così tanto. Appena uscita, Gina si mise a ballare e a cantare dalla contentezza e chiamò suo figlio Filippo e lo mandò a seguire la signora in modo da capire da dove proveniva. Filippo, raccontò che fece tutto il giro dei negozi del paese e comprò tantissime cose, Riempi il carrello e si comprò anche un altro carrello e lo riempì. Tutti i venditori erano sorpresi di questa cosa e mi raccontarono tutti la stessa cosa. Era troppo strano. Ma la sapete una cosa. Filippo la seguì fino a casa e indovinare dove andò….A casa della Furba! E’ una combinazione oppure è proprio lei? E chi lo sa? Chi la conosce! Mai nessuno l’ha vista in faccia.* Your browser does not support the audio element. ↩︎ Lo sapete che c’è, anche io devo andare a messa oggi, vengo con voi così vi accompagno. Your browser does not support the audio element. ↩︎ Fuitina: fuga repentina che identifica l’allontanamento di una coppia di giovani aspiranti coniugi dai rispettivi nuclei familiari di appartenenza, allo scopo di rendere esplicita (o far presumere) l’avvenuta consumazione di un atto sessuale completo. ↩︎
Gino apri gli occhi, quella mattina si svegliò un po’ turbato di ciò che era avvenuto la sera prima ma soprattutto con un brutto pensiero, forse aveva fatto un incubo. Sollevò lo sguardo, guardò l’orologio e la sua faccia si scurì immediatamente. In un attimo aveva realizzato che era giovedì e doveva fare una commissione. Non aveva la minima voglia di fare questa commissione e rabbrividì al solo pensiero. Si girò d’altro lato sperando di addormentarsi nuovamente e di svegliarsi il giorno dopo. Chiuse a malapena una palpebra quando sentì una voce dal profondo e non era un incubo, purtroppo, era sua mamma che urlava: Gino svigghiati chi iè taddu. Ti ricoddi chi m’ha spicciari da cosa a posta? U sai chi io non ci pozz’ annari! 1 Quindi, era tutto vero. Sua mamma aveva bisogno di lui, ma la commissione alla posta Gino non l’aveva mai mandata giù, la faceva controvoglia e lo metteva di malumore. Allora risponde alla mamma: mamma fammi dommiri, fammi dommiri autri 10 minuti! Poi mi siddiu e sugnu stancu e u sai che na suppottu Naiu Valia! 2 la mamma gli urlò subito contro insistentemente fin tanto che Gino decise di alzarsi. Scese le scale con i capelli e la faccia stropicciata, prese il caffè e lì accanto trovò le carte, carte che doveva portare alla posta. Le guardò, poi si mise a giocare con la tazzina di caffè mettendola vicino agli occhi in modo da fare sparire le carte dal suo sguardo. Alla fine si rassegnò, si alzò, prese le carte e uscì. Attraversò il vialetto e si ritrovò di fronte alla piazza del paese. In piazza a distanza vide due donne, già da lontano riconobbe Cammela a cuttighiara 3 che parlava con una certa veemenza che la caratterizzava. Stava raccontando alle vicine la disavventura proprio di Gino del giorno prima “Scinni Papà”. Carmela non appena lo vide arrivare, cambiò subito discorso, tanto sapeva tutto di tutti e aveva tanti cuttigghi a disposizione da raccontare. Gino si sentì un po’ osservato mentre passava e percepì in sottofondo un po’ di sghignazzi. Una volta in piazza, vide Don Gianni che stava lì seduto ad aspettare che il tempo passasse. Don Gianni salutò Gino con molto rispetto e un bel sorriso affettuoso, erano molto amici i due. Gino ricambiò. Poco più avanti Ciccio invece gli disse: Ma tò patri a unni u lassasti? Aoggi nun scinniu? 4 chiaramente una provocazione, una presa in giro poco velata da parte di Ciccio. A quel punto Gino capì che si riferiva al giorno precedente e allora rispose a tono: Ciccio Birra ma picchì non ti bivi na birra e ti cucchi? 5 Proseguì però senza fermarsi, non voleva problemi quella mattina, già ne aveva tanti, la sua missione principale era la commissione in posta. Superata la piazza, si ritrova di fronte alla posta. Già in lontananza poteva sentire la puzza di sigaretta misto profumo d’arancia e di cocco di Naiu Valia e già gli veniva un senso di vomito. Non appena arrivò all’ingresso la vide. Naiu Valia era lì, sicca sicca, con i capelli scuri, la faccia scura, vestita di nero, come sempre, a non fare nulla. Aveva quello sguardo rilassato ma anche minaccioso e incavolato. Allora Gino entra in silenzio e lei subito a tono: cu non mori si rividi! Ma cu ti porta? C’ha fari? 6 Gino rispose dicendo che doveva fare due raccomandate, prendere un pacchetto…. non lo fece neanche finire di parlare che rispose Ma chi ti sbigghiasti ora? tutto oggi a fari? iè tardu, su i 10, no sai chi io a fari a pausa? Si voi mi poi spettari ca, poi quannu tornu videmu chi putemu fari. 7 Gino non aveva voglia di problemi quella mattina allora rispose che andava bene e si sedette. Aspettò lì seduto sino alle 11 ma niente, di Naiu Valia nessuna traccia. Allora si fece una passeggiata e andò a bere un po’ d’acqua al lavatoio (posto utilizzato una volta per lavare i panni). Una volta ritornato vide due vecchiette dentro che dovevano ritirare la pensione e Naiu Valia che era appena tornata. Gino allora si avvicinò e Naiu Valia lo richiamò subito Gino ma che fai? non vedi che c’è gente. Devi aspettare il tuo turno e prendi il numerino. Mica siamo al mercato qui! allora Gino stava per innervosirsi ma sapeva che non era il caso e non poteva perchè se tornava a casa senza aver sbrigato le commissioni poi sentila la mamma. Allora disse: va bene aspetto e prese il numerino. Naiu Valia, cominciò a parlare con le vecchiette di tutto, sembrava di essere davvero al mercato. Cominciarono dal l’uncinetto per finire con le ricette di cucina siciliane. Gino non ne potè più, era stanco e si addormentò. Non appena aprì gli occhi vide che non c’era più nessuno ed era finalmente arrivato il suo turno. Allora si avvicinò al bancone, lì di fronte a Naiu Valia, lì pronto con tutte le sue carte e le sue cose da sbrigare, dice: è il mio turno adesso giusto? Naiu valia rispose: Ma beddu chi ti sbigghiasti ora! Non vidi chi ie menzionnu. Io annari a manciari. Tranquillu….rilassati i poi fari dumani! tantu ca fari? 8 Se siete arrivati a leggere fin qui avete capito perchè l’addetta della poste aveva come soprannome Naiu Valia. Questa storia ovviamente è un’ esasperazione. Non vuole essere provocatoria verso gli uffici postali né verso gli addetti postali né contro il sud o il nord, è solo una storia, prendetela così com’è e state tranquilli e rilassatevi! Traduzioni e audio: Gino svegliati che è tardi, ti ricordi che mi devi sbrigare quelle commissioni all’ufficio postale? Lo sai che io non posso andarci! Your browser does not support the audio element. ↩︎ Mamma fammi dormire! fammi dormire altri 10 minuti! Poi mi scoccio e sono stanco e lo sai che non sopporto l’addetta della posta! Your browser does not support the audio element. ↩︎ cuttighiara persona che ama sparlare di altre persone Your browser does not support the audio element. ↩︎ *Ma dove hai lasciato tuo papà? Oggi non è sceso con te? *Your browser does not support the audio element. ↩︎ Francesco detto Ciccio Birra perchè non ti bevi una birra e vai a letto? Your browser does not support the audio element. ↩︎ Chi non muore si rivede! Ma chi ti ha consigliato di venire? Che cosa devi fare? Your browser does not support the audio element. ↩︎ Ma ti sei svegliato ora? Devi fare tutto oggi? è tardi, sono le 10, non lo sai che io alle 10 devo faccio la pausa? Se vuoi puoi aspettarmi qui. Quando trono vediamo che cosa riusciamo a sbrigare. Your browser does not support the audio element. ↩︎ Ma bello, ti sei appena svegliato! Non vedi che è mezzogiorno. Io devo andare a pranzare. Stai tranquillo e rilassati, le puoi sbrigare domani le commissioni. Tanto che cosa hai da fare? Your browser does not support the audio element. ↩︎
Scendi papà! disse Gino seduto sulla sua vespa una volta arrivato nella piazza centrale del paese. Non sentendolo scendere allora ripeté dai..scendi papà! Ma niente, allora una terza volta scendi pa.. lo ripetè girandosi, ma il papà non c’era! Allora cominciò ad impallidire e divenne bianco, un tutt’uno con il bianco della sua tanto amata Vespa. Sfortunatamente per Gino quel giorno era la festa del paese di Sant’Antonio e la piazza era piena colma di gente. Tutti, in piazza, si girarono a guardare la scena e cominciarono a ridere. Gino dal bianco divenne rosso, poi girò la vespa era molto preoccupato per il papà allora tornó indietro alla sua ricerca. Dal paese scese giù in velocità cercando di guardare in tutte le direzioni, era agitato e man mano che scendeva e non lo trovava si agitava sempre di più. Una volta arrivato al paese adiacente vide un vecchietto seduto a bordo strada su una sedia bianca, in silenzio che osservava. Gino si avvicinò e gli chiese se aveva visto suo papà. Lui rispose 153, 24, 7 e 1. Gino non capì allora proseguì per la strada e dopo poco, finalmente trovo tanta gente, tutti attorno a soccorrere il papà. Hai hai! Urlava il padre. Chi figghiu sciaguratu chi iaiu! Nenti vali! mancu a Vespa sapi puttari! Gino quella mattina aveva commesso due grandi errori. Quello di aver fatto cadere il padre dalla vespa senza accorgersene, ma anche di essersene accorto troppo tardi e davanti a troppa gente. Il secondo fu il problema più grosso, che lo segnó a vita. Da quel momento la sua storia tramite il cuttigghio, fu raccontata a tutti in paese poi nel paese vicino e così via in tutta la città. Fintanto che tutti non arrivarono a conoscere la disavventura di Gino. Fu un attimo che Gino fu soprannominato “scinni papà!” Fortunatamente Gino, non andava troppo veloce con la Vespa allora suo papà, se la cavò con solo qualche piccola ammaccatura e un’abrasione lungo tutto il braccio. Gino non se n’era accorto poichè quella mattina era sommerso di pensieri, pensava alle commissioni che lo attendevano l’indomani. Ma alla fine, cosa voleva dire il vecchietto saggio a bordo strada? Era lì da tutta la mattina e aveva visto passare 153 macchine, 24 moto ape, 7 motorini e 1 solo stupido, si è fermato dopo essere passato più volte con e senza papà. Traduzioni: Chi figghiu sciaguratu chi iaiu! Nenti vali! mancu a Vespa sapi puttari! Che figlio sciagurato che ho! non vale nulla! non sa neanche guidare la sua vespa! cuttigghio sparlare di altre persone
Si cominciava ad intravedere qualche raggio di sole, che splendeva sui ciottoli bianchi della spiaggia. I piccoli vetrini creavano un riflesso ancora più forte, quasi abbagliante. In lontananza si intravedevano delle barchette che dondolavano in mare. Quella mattina l’acqua era molto calma, piano piano le piccole barchette si avvicinavano verso la riva e divennero pescherecci di media grandezza. Sul lungomare, una coda di lape (moto ape) in attesa. Gino sta ancora dormendo, ieri ha fatto consegne fino a tardi. Oggi u mari ie chinu i pisci! 1 si sente urlare in lontananza. La prima barchetta di un anziano pescatore attracca, un pescivendolo si avvicina e con il suo garzone comincia a scaricare cassette piene di pesce. Le trasportano sulla loro ape, di colore giallo sbiadito, ne aveva fatta di strada e ne aveva preso di sole quell’ape. Riempiono il cassone con le cassette piene di pesce fresco e ci misero sopra pezzi di ghiaccio per mantenerlo. Chi bellu pisci, pisci friscu!! pisci spada, sciabulata, aguglia, curstadelle io iaiu! 2 sentì in lontananza Gino, stava ancora dormendo e voleva tanto continuare a dormire ma oggi era martedì e come tutti i martedì puntuale passava Francu u pisciaru. Mu fai un chilu i curstadelle!! 3 sentì Gino urlare dalla casa accanto. il pescivendolo si ferma, scende e si avvicina alla bilancia, bilancia bellissima, enorme, con due piatti e un bilanciere dorato. Mette due pesi da mezzo chilo sopra un piatto della bilancia, sull’altro, mette un foglio e ci butta sopra a pugni le costardelle fintanto che i due vassoi non siano in equilibrio. Signura c’ha regalu! sulu sta junnata 5 euru o chilo! 4 Sempre urlando, in modo che anche le massaie vicine potessero sentire. Un cestino di vimini (u panaru) cala giù con una carrucola dal balcone. Il garzone prende i soldi dal cestino e ci mette le corstadelle, ci aggiunge due limoni e un pò di prezzemolo. Fortunatamente per Gino, quella mattina nessun altro tra i suoi vicini rispose all’offerta di Francu. Allora sentì il forte rumore dell’ape che piano, piano si allontanava. Il rumore stava per terminare e finalmente Gino avrebbe potuto riprendere a dormire. Banane, mulinciane, pumadoru, kiwi io iaio. Culi voli l’ovaaa! 5 il sonno di Gino fu presto interrotto da Pippo u viduraru. Viniti,viniti,accattati,accattati, tutta roba frisca iè! 6 Alla fine Gino come tutti i martedì, decise di alzarsi, anche se ancora presto e ancora assonnato, spalancò la finestra, un forte sole e tanto calore illuminò la sua giornata. Audio Siciliani Oggi u mari ie chinu i pisci! Your browser does not support the audio element. ↩︎ Chi bellu pisci, pisci friscu!! pisci spada, sciabulata, aguglia, curstadelle io iaiu! Your browser does not support the audio element. ↩︎ Mu fai un chilu i curstadelle!! Your browser does not support the audio element. ↩︎ Signura c’ha regalu! sulu sta junnata 5 euru o chilo! Your browser does not support the audio element. ↩︎ Banane, mulinciane, pumadoru, kiwi io iaio. Culi voli l’ovaaa! Your browser does not support the audio element. ↩︎ Viniti,viniti,accattati,accattati, tutta roba frisca iè! Your browser does not support the audio element. ↩︎
Scendi papà! disse Gino seduto sulla sua vespa una volta arrivato nella piazza centrale del paese. Non sentendolo scendere allora ripeté dai..scendi papà! Ma niente, allora una terza volta scendi pa.. lo ripetè girandosi, ma il papà non c’era! Allora cominciò ad impallidire e divenne bianco, un tutt’uno con il bianco della sua tanto amata Vespa. Sfortunatamente per Gino quel giorno era la festa del paese di Sant’Antonio e la piazza era piena colma di gente. Tutti, in piazza, si girarono a guardare la scena e cominciarono a ridere. Gino dal bianco divenne rosso, poi girò la vespa era molto preoccupato per il papà allora tornó indietro alla sua ricerca. Dal paese scese giù in velocità cercando di guardare in tutte le direzioni, era agitato e man mano che scendeva e non lo trovava si agitava sempre di più. Una volta arrivato al paese adiacente vide un vecchietto seduto a bordo strada su una sedia bianca, in silenzio che osservava. Gino si avvicinò e gli chiese se aveva visto suo papà. Lui rispose 153, 24, 7 e 1. Gino non capì allora proseguì per la strada e dopo poco, finalmente trovo tanta gente, tutti attorno a soccorrere il papà. Hai hai! Urlava il padre. Chi figghiu sciaguratu chi iaiu! Nenti vali! mancu a Vespa sapi puttari! Gino quella mattina aveva commesso due grandi errori. Quello di aver fatto cadere il padre dalla vespa senza accorgersene, ma anche di essersene accorto troppo tardi e davanti a troppa gente. Il secondo fu il problema più grosso, che lo segnó a vita. Da quel momento la sua storia tramite il cuttigghio, fu raccontata a tutti in paese poi nel paese vicino e così via in tutta la città. Fintanto che tutti non arrivarono a conoscere la disavventura di Gino. Fu un attimo che Gino fu soprannominato “scinni papà!” Fortunatamente Gino, non andava troppo veloce con la Vespa allora suo papà, se la cavò con solo qualche piccola ammaccatura e un’abrasione lungo tutto il braccio. Gino non se n’era accorto poichè quella mattina era sommerso di pensieri, pensava alle commissioni che lo attendevano l’indomani. Ma alla fine, cosa voleva dire il vecchietto saggio a bordo strada? Era lì da tutta la mattina e aveva visto passare 153 macchine, 24 moto ape, 7 motorini e 1 solo stupido, si è fermato dopo essere passato più volte con e senza papà. Traduzioni: Chi figghiu sciaguratu chi iaiu! Nenti vali! mancu a Vespa sapi puttari! Che figlio sciagurato che ho! non vale nulla! non sa neanche guidare la sua vespa! cuttigghio sparlare di altre persone
Ricerca Eccoci qua finalmente! In questi giorni ho sentito la necessità di creare questo blog che state proprio visitando! Mentre navigavo on line, sono venuto a conoscenza degli static site generator; ho sempre saputo della loro esistenza ma non avevo mai dedicato loro molta importanza. Tuttavia, navigando più a fondo, sono stato attratto da Hugo, uno fra i tanti nuovi motori per generare website statici. No Wordpres Partivo dall’esigenza di avere qualcosa di statico in quanto non dispongo di un server dove installare Wordpress o altri CMS e, per un piccolo blog personale, non credo sia necessario e oltretutto è troppo costoso. Infine, volevo che il blog fosse efficiente e scalabile e cross platform. DRY La prima cosa a cui ho pensato da programmatore è stata: quante operazioni dovrò fare per eseguire un deploy senza un CMS? come farò a portare i miei nuovi contenuti online? Sono anche un’amante di Google Docs e mi piace scrivere ed avere tutto in un unico posto. Allora mi sono chiesto: come posso unire Hugo e Google Docs senza dover girare mille manovelle? Innanzitutto, ho studiato la struttura dei contenuti MarkDown e mi sono messo alla ricerca di qualcosa che mi desse la possibilità di convertire i Documenti Google in MarkDown. La cosa più utile che ho trovato sul web è stato questo Google script mangini/gdocs2md: Convert a Google Drive Document to the Markdown format, suitable for publishing. a cui faccio i miei più sentiti ringraziamenti. Nonostante la comprovata efficacia del suddetto script, avevo bisogno di qualcosa in più rispetto al ricevere la conversione di un articolo via mail. Di conseguenza, per avere qualcosa di più automatizzato, ho modificato un pò lo script e creato il link che vedi sotto: Link allo script su github Lo script converte tutti i documenti elencati in un foglio Google, organizza in cartelle per categorie, estrae immagini da articoli, aggiunge l’intestazione del MarkDown, i tags ed infine crea un file zip contenente tutti i contenuti. Ho deciso di impostare lo zip con visibilità pubblica quindi scaricabile, pronto per il deploy. Ho successivamente creato una sorta di database utilizzando uno sheet di Google come se fosse una tabella con la seguente struttura: ToDeploy Title Summary SummaryImage Category Data tag1 tag2 tag3 tag4 tag5 Language 1 Come Inviare File PDF e articoli web al Kindle da Android 0 Kindle 2020-05-09 kindle pdf articolo web send to kindle android it In questo modo ho la possibilità di fare il deploy di tutto o di aggiornare solo ciò che ho modificato utilizzando il primo campo ToDeploy. Successivamente, ho creato un altro google script che riceve in ingresso l’id della cartella su cui è presente lo zip precedentemente creato e restituisce l’url pubblico da cui effettuare il download dello zip con i contenuti. function doGet(e) { params = JSON.parse(JSON.stringify(e)); var docid = params.parameters.docid; if(docid==null || docid=="") return; var parentFolder=DriveApp.getFolderById(docid); if(parentFolder==null) return; var url = parentFolder.getFilesByName("BlogProd.zip").next().getDownloadUrl(); return ContentService.createTextOutput(url); } Infine ho messo tutte insieme all’interno del seguente script linux sh in modo da poter automatizzare build e il deploy: #!/bin/bash #ln -s /home/yourname/works/luciosoftsite/blog/ ~/luciosoftsite #ln -s /home/yourname/works/luciosoftsite/blog/content/ ~/luciosoftblogcontent #requirements wget|unzip|hugo folderid="your google doc folder id” yourscriptid=”your google script id” cd ~/luciosoftblogcontent; retriveurl="https://script.google.com/macros/s/$scriptid/exec?folderid=$folderid”; downlodUrl=$(wget $retriveurl -q -O -); fileName="download.zip"; wget -c $downlodUrl -O $fileName; unzip -o $fileName; rm -f $fileName; cd ~/luciosoftblog; #hugo serve -D HUGO_ENV="production" hugo --config config.yaml firebase deploy Lo script è molto semplice, non fa altro che richiamare lo script google per recuperare l’url di download. Una volta recuperato l’url, a questo punto lo script effettua il download dello zip, estrae i contenuti all’interno del folder dei contenuti di Hugo, esegue il comando per creare Build, ed infine richiama firebase per effettuare il deploy. E anche oggi la magia è avvenuta. Sì, probabilmente avrei potuto seguire mille altre strade, anche più sicure e più efficienti ma questo è un inizio, il mio obiettivo era più grande. Vorrei trasformare lo script linux in un container docker e lo script google in un’applicazione dinamica con login google. Ma questa è un’altra storia che, se avrà seguito, vedrete prossimamente in un altro link all’interno del mio blog :) Poject ghithub link
Research In these days I have felt the need to create the blog you are reading. While surfing the web, I became aware of the static site generators, I have always known what they are and that they have always existed but I had never given them much attention. Though, while surfing deeper, I was attracted by Hugo, one of the many new engines to generate static websites. No WordPress I needed to have something static for I don’t have a server where I can install Wordpress or other CMS and, for a small personal blog, I don’t think it’s necessary and it is also too expensive. Finally I wanted the blog to be efficient and scalable and cross platform. DRY The first thing I thought about as a programmer was: how many operations do I need to do to deploy without a CMS? how will I bring my new content online? I am also a Google Docs lover and I like writing and having everything in one place. So I asked to myself: how do I combine Hugo and Google Docs without having to turn a thousand cranks? I studied the structure of MarkDown content and I started looking for something to convert Google Docs to MarkDown. The only useful thing I found on the web was this Googlescript mangini/ gdocs2md: Convert to Google Drive Document to the Markdown format, suitable for publishing. to whom I offer my heartfelt thanks. Although this script is very effective, I needed something more than receiving the conversion of an article by email. To have something more automated, I modified the script a bit and created the link script as follows: Link to my github repos The script converts all the documents listed in a Google sheet, organizes into folders by categories, extracts images from articles, adds the MarkDown header, tags and finally creates a zip file containing all the contents. I decided to make the zip public and therefore downloadable, ready for deployment. I then created a sort of database using a Google sheet as a table with this struct: ToDeploy Title Summary SummaryImage Category Date tag1 tag2 tag3 Tag4 Tag5 Language 1 Send As PDF files and web articles to the Kindle from Android 0 Kindle 09/05/2020 kindle pdf web article send to kindle android it In this way I have the possibility to deploy everything or to update only what I modified using the first field ToDeploy field. Subsequently, I created another google script that receives the id of the folder where there is the previously created zip. The script finally returns the public url from which it is possible to download the zip with the contents. function doGet (e) { params = JSON.parse (JSON.stringify (e)); var docid = params.parameters.docid; if (docid == null || docid == "") return; var parentFolder = DriveApp.getFolderById (docid); if (parentFolder == null) return; var url = parentFolder.getFilesByName ("BlogProd.zip"). next (). getDownloadUrl (); return ContentService.createTextOutput (url); } Finally I put them all together in the following script linux sh. So I can automate building and deployment: #!/bin/bash #ln -s /home/yourname/works/luciosoftsite/blog/ ~/luciosoftsite #ln -s /home/yourname/works/luciosoftsite/blog/content/ ~/luciosoftblogcontent #requirements wget|unzip|hugo folderid=”your google doc folder id” yourscriptid=”your google script id” cd ~/luciosoftblogcontent; retriveurl="https://script.google.com/macros/s/$scriptid/exec?folderid=$folderid”; downlodUrl=$(wget $retriveurl -q -O -); fileName="download.zip"; wget -c $downlodUrl -O $fileName; unzip -o $fileName; rm -f $fileName; cd ~/luciosoftblog; #hugo serve -D HUGO_ENV="production" hugo --config config.yaml firebase deploy The script is very simple, it calls the google script to retrieve the download url. Once the url is recovered, the script does the download of the zip, extracts the contents into the Hugo contents folder, executes the command to create Build, and finally calls firebase for deploy. And even today the magic has happened. Yes, I could have followed a thousand other ways, even safer and more efficient. But this is a starting point, my goal is bigger. I would like to turn the linux script into a docker container and the google script into a dynamic application with google login. But this is another story that, maybe, you will be able to see in another link at my blog :) Link al progetto ghithub
Noi programmatori abbiamo diverse competenze e tanti pattern che utilizziamo, ma credo che la cosa che maturiamo nel tempo e ci accomuna di più è l’ottimizzazione delle risorse per risparmiare tempo. Tutti sanno che il tempo è la cosa più importante! Lo dice lo stesso modo di dire: il tempo è denaro. Il detto si basa su tempo umano. Il tempo umano deve essere utilizzato come valore aggiunto per la creatività, invenzione, innovazione costruzione e implementazione. Noi programmatori siamo fortunati, abbiamo a disposizione un’altra unità di misura del tempo, oggigiorno tendente quasi all’infinito, che possiamo e dobbiamo sfruttare al massimo e questo tempo e il tempo macchina. Un tempo si facevano fatigare gli schiavi poi i cavalli, i muli, i motori oggi ci siamo evoluti, culturalmente e mentalmente, Gli schiavi di quest’era sono le macchine. Per macchine intendo i nostri pc, o meglio ancora i nostri server, il cloud o i robot. Ho deciso di scrivere questo articolo per riportare delle tecniche utili per efficientare il nostro lavoro e di conseguenza ottenere più tempo. La mia tecnica principale da utilizzare per ottenere questo risultato è: appuntare gli step manuali che compiamo per fare un compito ricercare possibili soluzioni alternative riflettere su come possiamo automatizzare ogni singolo step mettere tutto assieme ripartire dal punto 1 se il nostro tempo d’impiego non è ancora = 0 per l’attività scelta Un esempio Spesso per velocizzare il nostro lavoro scriviamo degli script, questi script sono necessari altrimenti dovremmo scrivere n istruzioni di comandi. Giornalmente o mensilmente o dopo aver fatto qualcos’altro, dobbiamo eseguirli. Quindi apriamo il terminale e lanciamo lo/gli script. Per evitare di lanciare lo script a mano, che implica ricordarci, trovare lo script e lanciarlo, possiamo procedere nel seguente modo: se si tratta di qualcosa che va eseguito con una schedulazione fissa possiamo schedularlo utilizzando uno scheduler in linux possiamo fare un cron oppure possiamo utilizzare atrigger per eseguire la schedulazione nel caso in cui il nostro script sia su un server a cui non abbiamo accesso alla schedulazione Se invece non c’è una schedulazione fissa e dobbiamo eseguirlo al bisogno Possiamo utilizzare una scorciatoia da tastiera. In linux ma anche su Windows e molto semplice settare una una scorciatoia. Nel mio caso usando Ubuntu basta andare sotto *impostazioni *-> scorciatoie da tastiera e aggiungiamo il nostro nuovo parametro che conterà il path al nostro script e la scorciatoia desiderata. Come potete vedere nella seguente immagine, io lo uso ad esempio per lanciare in debug il mio blog e successivamente per fare il deploy: per quanto questo possa sembrare bello e può aiutarci a renderci più efficienti, con due tasti, il gioco è fatto, si può sempre ottimizzare. Voi direte, che altro puoi fare per ottimizzare questo? e perchè? Il perchè è semplice, per poterlo eseguire ho necessariamente bisogno di avere la mia macchina a disposizione e accesa. Quindi sarebbe meglio laddove possibile utilizzare sempre un server o il cloud in modo da fare tutto da remoto in modo così da poter avere più controllo e soprattutto non dipendere da una macchina. Oggi ci sono tanti strumenti che abbiamo a disposizione per eseguire script o creare piccole pagine web o piccole applicazioni o siti vetrina. Il piano gratuito di questi servizi, mette a disposizione tanti strumenti fra i quali: Spazio hosting. Spazio database. Funzioni per micro servizi Container Macchine virtuali e chi più ne ha più ne metta C’è ne sono così tanti che a volte tendiamo a non vederli, nonostante siano sotto il nostro naso. Quelli che utilizzo io maggiormente, sono: Firebase Google Cloud Amazon AWS Google Script L’ultimo servizio a differenza degli altri non è un servizio cloud ma semplicemente un motore di funzioni con schedulazione o attivazione in funzione dei servizi Google. Sono molto integrate con il nostro account Google e se lo usiamo come mail principale ci darà davvero delle grandi potenzialità per automatizzare: invii di mail sincronizzazione di dati editing di documenti chiamare API esterne memorizzare dati in excel ecc ecc… è brutto dire ecc ecc ma davvero, l’elenco sarebbe troppo lungo, vi permette di automatizzare tutto ciò che è Google. Gli altri tre sono i primi tre migliori provider Cloud. Per cominciare io consiglio di usare Firebase perchè mette a disposizione davvero tanti servizi e se si tratta di primo inzio con il cloud, l’accesso con Firebase, credo sia più semplice e con una linea di apprendimento più rapida. Conclusioni Cerchiamo di fare più attenzione a quello che ogni facciamo senza pensare. Se ogni giorno facciamo una cosa ripetitiva, anche se ci mettiamo davvero poco a farla, teniamola in considerazione e pensiamoci. Dopo anni le cose ripetitive possono diventare tante e rischiamo di saturare tutto il nostro tempo col fare solo cose ripetitive. Vi lascio e vi saluto con una frase che mi è venuta adesso. Probabilmente non ha senso o forse è già di qualcuno ma io non lo so. Meglio creare un sistema che ci risolve un problema che risolvere il problema stesso!
We as programmers have different skills and we use lot of patterns, but I think the thing that we mature over time and we have more in common is the resources optimization for time saving. Everyone knows that time is the most important thing! The same way of saying it: time is money. The saying is based on human time. Human time must be used as an additional value for creativity, invention, construction innovation and implementation. We programmers are lucky, we have another measurement time unit, nowadays almost tending to infinity. This is machine time and we have to make the most of it. Once we used slaves, then horses, mules, engines motor. Today we have evolved, culturally and mentally. The slaves of this era are machines. By machines I mean our PCs, or better yet our servers, the cloud or robots. I decided to write this article to report useful techniques to make our work more efficient and consequently have more time. My main technique to be used to obtain this result is: write down the manual steps that we perform to do a task search for possible alternative solutions reflect on how we can automate each single step put everything together starting from point 1 if our time for the task is not equal to 0 An example Often to speed up our work we write scripts, these scripts are necessary otherwise we would have to write n command statements. Daily or monthly or after doing something else, we have to do it. So let’s open the terminal and run the scripts. To avoid launching manual the script, which implies remembering, finding the script and launching it, we can proceed in the following way: if it is something that must be done with a fixed date Schedule it using a scheduler With linux, we can make a cron We can use atrigger to execute the scheduling in the event that our script is on a server to which we do not have access to server side If instead there is no fixed scheduling date and we have to execute it as needed We can use a keyboard shortcut. In linux but also on Windows it is very simple to set a shortcut. In my case using Ubuntu just go under *settings *-> keyboard shortcuts and add our new parameter that will count the path to our script and the desired shortcut. As you can see in the following image, I use it for example to debug my blog and then to deploy: as beautiful as it may seem and can help us make us more efficient, with two buttons, you’re done, yes can always optimize. Will you say, what else you can do to optimize this? and why? The reason is simple, in order to run it I necessarily need to have my machine available and turned on. So it would be better where you can always use a server or the cloud in order to do everything remotely so that you can have more control and above all not depend on a local machine. Today there are many tools available for us to run scripts or create small web pages or small applications or showcase sites, the free plan of these services, provides many tools including: Hosting space Database space Functions for microservices Container Virtual machines and whoever has more put them There are so many that sometimes we tend not to see them, despite being under our noses. The ones I use most are: Firebase Google Cloud Amazon AWS Google Script The last service unlike the others is not a cloud service but simply an engine of functions that can be scheduled or activated in relation on google service. They are very integrated with our Google account and it will really give great potential to automate: mailing data synchronization document editing calling external API storing data in excel etc etc … it’s bad to say etc etc but really, the list would be too long, it allows you to automate everything which is Google. The other three are the top three best cloud providers. To begin with I recommend using Firebase because it offers so many services and it is simler and with a faster learning line. Conclusions We have to pay more attention to what we all do without thinking on it. If we do something repetitive every day, even if we take very little time to do it, let’s take it into consideration and think about it. After years, repetitive things can become many and we risk saturating all our time by doing only repetitive things. I leave you and greet you with a phrase that has come to me now. It probably doesn’t make sense or maybe it’s already of someone’s but I don’t know. It is better to create a system that solves a problem than to solve the problem itself!
Giuseppe te ca 1 euro, vammi a cattari na buttigghia i pommarola chi stamatina ma scuddai ma pigghiu a putia. Cu restu a cattiti chiddu chi voi, basta chi non ti catti vularie. Si proprio te na cattari, a cattiti i carruba chi su boni. 1 Questa frase sembra molto normale oggi, va be in sicilia ovviamente, ma un un tempo, non molto lontano, quando la globalizzazione, la tecnologia e commercializzazione dei prodotti non era così forte come ai giorni odierni, la frase non reggeva. Non tutti avevano la possibilità di comprare una bottiglia di pomodoro. E allora come si faceva? non si mangiava la famosissima e ricorrente pasta alla norma? Magari all’epoca non era così ricorrente era un piatto della domenica. No non se ne faceva a meno e per non farlo mancare a tavola, quasi tutti si adoperavano a fare le bottiglie in casa, in garage oppure in giardino. Giuseppe era molto ubbidiente e tornò a casa con la passata e le carrube. La passata che diede alla mamma, gli ricordò la storia di buttugghi i pummorola allora disse alla mamma, se poteva raccontarle la storia di quando lei faceva le bottiglie in casa. La mamma dapprima stentò a raccontarla, aveva già raccontato la storia delle bottiglie si tante volte che non aveva voglia, ma davanti alla faccia del figlio Giuseppe che esprimeva in uno sguardo “dai dai mamma ti prego raccontamela per favore..lo sai che mi piace tanto..” Allora la mamma non ne può fare a meno e comincia la storia. La preparazione delle bottiglie di pomodoro era una ricorrenza annuale, era una cosa bellissima e per noi tutti nella famiglia era come se fosse una festa. La notte precedente alla preparazione delle bottiglie di pomodoro, non riuscivo a pigliare sonno per l’agitazione e per la voglia di fare le bottiglie. La mattina prestissimo, prima ancora dell’alba, mi posizionai li in terrazza ad aspettare il rientro del nonno. Quel momento lo ricordo sempre, mi luccicavano gli occhi a vedere la lapa i Puddicinu arrivare carica di pomodori, tutti rossi ma rossi, rossi belli tutti luccicanti. Non so se perchè ero piccina oppure perché i pomodori erano i casa 2, riuscivo a percepire l’odore già da quella distanza. Di corsa balzavo giù per le scale urlando come una forsennata Oggi facemu i buttigghi!! Oggi facemu i buttigghi!! una volta lì davanti alle cassette, ne prendevo sempre un paio, li toccavo, il odoravo… ummm che buoni!!! Ii adoravo. Gli davo una sciacquata veloce e li mangiavo così sani sani. Nel cortile la nonna preparava tutte le bottiglie di vetro, bottiglie recuperate da innumerevoli bevute di birra. Una per una, le insaponava e metteva in ammollo nella bagnarola. Noi ragazze, le davamo una mano, le immergevamo nelle bagnarole in modo da rimuovere bene tutto il detersivo e così da lavarle bene bene. Ogni tanto di nascosto arrivavano i cugini che ci bagnavano con la pompa, noi per giocare poi bagnavamo loro. Era divertentissimo ridevamo e correvamo come i pazzi. La nonna se ci vedeva si biliava e ci urlava contro Viditi chiddu ca aviti a fari, si vi sciuppiati poi vi dugnu i supra. Oggi amu a travagghiari amo a fari i buttigghi, e si cuntinuati accusi finisci chi vi sciuppiati e namu a ricurdari a iunnata. 3 infine tutti bagnati finivamo di sciacquare le bottiglie rimaste. Lasciavamo tutte le bottiglie, tantissime bottiglie al sole tutte luccicanti a riposare e asciugare. Ma i giochi con l’acqua non finivano qui! Subito dopo si cominciava con il lavaggio delle cassette dei pomodori. buttavamo i pomodori dentro le bagnarole, li lasciavamo in ammollo per un pò e poi uno per uno li prendevamo e riponevamo dentro le cassette. La nonna e le zie nel frattempo, preparavano un gran fuocherello acceso per terra e sopra al fuoco c’era la più grande casseruola mai vista, io anche se piccola, ero molto alta, superavo il metro e venti ma ciò nonostante, non arrivavo a vedere dentro la casseruola. Per vedere dentro, dovevo salire su una scaletta. Mi piaceva guardare i pomodori mentre cuocevano, era bello tutto quei pomodori che strofinavano tra loro, tanto calore e bollore che usciva e un profumo intenso di pomodoro. I pomodori più piccoli ogni tanto saltavano in alto per il tanto bollore. Con un grande arnese, che io da piccola lo vedevo come il più grande cuppino che si è fuso ad uno scolapasta, venivano afferrati i pomodori pronti, quelli più pronti, pronti perché stavano a galla, lì pronti ad uscire. Una volta afferrati, li facevano rotolare giù per la macchina mangia pomodori. La macchina mangia pomodori, stava sotto il controllo della zia Pizzitta. Lei si occupava di mettere in moto la macchina, fermarla se bloccata e aiutava i pomodori a essere spremuti per bene. Li aiutava con un mattarello, li spingeva giù per il canale e man mano che scendevano, gli diceva: entrate entrate, mettetevi in fila e non spingete ci state tutti e a breve sarete uniti per sempre. Un bel succo rosso vivo, molto concentrato, caldo e intenso, colava giù dalla macchina e finiva in una grande pentola. Da un altra estremità della macchina, uscivano le bucce e Pizzitta, di tanto intanto si occupava di passarle nuovamente nella macchina. La postazione successiva era proprio la mia e della zia Maria. Io mi occupavo di prendere le bottiglie dalle ceste, di staccare le foglie dal grande mazzo di basilico e di mettere due foglioline all’interno di ogni bottiglia. Mi piaceva tanto questo compito perchè sentivo l’odore intenso del basilico sulle mani. La zia Maria, prendeva la bottiglia da me preparate e la metteva sotto il rubinetto, girava la valvola e fuoriusciva il caldo succo di pomodoro. Lasciava aperto il rubinetto fintanto che la bottiglia non si fosse ben riempita di succo di pomodoro. Lo zio Tano, con grande maestria e tanta attenzione, prendeva le bottiglie molto calde per via del sugo che fumava fin fuori dal collo delle bottiglie, le piazzava sotto un’altra postazione. Prendeva un tappo e lo attaccava ad una calamita di un altro macchinario, poi con decisione e fermezza girava una grossa manovella fintanto che il tappo non fosse ben attaccato alla bottiglia. Faceva una veloce verifica in modo da testare che fosse ben chiusa e dava l’ok a Giorgo che si occupava di prendere la bottiglia e riporla nella cassetta. Giorgio non era di famiglia ma era come se lo fosse. Noi lo facevamo stare li a svolgere il suo compito. Sebbene fosse un piccolo compito, lo faceva tanto contento. Adesso le bottiglie di pomodoro, dopo tutti questi step seguiti con cura sono pronte!! Giuseppe era ancora molto attento e sentendo la false e sapendo che la storia non finisce così subito ribatte dai mamma lo so che non finisce così, continua a raccontare. E allora la mamma riprende: è vero, Il bello deve ancora arrivare. Si prendevano dei grandi barili ex barili di petrolio davvero enormi sempre molto più alti di me. Lo zio o la nonna con l’aiuto di una scala, si calavano dentro a testa in giù e pian piano ci mettevano dentro le bottiglie di pomodoro, disposte una accanto all’altro non proprio attaccate ma vicine, poi sopra uno strato di panni e pezze vecchi e così via. Quando si giungeva alla fine, sopra l’ultimo strato di pezze, si mettevano delle pietre per tenere il tutto fermo. Infine si versava dentro l’acqua con una pompa che dal basso saliva fino in cima, era importante che fosse riempito tutto fino all’orlo. A questo punto, proprio sotto i grandi barili, venivano accesi altri grandi fuochi. Dopo un pò di ore, il tempo di mettere apposto tutta l’attrezzatura, si spegnevano i fuochi e si lasciavano dentro le bottiglie tutta la notte a raffreddare e a riposare. Nta notti a Nonna non pigghiava sonnu, si girava e si vutava, stava cu penseri pi buttigghi. Giuseppe u sai picchì a nonna aviva u penseri e buttighie? 4 disse la mamma. Picchi na vota, succidiu nu macello, i buttigghi non furunu chiusi pi come si devi e nta nanzi a notti, i buttighi scuppiarunu e na ranni botta si sintiu! 5 rispose subito Giuseppe a tono. Si bravo, fecero una botta fortissima e la nonna saltò in aria. Quella notte fortunatamente era andato tutto bene, nessuna bottiglia scoppiò. L’indomani mattina, tirarono fuori tutte le bottiglie una per una ed erano tutte integre, troppo belle e troppo buone. Riponemmo le bottiglie all’interno delle cassette che poi posizionammo all’interno di grandi scaffali. Che bello tutte le bottiglie per l’anno per la famiglia erano pronte. Ne prendemmo subito un paio e preparammo una pasta al pomodoro per tutti. Quella pasta, la ricordo ancora, aveva il sapore dei pomodori ma anche di tutti noi e di tutti gli sforzi fatti per produrre la passata. Dai Giuseppe è pronto, adesso mangiamoci la nostra pasta al pomodoro e immaginiamo di averla fatta con le nostre mani. Dai Giuseppe è pronto, adesso mangiamoci la nostra pasta al pomodoro e immaginiamo di averla fatta. Traduzioni: Giuseppe tieni 1 euro, vai a comprare una bottiglia di pomodoro, questa mattina me la sono scordata di comprarla alla bottega. Con il resto dei soldi, comprati tutto quello che vuoi, però non ti comprare porcherie. Se proprio te le devi comprare, compra le carrube che sono più sane. Your browser does not support the audio element. ↩︎ prodotti a kilometro 0, prodotti in campagna familiare. ↩︎ Prestate attenzione a quello che fate, se vi fate male poi io vi do altre botte. Oggi dobbiamo lavorare, dobbiamo fare le bottiglie di pomodoro, se continuate così, rischiate di farvi male e poi finisce che finiamo all’ospedale e ci ricorderemo per sempre di questa giornata sventurata. Your browser does not support the audio element. ↩︎ Durante la notte la nonna non prendeva sonno. si girava e rigirava, stava con il pensiero alle bottiglie. Giuseppe lo sai perchè la nonna aveva il pensiero alle bottiglie? Your browser does not support the audio element. ↩︎ Perché una volta è successo che non erano state chiuse bene e allora durante la notte sono scoppiate, facendo un gran fracasso Your browser does not support the audio element. ↩︎
Erano le 4.30 e Pierluigi aveva ancora gli occhi chiusi. Era sommerso in un sogno profondo. il sogno di un gioco, guardie e ladri. Pierluigi, era alla ricerca di un posto posizionato in alto, un luogo dove avrebbe potuto godere di una buona visione con multiple vie di fuga. Scelse la terrazza di Floride, una terrazza enorme che offriva una vista sulla piazza centrale del paese. Da lì poteva vedere tutta la piazza e seguire le guardie che avevano appena finito di contare e stavano per andare alla ricerca dei ladri. Un sobbalzo lo svegliò aprì leggermente gli occhi e vide a bettula 1 del nonno. L’aveva usata come cuscino e gli aveva biascicato addosso mentre dormiva. Il sobbalzo era stato causato da Stella la cavalla del nonno che si stava destreggiando nella dura salita che portava alla campagna. Cooooocoocode sentì, un colpo di ali e la bettola iniziò a muoversi. Pierluigi, aveva sonno e non si fece domande sul perché la bettola si muoveva ed emetteva versi di gallina, chiuse gli occhi e riprese a dormire. Appena arrivarono in campagna, si fermarono di fronte all’orto. Scesero dal cavallo, il nonno disse Sta Iddina non è chiu bona a nenti. Non fa qiu ovae non è bona mancu più fari u brodu, c’amo a tagghiari a testa. 2 allora il nonno si girò verso Pierluigi e gli diede un coltello dal manico di legno molto affilato, l’aveva affilato la mattina con tanta cura. Pigghia stu cuteddu, cià fari nun tagghiu nte cannarozzi. Te ca. 3 Pierluigi aveva solo 9 anni era ingenuo e perplesso non capiva il perché di questa azione e non si spiegava perché doveva essere proprio lui a farla. Era molto impaurito dalla gallina. Il nonno la prese tenendola per le ali e tenendogli la testa e gli disse fozza tagghia! allora Pierluigi si avvicinò e avvicinò il coltello alla gola della galliana, la sfiorò, non appena vide il sangue, rabbrividì e si fermò. Il nonno cercò di riprendere il coltello per terminare la gallina. Ad un tratto la gallina gli sfuggì di mano e cominciò ad emettere versi smorzati. Si mise a correre sguazzando sangue di qua e di la. Il sangue schizzò su Pierluigi e sul nonno e sul prato, Sembrava una scena splatter come nei film di Tarantino. Cercarono di acchiappare la gallina ma gli sfuggì, dopo un pò la gallina si stancò e cominciò a barcollare qua e la. Il nonno in un balzo l’afferrò e con un colpo secco gli recise la gola, mentre lo fece, si sentì un ultimo stridulo coccodè che mise fine alle sofferenze della povera gallina. Allora la prese e la appese ad un palo vicino all’orto e disse ora voggiu a vidiri si tornunu staceddi da mala sorti! 4 tutta questa crudeltà per far scappare gli uccellini che in quei giorni mangiavano il raccolto. Il nonno aveva fatto uno spaventapasseri ma non era stato sufficiente. Ci teneva molto alla sua campagna aveva fatto tanti sacrifici e avrebbe fatto qualsiasi cosa per raccoglierne i frutti. Pierluigi era un po scandalizzato dalla scena a cui aveva assistito. Un attimo dopo, si mise al lavoro, doveva mbvirare 5, spostando la suca di rasola a rasola. Mise la suca sotto un grande albero di pesche e si sedette ad aspettare. Ad un tratto, gli calò la palpebra e si trovò di fronte le guardie che stavano salendo dalle scale principali. Allora Pierluigi, scappò verso l’altra uscita della terrazza ma un’altra guardia arrivò velocemente su dalle stesse scale. Una guardia disse ormai sei un trappola, arrenditi. Pierluigi, ci teneva a non essere catturato e avrebbe fatto di tutto, allora si sporse dalla terrazza mentre i due si avvicinavano. Fu lì che capì che poteva farcela, col cuore in gola e la guardia ad un soffio, flush si tuffó dalla terrazza. Durante il volo emise un gemito di follia, ammortizzó la caduta facendo delle capriole sul prato morbido che lo aiutò ad attenuare la caduta. Si chiuse sì tanto in se stesso che successe una cosa insolita, emesse una flatulenza e subito dopo sentì qualcosa che spinse nelle mutande proprio in prossimità del sedere. Non era paura ma causato dalla contrazione che fece il suo corpo. Era piccolo e non sentì alcun dolore, si alzò e continuò a correre fino a casa verso il bagno per finire ciò che era cominciato. Ad un tratto mentre si lavó le mani, sentì l’acqua, la sentì fino ai piedi si svegliò e vide una pozza enorme, si era scordato di spostare la suca e il pesco era pieno colmo d’acqua. Allora prese la suca e di corsa la sposto nella rasula sotto. Dopo un po' arrivo il nonno, Pierluigi era stanco e aveva fame chiese al nonno che ore sono? Il nonno alzò lo sguardo e guardò il sole e disse iè menzionnu ora ni calamu e annamu a manciari. Iai fami? Te docu manciati na pessica. 6 Pierluigi accenno un sospiro di sollievo, domandava spesso che ore erano al nonno, era affascinato da come sapesse sempre l’ora esatta senza l’uso dell’orologio. Il nonno preparò con le foglie dei fichi d’india ei limoni marci u pastrozzu 7 un secchio di mangime che diede alle pecore, andavano pazze per u pastrozzu. Il nonno prese il cavallo e si incamminarono verso casa. Mentre scendevano Pierluigi guardò la campagna ed in lontananza vide la gallina che stava lì, illuminata dal sole, riusciva a vedere chiazze di sangue rosso, qualche goccia che colava piano piano giù. Si sentiva un po' in colpa per la gallina, ma il pensiero svanì un attimo dopo arrivati a casa di fronte a una gran tavola bandita con tutta la famiglia lì pronti a pranzare. Bértula o bisaccia è un manufatto tessile comunemente diffuso su tutto il territorio regionale. Il suo uso ha origini antichissime ed è fortemente legata alla vita quotidiana. In ambito contadino, viene utilizzato per trasportare cibo come frutta, verdura o latte e uova. Oppure anche per utensili. ↩︎ Questa gallina non produce più niente. Non fa più le uova e non è più buona neanche per fare il brodo, dobbiamo tagliarle la testa. ↩︎ Prendi questo coltello e coltello, devi fare un taglio in gola. Tieni ↩︎ Ora voglio vedere se tornano ancora questi uccelli che portano solo guai ↩︎ Mbvirare annaffiare le piante, utilizzato sia con l’utilizzo della suca (canna, pompa) oppure anche tramite canalette a scorrimento d’acqua. ↩︎ E’ mezzogiorno, tra poco torniamo a casa e andiamo a mangiare. Hai Fame? Tieni mangiati una pesca. ↩︎ U pastrozzu composto di limoni e aranci, lasciati sotto un telone e sotto al sole, rimangono lì per giorni, settimane a macerare, in modo da comporre questa melma da un odore molto distinguibile e forte. Viene utilizzato come mangime per gli animali, ha ottimi valori nutrizionali. ↩︎
Si cominciava ad intravedere qualche raggio di sole, che splendeva sui ciottoli bianchi della spiaggia. I piccoli vetrini creavano un riflesso ancora più forte, quasi abbagliante. In lontananza si intravedevano delle barchette che dondolavano in mare. Quella mattina l’acqua era molto calma, piano piano le piccole barchette si avvicinavano verso la riva e divennero pescherecci di media grandezza. Sul lungomare, una coda di lape (moto ape) in attesa. Gino sta ancora dormendo, ieri ha fatto consegne fino a tardi. Oggi u mari ie chinu i pisci! 1 si sente urlare in lontananza. La prima barchetta di un anziano pescatore attracca, un pescivendolo si avvicina e con il suo garzone comincia a scaricare cassette piene di pesce. Le trasportano sulla loro ape, di colore giallo sbiadito, ne aveva fatta di strada e ne aveva preso di sole quell’ape. Riempiono il cassone con le cassette piene di pesce fresco e ci misero sopra pezzi di ghiaccio per mantenerlo. Chi bellu pisci, pisci friscu!! pisci spada, sciabulata, aguglia, curstadelle io iaiu! 2 sentì in lontananza Gino, stava ancora dormendo e voleva tanto continuare a dormire ma oggi era martedì e come tutti i martedì puntuale passava Francu u pisciaru. Mu fai un chilu i curstadelle!! 3 sentì Gino urlare dalla casa accanto. il pescivendolo si ferma, scende e si avvicina alla bilancia, bilancia bellissima, enorme, con due piatti e un bilanciere dorato. Mette due pesi da mezzo chilo sopra un piatto della bilancia, sull’altro, mette un foglio e ci butta sopra a pugni le costardelle fintanto che i due vassoi non siano in equilibrio. Signura c’ha regalu! sulu sta junnata 5 euru o chilo! 4 Sempre urlando, in modo che anche le massaie vicine potessero sentire. Un cestino di vimini (u panaru) cala giù con una carrucola dal balcone. Il garzone prende i soldi dal cestino e ci mette le corstadelle, ci aggiunge due limoni e un pò di prezzemolo. Fortunatamente per Gino, quella mattina nessun altro tra i suoi vicini rispose all’offerta di Francu. Allora sentì il forte rumore dell’ape che piano, piano si allontanava. Il rumore stava per terminare e finalmente Gino avrebbe potuto riprendere a dormire. Banane, mulinciane, pumadoru, kiwi io iaio. Culi voli l’ovaaa! 5 il sonno di Gino fu presto interrotto da Pippo u viduraru. Viniti,viniti,accattati,accattati, tutta roba frisca iè! 6 Alla fine Gino come tutti i martedì, decise di alzarsi, anche se ancora presto e ancora assonnato, spalancò la finestra, un forte sole e tanto calore illuminò la sua giornata. Audio Siciliani Oggi u mari ie chinu i pisci! Your browser does not support the audio element. ↩︎ Chi bellu pisci, pisci friscu!! pisci spada, sciabulata, aguglia, curstadelle io iaiu! Your browser does not support the audio element. ↩︎ Mu fai un chilu i curstadelle!! Your browser does not support the audio element. ↩︎ Signura c’ha regalu! sulu sta junnata 5 euru o chilo! Your browser does not support the audio element. ↩︎ Banane, mulinciane, pumadoru, kiwi io iaio. Culi voli l’ovaaa! Your browser does not support the audio element. ↩︎ Viniti,viniti,accattati,accattati, tutta roba frisca iè! Your browser does not support the audio element. ↩︎
Scendi papà! disse Gino seduto sulla sua vespa una volta arrivato nella piazza centrale del paese. Non sentendolo scendere allora ripeté dai..scendi papà! Ma niente, allora una terza volta scendi pa.. lo ripetè girandosi, ma il papà non c’era! Allora cominciò ad impallidire e divenne bianco, un tutt’uno con il bianco della sua tanto amata Vespa. Sfortunatamente per Gino quel giorno era la festa del paese di Sant’Antonio e la piazza era piena colma di gente. Tutti, in piazza, si girarono a guardare la scena e cominciarono a ridere. Gino dal bianco divenne rosso, poi girò la vespa era molto preoccupato per il papà allora tornó indietro alla sua ricerca. Dal paese scese giù in velocità cercando di guardare in tutte le direzioni, era agitato e man mano che scendeva e non lo trovava si agitava sempre di più. Una volta arrivato al paese adiacente vide un vecchietto seduto a bordo strada su una sedia bianca, in silenzio che osservava. Gino si avvicinò e gli chiese se aveva visto suo papà. Lui rispose 153, 24, 7 e 1. Gino non capì allora proseguì per la strada e dopo poco, finalmente trovo tanta gente, tutti attorno a soccorrere il papà. Hai hai! Urlava il padre. Chi figghiu sciaguratu chi iaiu! Nenti vali! mancu a Vespa sapi puttari! Gino quella mattina aveva commesso due grandi errori. Quello di aver fatto cadere il padre dalla vespa senza accorgersene, ma anche di essersene accorto troppo tardi e davanti a troppa gente. Il secondo fu il problema più grosso, che lo segnó a vita. Da quel momento la sua storia tramite il cuttigghio, fu raccontata a tutti in paese poi nel paese vicino e così via in tutta la città. Fintanto che tutti non arrivarono a conoscere la disavventura di Gino. Fu un attimo che Gino fu soprannominato “scinni papà!” Fortunatamente Gino, non andava troppo veloce con la Vespa allora suo papà, se la cavò con solo qualche piccola ammaccatura e un’abrasione lungo tutto il braccio. Gino non se n’era accorto poichè quella mattina era sommerso di pensieri, pensava alle commissioni che lo attendevano l’indomani. Ma alla fine, cosa voleva dire il vecchietto saggio a bordo strada? Era lì da tutta la mattina e aveva visto passare 153 macchine, 24 moto ape, 7 motorini e 1 solo stupido, si è fermato dopo essere passato più volte con e senza papà. Traduzioni: Chi figghiu sciaguratu chi iaiu! Nenti vali! mancu a Vespa sapi puttari! Che figlio sciagurato che ho! non vale nulla! non sa neanche guidare la sua vespa! cuttigghio sparlare di altre persone
Theory Flutter is a new language that has recently come out from the beta, implemented by Google on the basis of Dartlanguage less known today than FLutter. It is a modern language but based on Dart, the syntax is very similar to any other modern object language. The main concept on which it is based is Everything is a widget. But what does it really mean? All we are dealing with when developing in Flutter is a widget. A widget may or may not have a status, which can determine its content or form. It could be seen as an object as you imagine it with the difference that it can have a state to update the view and it can contain contents and shapes. Yes, you have understood shapes correctly, since a widget, besides defining properties and methods, can define shapes. Finally, a widget can include or extend another widget. So an app in flutter is the composition of the widgets together. An example could be a form containing n input text widgets etc. and a button also widget. The main widget that we can make are of two types, at least for now before google upsets everything, and are as follows: Widgets stateless (statelesswidget) are immutable, like a static class. A button that never changes color can be defined as a static widget. It can also be used in case we need to show only non-interactive descriptions. Stateful widgets (statefulwidget): one (or more) property related to them, changing, can change the behavior, appearance or functionality of the widget. An app screen that changes based on content in an http response is a widget with status (just because the screen content varies based on the http response). Example: in case we need to request data from users and then update the same widget to validate or show an ok data entered correctly. To update the widget you need to use the setstate with the property updated so that the change is extended. Consideration The thing I liked most about Flutter: that we can develop everything through a single language. the livereload works really well and allows us to write the code faster. with single environment and same code we have a build for Android and IOS as Native Recently flutter can also be used in web and desktop (Linux & Windows) :) At the beginning I was a little skeptical about using widgets and it was a little complicated and dispersed to understand the advantages. After using them I must say that they are simpler than they seem to be implemented and thanks to this approach it will be easier to reason according to the best practice “DRY” (Don’t Repeat Yourself). On the one hand it adds an incredible nesting during the construction phase of the interface by boxing one widget into another and so on; however it has a logic that is modular in nature and this is excellent for decoupling small widgets that will consist of their properties and their tasks. By decoupling, we can test them and use them with different widgets to make more complex widgets. In this way the code will be more maintainable and functional.
Teoria Flutter è un nuovo linguaggio uscito da non molto dalla beta, implementato da google sulla base del linguaggio Dart, meno noto ad oggi di FLutter. E’ un linguaggio moderno ma basandosi su Dart, la sintassi è molto simile a qualsiasi altro linguaggio moderno ad oggetti. Il concetto principale su cui si basa è Everything is a widget. Ma cosa vuol dire davvero? Tutto ciò con cui abbiamo a che fare sviluppando in Flutter è un widget. Un widget può avere o meno uno stato, che ne può determinare contenuto o forma. Potrebbe essere visto come un oggetto come lo immaginate con la differenza che può avere uno stato per aggiornare la view e può contenere contenuti e forme. Sì, avete capito bene! forme in quanto un widget oltre a definire proprietà e metodi, può definire forme. Infine un widget può includere o estendere un altro widget. Quindi un’applicazione in flutter è la composizione dei widget insieme. Un esempio può essere un form che contiene un widget di input text ecc. e un botton anch’esso widget. I widget principali che possiamo realizzare sono di due tipi, almeno per adesso prima che google sconvolga tutto, e sono i seguenti: Widget privi di stato (statelesswidget): sono immutabili, come una classe statico. Un bottone che non cambia mai colore può essere definito come uno widget statico, può essere utilizzato anche nel caso abbiamo bisogno di mostrare solo descrizioni non interattive. Widget dotati di stato (statefulwidget): una (o più) proprietà ad essi afferente, cambiando, può mutare il comportamento, le sembianze o le funzionalità del widget. Una schermata dell’app che cambia in base al contenuto in una risposta http è un widget dotato di stato (proprio perché il contenuto della schermata varia in base alla risposta http). Esempio: nel caso in cui dobbiamo richiedere dei dati agli utenti e poi riaggiornare lo stesso widget per validare o mostrare un ok dati inseriti correttamente. Per aggiornare il widget è necessario utilizzare il setstate con la proprietà aggiornata in modo che venga prorogata la modifica. Considerazioni La cosa che ho apprezzato di più di Flutter: che possiamo sviluppare tutto tramite un unico linguaggio. il live reload funziona davvero bene e ci permette di scrivere il codice più velocemente. Dobbiamo scrivere e manutenere un’ unica linea di codice fruibile su Android e IOS come nativo. da poco con la stessa linea di codice è possibile fare il build anche per web e desktop (Linux e Windows) :) All’inizio ero un pò scettico nell’utilizzare i widget ed è stato un pò complicato e dispersivo comprenderne i vantaggi. Tuttavia, dopo averli utilizzati devo dire che sono più semplici di quello che sembrano da implementare e grazie a questo approccio sarà più semplice ragionare secondo la best practice “DRY” (Don’t Repeat Yourself). Da un lato aggiunge una nidificazione incredibile in fase di costruzione dell’interfaccia iscatolando un widget dentro un altro e così via, dall’altro ha una logica che di sua natura è modulare e ciò è ottimo per disaccoppiare piccoli widget che saranno consistenti delle loro proprietà e i loro compiti. Disaccoppiando, possiamo testarli e usarli con diversi widget per fare widget più complessi. In questo modo il codice sarà più manutenibile e funzionale.
Con questo articolo voglio spiegare come poter fare una app per il proprio canale personale youtube in 10 minuti. Io ho sviluppato in questo modo l’app Italiano con Eli, creata per il canale Italiano con Eli. Mentre stavo sviluppato l’app ho deciso di generalizzare la soluzione in modo da poter essere utilizzata da chiunque per recuperare qualsiasi video dato un determinato link del corrispettivo canale Youtube. L’applicazione completa e funzionante è disponibile al seguente link github. Potete fare un Fork o un Clone, usarla così com’è oppure modificandola come meglio vi pare. Se vi piace potete anche mettere un like. Configurazione necessaria Unica cosa da fare per poterla utilizzare è configurare la connessione alle API di Youtube in modo da poter utilizzare la ricerca dei vostri video del canale. Per una corretta configurazione potete seguire i seguenti step: Accedere al Google Api Console con il vostro account Google se non lo avete, create un nuovo account. Andare sotto progetti e cliccate su NUOVO PROGETTO, Immetti il nome e aspetta che google crei il progetto. Clicca sul link delle Google API nella libreria delle API e cerca oppure seleziona le api youtube come evidenziato nella foto di seguito dopo clicca abilità e vai sotto la voce di menu credenziali Seleziona Youtube Api v3, seleziona dati pubblici e conferma Adesso Google creerà la tua chiave di accesso l’api key recuperata al punto 6 dovrà essere inserita all’interno dell’applicazione e sostituita nella pagina main.dart al posto della key come segue: static String key = ""; // ** ENTER YOUTUBE API KEY HERE ** static String channelid = "UC_8mNVpafplqHNy85No4O2g"; // ** ENTER YOUTUBE CHANNEL ID HERE ** il channel id deve fare riferimento al vostro canale youtube. Lo potete semplicemente estrarre dal link del vostro canale youtube, prendendo la parte finale del link: La configurazione è finita, avete appena creato la vostra nuova App per il vostro canale Youtube Yehaa! Qualche cenno al codice Per agevolare lo sviluppo dell’applicazione ho usato i seguenti due plugin che potete trovate nella pagina dei plugin per flutter flutter packages i plugin utilizzati sono i seguenti: youtube player Flutter youtube Api il codice dell’applicazione è molto semplice e ho usato una struttura di suddivisione pagine abbastanza chiara: Model contiene il modello dei dati recuperati dalle youtube Api. Adesso la classe fa anche qualcosa in più, c’è un metodo che recupera i dati da youtube e li inserisce in una lista di youtube data. Usa un sistema che fa il recupero dei dati solo se non sono presenti in cache usando la flutter shared preference. Questo meccanismo è molto utile in quanto le Api di Google sono a pagamento in funzione dell’utilizzo. Utilizzando la cache risparmierete drasticamente in chiamate. se non avete mai usato la cache in flutter potete leggere quest’altro mio articolo: flutter shared preferences in profondità. Pages contiene le pagine dell’APP home page category page pagina di play video UI contiene un widget per rappresentare la lista dei video. L’ho creato a parte come widget per avere a disposizione una view da poter utilizzare sia nella pagina home che category. In questo modo, potrei usarlo anche in altre pagine in futuro. Main.dart altro non fa che inizializzare la classe del model, caricare i dati di youtube e passarli alle rispettive pagine. Utilizzando questa struttura il codice è molto semplice anche da manutenere ad esempio la pagina home contiene solo il seguente codice: import 'package:flutter/material.dart'; import 'package:youtubeapichannel/Model/YoutubeData.dart'; import 'package:youtubeapichannel/UI/YoutubeVideoList.dart'; YoutubeData youtubeData; class Home extends StatefulWidget { Home(YoutubeData inyoutubeData) { youtubeData = inyoutubeData; } @override _HomeState createState() => new _HomeState(); } class _HomeState extends State<Home> { callAPI() async { setState(() {print('UI Updated'); }); } @override void initState() { super.initState(); //callAPI(); } @override Widget build(BuildContext context) { if(youtubeData!=null) return new YoutubeVideoList(youtubeData.getHomeList()); else return new Container(child:new Text("problem loading data from youtube!")); } } Quindi riceve semplicemente in input la classe contenente la lista dei video e richiama il plugin YoutubeVideoList che sta sotto la UI per rappresentare i video sotto forma di lista di video youtube. Unica pecca, è che quando l’ho realizzata non ero tanto esperto e ho deciso di passare i dati come parametro. Oggi flutter mette a disposizioni tecniche migliori per passare i dati da una view ad un altra, in effetti dovrei fare un po' di refactor, ma oggi non ne ho voglia.
With this article I want to explain how to build an app for your personal youtube channel in 10 minutes. I did this basic project to create Italiano con Eli app, created for the YouTube channel Italiano con Eli. While I was developing the app I decided to generalize the solution so that it can retrieve any video to any Youtube channel. The complete and working application is available at the following link github. You can make a Fork or a Clone or use it as it or by modifying it as you like. If you like you can also put a like to my git project. Required configuration The only thing to do to be able to use it is to configure the connection to the Youtube API so that you can use the search for your channel videos. For a correct configuration you can follow the following steps: Log in to the Google Api Console with your Google Account if you don’t have it, create a new account. Go under projects and click on NEW PROJECT, enter the name and wait for google to create project. Click on the link in the Google API library and search or select the youtube API as highlighted in the photo below after click ability and go under the credentials menu item Select Youtube Api v3, select public data and confirm Now Google will create your api key the api key recovered in step 6 will be inserted within the application and replaced the page main.dart instead of the key as follows: static String key = ""; // ** ENTER YOUTUBE API KEY HERE ** static String channelid = "UC_8mNVpafplqHNy85No4O2g"; // ** ENTER YOUTUBE CHANNEL ID HERE ** the channel id must refer to your youtube channel. You can simply extract it from the link of your youtube channel, taking the final part of the link: The configuration is finished, you have just created your App for your personal Youtube channel! Yehaa! Some references to the code To facilitate the development of the application I used the following two flutter plugins that you can find on flutter packages pages. The plugins used are the following: youtube player Flutter youtube API the application code is very simple and I have given apage division structure fairly clear: Model contains the model of data recovered from API youtube. Now the class also does something more, there is a method that also retrieves the data from youtube and inserts it in the model and some method to recover the data. There is also a system that recovers data only if it is not cached using the shared preference flutter. This mechanism is very useful as the Google API is paid according to the use. Using the cache will save you drastically in calls. if you have never used the cache in flutter you can read this mine other article: flutter shared preferences in depth. Pages contains the APP pages home page category page play video page UI contains a widget to represent the list of videos. I created it separately as a widget to create a view that can be used on both the home page and category and I could also use it in other pages in the future. **Main.dart ** just to initialize view, the model class, load the youtube data and pass it to the respective pages. Using this structure, the code is very simple to maintain, for example the home page contains only the following code: import 'package:flutter/material.dart'; import 'package:youtubeapichannel/Model/YoutubeData.dart'; import 'package:youtubeapichannel/UI/YoutubeVideoList.dart'; YoutubeData youtubeData; class Home extends StatefulWidget { Home(YoutubeData inyoutubeData) { youtubeData = inyoutubeData; } @override _HomeState createState() => new _HomeState(); } class _HomeState extends State<Home> { callAPI() async { setState(() {print('UI Updated'); }); } @override void initState() { super.initState(); //callAPI(); } @override Widget build(BuildContext context) { if(youtubeData!=null) return new YoutubeVideoList(youtubeData.getHomeList()); else return new Container(child:new Text("problem loading data from youtube!")); } } Then simply receive the class containing the list of videos as input and call the YoutubeVideoList plugin that is under the UI to represent them in the form of a list of YouTube videos. The only flaw is that when I made it I was not so expert and I decided to pass the data as a parameter. Today flutter offers better technical provisions for passing data from one view to another, in fact I should make an update and do the usual refactor.
Prerequisiti Questo articolo richiede una discreta conoscenza dei seguenti argomenti che potete approfondire ai seguenti link: Firebase Remote Config Flutter Shared Preferences Come mai vi parlo di questi due plugin che sembrerebbero non avere niente a che fare l’uno con l’altro? Con questa guida, vi illustrerò come trarre beneficio da questi due plugin e come risparmiare usando i servizi gratuiti offerti da Firebase! :) Nell’app Italiano con Eli, creata per il canale Italiano con Eli, ho creato un progetto su Firebase, in modo da poter utilizzare il remote config e altre potenzialità che Firebase mette a disposizione. L’app si occupa di mostrare una lista video da canale youtube, se interessati potete scaricare il codice sorgente su github, potete utilizzarlo con qualsiasi canale youtube. Per rendere l’app Italiano con Eli più originale ho pensato di aggiungere ai video delle domande sugli argomenti trattati nei video. Avevo quindi bisogno di un posto dove salvare i dati delle domande. Con Firebase, la cosa più logica e anche più diretta sarebbe quella di usare Realtime Database oppure Firestore. Io ho pensato di utilizzare remote config! Sì, avete capito bene! Remote config! Il primo pensiero che vi sta saltando in mente in questo momento sarà “questo è pazzo!” e il secondo “ma perchè?” condivido pienamente con voi il vostro primo pensiero… c’è un pò di pazzia nella scelta adottata. Per quanto riguarda il perché invece, posso rispondere come segue: non avevo molto tempo a disposizione remote config è gratis e non ha limitazione di utilizzo si può dare accesso al servizio a una persona esterna le altre scelte mi avrebbero portato a dover costruire anche un’interfaccia back end per il censimento delle domande Conoscevo già remote config e sarei stato più rapido nell’implementazione Ma come funziona un servizio non nato per questo lavoro? Funziona benissimo, devo dire che mi ha sorpreso la reattività che dimostra l’app e il sistema che ho messo in piedi. Per farlo funzionare così bene però ho dovuto mettere assieme i due plugin nel modo che vi spiego di seguito. All’interno della console di remote config vado a inserire un nuovo parametro per ogni video. Nel campo nome inserisco l’id del video di youtube e all’interno del parametro un json contenente le domande e le risposte così strutturato: { "Playlist": "PLsrqydfBIVzy9IMGwSQieTVeSWC7cRCnN", "Questions": [ { "question": "How do you say how much is it??: ", "answare": [ "Quanto costa?", "Quanto costano? ", "Quanto costi?" ] }, { "question": "How do you say how much are they?: ", "answare": [ "Quanto costano?", "Quanto costate?", "entrambe/both" ] }, { "question": "Choose the correct option", "answare": [ "Quanto costa questo computer? 300 Euro", "quanto computer ti costa? 300 Euro", "Quanto costano questo computer?" ] }, { "question": "Quanto costa? is", "answare": [ "singolare", "plurale" ] } ] } Inoltre firebase mette a disposizione un inserimento facilitato che effettua anche la validazione del json, questo ci permette di editare più facilmente e non commettere errori. Nell’app Flutter ho poi costruito l’oggetto Quiz che conterrà l’id di youtube, una lista di Domande e la Domanda conterrà una lista di Risposte. Con vari metodi utili di gestione e costruttore per deserializzazione dei dei dati Json come segue: class Answer { final String answerText; final bool isCorrect; Answer(this.answerText, this.isCorrect); } class Question { final String question; final List<Answer> answers; Question(this.question, this.answers); } class Quiz { List<Question> _questions; int _currentIndex = -1; int _score = 0; String _videoId; Quiz.fromJson(String videoId, String jsonInput){ _videoId = videoId; _questions = new List<Question>(); var jsonQuestions = json.decode(jsonInput); jsonQuestions['Questions'].forEach((quest) { List<Answer> answers = new List<Answer>(); bool iscorrect=true; quest['answare'].forEach((ans) { //mettere la prima a true answers.add( new Answer(ans, iscorrect) ); iscorrect=false; }); answers.shuffle(); _questions.add( new Question( quest['question'], answers) ); }); } Quiz(this._questions) { _questions.shuffle(); } List<Question> get questions => _questions; int get length => _questions.length; int get questionNumber => _currentIndex + 1; int get score => _score; Question get nextQuestion { _currentIndex++; if (_currentIndex >= length) return null; return _questions[_currentIndex]; } Question get prevQuestion { _currentIndex--; if (_currentIndex < 0) return null; return _questions[_currentIndex]; } String get correctAnsware { return _questions[_currentIndex].answers.where((x) => x.isCorrect == true).first.answerText; } void answer(bool isCorrect) { if (isCorrect) _score++; } saveScore() async { SharedPreferences preferences = await SharedPreferences.getInstance(); preferences.setInt("OK"+_videoId, this.score); preferences.setInt("KO"+_videoId, this.length - this.score ); //preferences.setString(_videoId, "{'questions':'"+ this.length.toString() +"', 'score':'"+ this.score.toString() +"'}"); } } Per prima cosa, l’App recupera la lista dei video tramite le API youtube, successivamente recupera il relativo json da remote config utilizzando l’id del video e lo passa al costruttore dell’oggetto che lo deserializza ed il gioco è fatto! Una volta ultimata l’app mi sono accorto però che youtube mette a disposizione un numero limitato di chiamate API giornaliere e oltretutto ho pensato che fosse inutile che l’App, ogni volta che viene aperta, debba richiamare le API Youtube. I video oltretutto non cambiano tanto in quanto questo canale produce un nuovo video a settimana. Per risolvere il problema delle chiamate API Youtube e rendere il tutto più reattivo ho deciso di salvare tutto in una lista di stringhe json in cache utilizzando proprio la Shared Preferences. Infine ho aggiunto un parametro nella remote config contente una data. In questo modo ho fatto un meccanismo che mi permette di aggiornare i contenuti nei dispositivi solo se la data è più recente rispetto a quella memorizzata in cache del singolo dispositivo stesso: class YoutubeData { RemoteConfig remoteConfig; String apikey; List<YoutubeDto> allYoutubeVideoList = []; List<YoutubeDto> getHomeList() { return List<YoutubeDto>.from(allYoutubeVideoList.where((item) => item.kind == "video" && item.id != item.channelId )); } List<YoutubeDto> allCategory() { return List<YoutubeDto>.from(allYoutubeVideoList.where((item) => item.kind == "playlist" && item.id != item.channelId )); } List<YoutubeDto> getCategoryVideoList(String plylistID) { return List<YoutubeDto>.from(allYoutubeVideoList.where((item) => item.kind == "video" && item.id != item.channelId && item.playlist == plylistID )); } YoutubeData (this.apikey, this.remoteConfig); checkAndLoad() async { SharedPreferences preferences = await SharedPreferences.getInstance(); List<String> videoIDList = preferences.getStringList("VideoIdList"); DateTime lastUpdateDate,updateDate; String date = remoteConfig.getString("UpdateDatetime"); updateDate = DateTime.parse(date); if(preferences.getString("LastUpdateDatetime")!=null && preferences.getString("LastUpdateDatetime").isNotEmpty) lastUpdateDate = DateTime.parse(preferences.getString("LastUpdateDatetime")); else lastUpdateDate = DateTime(2010); if( (videoIDList==null || videoIDList.length==0) || updateDate.isAfter(lastUpdateDate)) { await loadYtDataAndStoreInSharedPref(); preferences.setString("LastUpdateDatetime", DateTime.now().toString() ); } await loadLocalDataAndAddQuestion(); } loadYtDataAndStoreInSharedPref() async { YoutubeAPI ytApi = new YoutubeAPI(apikey, maxResults: 50); List<YT_API> ytResult = await ytApi.channel( remoteConfig.getString('ChannelId') ); //salviamo il conteggio dei video in shared pref SharedPreferences preferences = await SharedPreferences.getInstance(); preferences.setInt("TotVideo", ytResult.length ); //salviamo json youtube video in shared pref e lista video YoutubeDto youtubedto = new YoutubeDto(); List<String> videoIdList = new List<String>(); for(var result in ytResult) { videoIdList.add( result.id); String json = jsonEncode( youtubedto.yApitoJson(result) ); preferences.setString( result.id, json ); } preferences.setStringList("VideoIdList", videoIdList ); } //aggiungiamo le domande agli oggetti youtube precedentemente salvati loadLocalDataAndAddQuestion() async { SharedPreferences preferences = await SharedPreferences.getInstance(); List<String> videoIDList = preferences.getStringList("VideoIdList"); allYoutubeVideoList = new List<YoutubeDto>(); int cntQuestion=0; for(String videoID in videoIDList) { String jsonYoutube = preferences.getString(videoID); Map userMap = jsonDecode(jsonYoutube); YoutubeDto youtubeDao = new YoutubeDto.fromJson(userMap); youtubeDao.questionJson = remoteConfig.getString( "_" + videoID.replaceAll("-", "_trattino_") ); if( youtubeDao.questionJson != null && youtubeDao.questionJson.isNotEmpty ) { cntQuestion = cntQuestion + 'question'.allMatches(youtubeDao.questionJson).length; youtubeDao.playlist = json.decode( youtubeDao.questionJson)['Playlist']; } allYoutubeVideoList.add(youtubeDao); } preferences.setInt("TotQuestion", cntQuestion ); } } Qui di seguito potete vedere un esempio di come funziona:
Prerequisites This article requires a good knowledge of the following topics that you can study at the following links: Firebase Remote Config Flutter Shared Preferences Why are we talking about these two plugins that would see having nothing in common? i With this guide I will show you how to benefit from this two plugins and how to save money using the free services offered by Firebase! :) In the talian with Eli app, created for the youtube channel Italian with Eli, I created a project on Firebase, so you can use the remote config and other potential that Firebase makes available for you. The app takes care of showing a video list from the youtube channel, if interested you can download the source code on github, you can use it with any youtube channel. To make the Italian with Eli app more original I thought of adding questions to the videos on the topics covered in the videos. So I needed a place to save the question data. With Firebase, the most logical and also most direct thing would be to use Realtime Database or Firestore. I thought of using remote config! Yes you got it right remote config! You will be thinking “this guy is crazy” and “why is he doing that?” I fully share with you this thought… there is a bit of madness in the choice made. The reason why I thought of using remote config is the following: I didn’t have much time available remote config is free and has no limitation of use you can give access to the service to an external person the other choices would have led me to have to also build a back end interface to allow the census of the questions I had already used remote config and I would have been faster in the implementation. But how does a service not born with this scope work? It works very well indeed, I must say that I was surprised by the responsiveness that shows the app and the system that I put up. To make it work so well, however, I had to put the two plugins together in the way that I explain below. Inside the remote config console I’m going to insert a new parameter for each video. In the name field I insert the id of the youtube video and inside the parameter a json containing the questions and answers structured as follows: { "Playlist": "PLsrqydfBIVzy9IMGwSQieTVeSWC7cRCnN", " Questions ": [ { " question ":" How do you say how much is it ??: ", " answare ": [ " How much does it cost? ", " How much does it cost? ", " How much does it cost? " ] }, { "question": "How do you say how much are they ?:", "answare": [ "How much do they cost?", "How much do you cost?", "both / both" ] }, { "question" : "Choose the correct option", "answare": [ "How much does this computer cost? 300 Euros", "How much computer does it cost? 300 Euros", "How much does this computer cost?" ] }, { "question": "How much does it cost? is", "answare": [ "singular", "plural" ] } ] } In addition, firebase provides an easy insertion that also performs json validation, this allows us to edit more easily and not to make mistakes. In the Flutter app I then built the Quiz object which contains the youtube id, a list of Questions and the Question will contain a list of Answers. With various useful management and constructor methods for deserializing Json data as follows: class Answer { final String answerText; final bool isCorrect; Answer (this.answerText, this.isCorrect); } class Question { final String question; final List <Answer> answers; Question (this.question, this.answers); } class Quiz { List <Question> _questions; int _currentIndex = -1; int _score = 0; String _videoId; Quiz.fromJson (String videoId, String jsonInput) { _videoId = videoId; _questions = new List <Question> (); var jsonQuestions = json.decode (jsonInput); jsonQuestions ['Questions']. forEach ((quest) { List <Answer> answers = new List <Answer> (); bool iscorrect = true; quest ['answare']. forEach ((ans) { // put the first to true answers.add (new Answer (ans, iscorrect)); iscorrect = false; }); answers.shuffle (); _questions.add (new Question (quest ['question'], answers)); }); } Quiz (this._questions) { _questions.shuffle (); } List <Question> get questions => _questions; int get length => _questions.length; int get questionNumber => _currentIndex + 1; int get score => _score; Question get nextQuestion { _currentIndex ++; if (_currentIndex> = length) return null; return _questions [_currentIndex]; } Question get prevQuestion { _currentIndex--; if (_currentIndex <0) return null; return _questions [_currentIndex]; } String get correctAnsware { return _questions [_currentIndex] .answers.where ((x) => x.isCorrect == true) .first.answerText; } void answer (bool isCorrect) { if (isCorrect) _score ++; } saveScore () async { SharedPreferences preferences = await SharedPreferences.getInstance (); preferences.setInt ("OK" + _ videoId, this.score); preferences.setInt ("KO" + _ videoId, this.length - this.score); //preferences.setString(_videoId, "{'questions': '" + this.length.toString () + "', 'score': '" + this.score.toString () + "'}"); } } The app first retrieves the list of videos via the youtube API, then retrieves the relative json from remote config using the video id and passes it to the constructor of the object that deserialize and you’re done. Once the app was completed, however, I realized that youtube provides a limited number of daily API calls. Moreover I thought it was useless every time I opened the app that it had to call the Youtube API. The videos, moreover, do not change as much as the channel makes a new video a week. To solve the problem of Youtube API calls and make everything more responsive, I decided to save everything in a list of cached json strings using the Shared Preferences. Finally I added a parameter in the remote config containing a date. In this way I made a mechanism that allows me to update the contents on the devices only if the date is more recent than the one stored in the cache of the single device itself: class YoutubeData { RemoteConfig remoteConfig; String apikey; List <YoutubeDto> allYoutubeVideoList = []; List <YoutubeDto> getHomeList () { return List <YoutubeDto> .from (allYoutubeVideoList.where ((item) => item.kind == "video" && item.id! = Item.channelId)); } List <YoutubeDto> allCategory () { return List <YoutubeDto> .from (allYoutubeVideoList.where ((item) => item.kind == "playlist" && item.id! = Item.channelId)); } List <YoutubeDto> getCategoryVideoList (String plylistID) { return List <YoutubeDto> .from (allYoutubeVideoList.where ((item) => item.kind == "video" && item.id! = Item.channelId && item.playlist == plylistID)); } YoutubeData (this.apikey, this.remoteConfig); checkAndLoad () async { SharedPreferences preferences = await SharedPreferences.getInstance (); List <String> videoIDList = preferences.getStringList ("VideoIdList"); DateTime lastUpdateDate, updateDate; String date = remoteConfig.getString ("UpdateDatetime"); updateDate = DateTime.parse (date); if (preferences.getString ("LastUpdateDatetime")! = null && preferences.getString ("LastUpdateDatetime"). isNotEmpty) lastUpdateDate = DateTime.parse (preferences.getString ("LastUpdateDatetime")); else lastUpdateDate = DateTime (2010); if ((videoIDList == null || videoIDList.length == 0) || updateDate.isAfter (lastUpdateDate)) { await loadYtDataAndStoreInSharedPref (); preferences.setString ("LastUpdateDatetime", DateTime.now (). toString ()); } await loadLocalDataAndAddQuestion (); } loadYtDataAndStoreInSharedPref () async { YoutubeAPI ytApi = new YoutubeAPI (apikey, maxResults: 50); List <YT_API> ytResult = await ytApi.channel (remoteConfig.getString ('ChannelId')); // save the video count in shared pref SharedPreferences preferences = await SharedPreferences.getInstance (); preferences.setInt ("TotVideo", ytResult.length); // save json youtube video in shared pref andvideo list YoutubeDtoyoutubedto = new YoutubeDto (); List <String> videoIdList = new List <String> (); for (var result in ytResult) { videoIdList.add (result.id); String json = jsonEncode (youtubedto.yApitoJson (result)); preferences.setString (result.id, json); } preferences.setStringList ("VideoIdList", videoIdList); } // add the questions to the previously saved youtube objects loadLocalDataAndAddQuestion () async { SharedPreferences preferences = await SharedPreferences.getInstance (); List <String> videoIDList = preferences.getStringList ("VideoIdList"); allYoutubeVideoList = new List <YoutubeDto> (); int cntQuestion = 0; for (String videoID in videoIDList) { String jsonYoutube = preferences.getString (videoID); Map userMap = jsonDecode (jsonYoutube); YoutubeDto youtubeDao = new YoutubeDto.fromJson (userMap); youtubeDao.questionJson = remoteConfig.getString ("_" + videoID.replaceAll ("-", "_trattino_")); if (youtubeDao.questionJson! = null && youtubeDao.questionJson.isNotEmpty) { cntQuestion = cntQuestion + 'question'.allMatches (youtubeDao.questionJson) .length; youtubeDao.playlist = json.decode (youtubeDao.questionJson) ['Playlist']; } allYoutubeVideoList.add (youtubeDao); } preferences.setInt ("TotQuestion", cntQuestion); } } Below you can see an example of how it works: