Mi sono accorto che molte app per la produttività sono diventate troppo complesse. Io volevo solo un timer, qualcosa che mi aiutasse a concentrarmi senza aggiungere altre distrazioni.
Così, nel tempo libero, ho creato Popomo: una piccola app timer basata sulla tecnica Pomodoro. Niente account, niente notifiche inutili. Solo un timer essenziale e un’interfaccia pulita per aiutarmi a lavorare meglio.
Non è un progetto “ambizioso”, ma una piccola app senza troppe pretese. L’ho aggiornata di recente per renderla ancora più semplice da usare. Se ti capita di lavorare o studiare con il metodo Pomodoro, magari può esserti utile anche a te.
Fammi sapere cosa ne pensi! Ogni consiglio e feedback è prezioso.
Per chi non lo sapesse, una game jam è una competizione amichevole online, in cui, dato un tempo limite ed un determinato tema, si ha come obiettivo quello di creare un videogioco seguendo le regole della competizione.
La Game Jam di Giugno é durata 2 settimane e l’obiettivo era quello di creare un videogioco con il look and feel di un gioco NES (meglio noto come Nintendo 8 bit ).
Progettando tutto quanto, per poter ricreare le sensazioni di quella piattaforma, ho ricercato un po’ nel dettaglio quali fossero le varie limitazioni che tale dispositivo aveva.
Boh che dire, ce ne sono tantissime, ma oggi volevo condividervela una in particolare.
A cosa vi servirà saperla? Quasi sicuramente a niente, ma per alcuni potrebbe risultare interessante!
Limitazioni Nintendo 8 bit
Il nostro caro vecchio NES (Famicon per i nostri amici Giapponesi) aveva una risoluzione della bellezza di ben 250×240 pixel (per i meno esperti informatici, fate conto che il full hd degli schermi di oggi ha come risoluzione 1920×1080).
Per poter mostrare le immagini sullo schermo, i programmatori le disegnavano su una sorta di “scacchiera” immaginaria, in cui su un quadrato potevano disegnarci un immagine.
Questa tela quadrata viene chiamata “Tile”, mentre l’intera scacchiera è chiamata “Tilemap”.
Le immagini mostrate avevano la dimensione di una Tile che, normalmente, era di 8×8 (o 8×16 tipo per i personaggi alti, a seconda del tipo di gioco). Se si voleva mostrare immagini più grandi (una colonna alta, un edificio ecc..) veniva scomposta quella immagine in tante piccole immagini grandi quanto un Tile e poi venivano disegnate accanto, unite.
Il fardello dei colori
Fatta questa premessa, posso ora raccontarvi della limitazione protagonista di questo post: i colori.
Nintendo 8 bit, oltre ad avere una limitata palette di colori(256 incluse le trasparenze; 240 senza) aveva un altra limitazione ENORME.
Avete presente le Tile, quelle di cui vi ho parlato poco fa?
Ebbene, ogni Tile poteva avere massimo 4 colori, e la trasparenza conta come colore. Il nostro protagonista del gioco che muoviamo, oltre ad avere uno slot di colore occupato dalla trasparenza, aveva ancora ben 3 colori per poter essere disegnato.
Qualsiasi persona che ha visto anche solo un disegno o un immagine nella propria vita, può capire che disegnare un “possente guerriero” con solo 3 colori, dentro una tela di 8×8/8×16 quadratini era una bella sfida.
Oltre a renderlo riconoscibile, bisognava anche renderlo distinguibile dagli altri elementi che si trovavano attorno a lui (lo sfondo, gli oggetti attorno a lui). Se riguardate un gioco NES e fate caso a queste limitazioni, vi potete subito accorgere di quante finezze e trucchetti di design ogni singolo gioco aveva.
Nelle fasi finali di vita del NES, i programmatori avevano padroneggiato alla perfezione questa console, scoprendo tantissimi metodi di ottimizzazione.
Riuscirono, alla fine, anche a trovare un modo per disegnare un personaggio con potenzialmente 6 Colori (WHOAAAAAAAA): Disegnando 2 immagini una sopra l’altra sulla stessa Tile, e usando la trasparenza su tutte e due, potevano creare una SUPER immagine con la infinita potenza di 6 colori!!
Grandi poteri, grandi responsabilità
Ovviamente, come ogni super potere, richiede un costo… Grandi poteri, grandi responsabilità (per i programmatori): Avere 2 immagini sulla stessa Tile, per il nostro Nintendino, consisteva in uno sforzo IMMANE, tremendo. Il vero eroe qui non era il nostro stupendo eroe guerriero di 6 colori, ma bensì il nostro Nintendo che, nell’ombra, reggeva tutto il teatrino al costo della sua stessa vita.
Come poter usare questo super potere in modo giusto, senza far scoppiare il nostro “Entertainment System” ? Limitandone l’uso in rarissimi casi: Disegnare l’eroe protagonista, mentre tutto il resto rimaneva con solo 3 miseri colori (Plebei, PUAH). In rari casi veniva usato per momenti speciali, quali boss finali o eventi mega fighi, ma sempre centellinandolo.
Limitazioni Nintendo 8 bit: Tanti colori, tante responsabilità.
La mia nuova applicazione, Proday, disponibile ora per dispositivi Android sul PlayStore!
Ho appena rilasciato un nuovo progetto, un applicazione con lo scopo di aiutare le persone a non sprecare le giornate. Quante volte siete andati a letto, pensando di non aver combinato niente per tutta la giornata? Bastano poche attività per cambiare. Non importa la quantità di tempo impiegata, l’importante è fare qualcosa per voi stessi. Che sia qualcosa per il benessere fisico, apprendimento, o solamente quella piccola commissione che state rimandando da giorni.
Personalmente, mi è capitato molte volte, la sera, prima di addormentarmi, di avere questo pensiero in testa. Proprio per questo ho voluto creare questa applicazione. Basta una piccola spinta durante il giorno per tenerti motivato.
Rendi la tua giornata produttiva, completando almeno 3 attività al giorno. Che siano attività per il benessere fisico, intellettuali, personali poco importa! Tieni d’occhio il progresso della giornata grazie alla notifica di Proday. Visualizza una lista di spunti ed idee per darti quella piccola spinta che ti serve!
Al momento sono presenti le funzionalità basilari, ma verrà aggiornata mensilmente, grazie ai vostri feedback! Essi sono estremamente importanti per me, permettendomi di migliorare, oltre che me stesso, anche la applicazione. Che ne pensate? Fatemi sapere!
Oggi voglio parlare con voi di un aspetto che ogni sviluppatore, sia mobile che web, ha a che fare ogni giorno. Scrivere ed organizzare i propri log applicativi è importantissimo. Ancora più importante è offuscarli negli ambienti di produzione, sia per questioni di pulizia che per questioni di sicurezza (loggare lo stato di chiamate http con tutti i parametri, quali username, password o secret key ad esempio). Una libreria che ha reso questa gestione più serena in Android è Timber, ed oggi voglio parlarvene!
In Android la logica di decidere quali log tenere in produzione o meno è responsabilità del programmatore. Per un gran periodo della mia carriera, avevo creato una classe “LogUtils” che funzionava da intermediario con il mio codice e la classe android.util.Log. Essa conteneva tutti i metodi per loggare come la relativa classe nativa, ma prima di scriverli controllava se la costante d’ambiente BuildConfig.DEBUG fosse positiva. In caso contrario, non scriveva il log.
Questa grezza logica riusciva si a fare il proprio lavoro, ma non mi dava molte libertà. In alcuni casi mi poteva far comodo tenere dei log in produzione. Inoltre, dopo anni di sviluppo (e forse anche voi!), comincio ad infastidirmi verso alcuni processi ripetitivi che noi sviluppatori effettuiamo ogni giorno: Ogni volta che dovevo scrivere un log su una classe nuova, la famosissima ed irritante variabile statica TAG mancava ed il perdere quei 10 secondi ogni volta mi urtava il sistema nervoso.
Per tutte queste problematiche ci viene incontro Timber, una libreria Android che ci facilita la scrittura e la logica dei nostri log!
Android Timber, che cosa è
Timber è una piccola libreria Android che contiene dei metodi che facilitano la gestione dei log all’interno dell’applicazione. è possibile infatti “piantare” Timber all’inizio dell’applicazione, e decidere quali log devono essere stampati o meno. Potrete così decidere di offuscare determinati log con la build di produzione e invece stampare qualsiasi informazione in quella di debug. La logica con cui vengono oscurati o meno i log è decisa interamente da voi, dandovi così la più completa libertà, insieme alla semplicità di utilizzo.
Implementazione ed uso di Timber in un applicazione Android
Per importare la libreria nel vostro progetto basterà aggiungere l’implementazione nel vostro gradle:
implementation 'com.jakewharton.4.7.1'
Vi consiglio di controllare la guida nella pagina di Github poichè il numero di versione potrebbe essere aggiornato. Sincronizzate il progetto ed ecco fatto, siete pronti per “piantare” i vostri log!
Per configurare Timber andrà “piantato” un “albero” (Tree), vale a dire creare una istanza. Di solito il posto migliore è nel OnCreate della vostra classe Application:
public class ExampleApp extends Application {
@Override public void onCreate() {
super.onCreate();
if (BuildConfig.DEBUG) {
Timber.plant(new DebugTree());
} else {
Timber.plant(new CrashReportingTree());
}
}
}
Questo esempio “pianta” un DebugTree in caso si tratti di una build di Debug. In caso di build di produzione, invece, Timber crea un CrashReportingTree.
DebugTree è una classe che si trova all’interno della libreria che permette di loggare tutto. In ambiente di Debug è molto importante loggare qualsiasi cosa, per agevolare sia lo sviluppo che il bug fixing. In produzione la situazione è molto diversa, vogliamo che i log siano il meno possibile, per motivi di sicurezza.
Ogni applicazione può necessitare diverse logiche di log in produzione: Alcune non vogliono loggare niente, mentre altre solo i log di livello ERROR o di determinate classi. Per avere il pieno controllo sarà necessario creare una classe che estende Timber.Tree ed gestire la logica all’interno del metodo log:
/** A tree which logs important information for crash reporting. */
private static class CrashReportingTree extends Timber.Tree {
@Override protected void log(int priority, String tag, @NonNull String message, Throwable t) {
if (priority == Log.VERBOSE || priority == Log.DEBUG) {
return;
}
FakeCrashLibrary.log(priority, tag, message);
if (t != null) {
if (priority == Log.ERROR) {
FakeCrashLibrary.logError(t);
} else if (priority == Log.WARN) {
FakeCrashLibrary.logWarning(t);
}
}
}
}
In questo caso, qualsiasi log di livello VERBOSE o DEBUG viene completamente ignorato, mentre gli altri vengono gestiti da una logica personalizzata dell’applicazione.
Iniziamo a Timber – loggare!
Ora che abbiamo configurato la nostra libreria, possiamo iniziare a loggare! La procedura di log è semplicissima! Timber ha gli stessi metodi statici della classe Log di Android, basterà sostituire “Log.METODO” con “Timber.METODO” (Timber.d, Timber.w, Timber.v e così via):
Timber.d("Ciao, sono un log!")
TAG ? No grazie!
Eccoci qui . ad una grande funzionalità di questa libreria! Siete stanchi, ogni volta che loggate in una nuova classe, di creare la solita variabile statica privata TAG? Nessun problema! Richiamando i metodi di Timber, esso riuscirà ad intuire da quale classe è stato chiamato il metodo e metterà in automatico il nome della classe. Un problema in meno!
Nel caso invece si voglia mettere un TAG personalizzato, è sempre possibile farlo con il seguente modo:
Timber.tag("TagPersonalizzato").d("Ciao sono un log con un tag personalizzato!");
Concatenazione di Stringhe e variabili
Nel caso in cui il testo dei log contiene stringhe e variabili (la maggior parte dei casi), non sarà necessario scrivere “stringa” + variabile, oppure usare String.format(). Timber gestisce la formattazione in maniera automatica:
Timber.d("Ciao, mi chiamo %s %s", firstName, lastName);
Un’ altra funzionalità che ci permettere di perdere meno tempo nella scrittura di log!
Lint, che passione!
All’interno di Timber sono gestite delle regole Lint che ci mostreranno dei warning che potremmo risolvere manualmente o che verranno automaticamente risolte dalla libreria:
Numero incorretto di argomenti
Argomenti di tipo diverso da quello specificato
Lunghezza dei TAG superiori a 23 (lunghezza massima di Android)
Metodi che usano ancora Log.* invece di Timber.*
Concatenazione di stringhe al posto della formattazione automatica di Timber
Uso di log con messaggio nullo o vuoto. Notifica anche i log che contengono come messaggio solo un eccezione
Tutti questi warning possono essere automaticamente risolti da Timber cliccando sulla lampadina gialla.
Spero che il mio articolo vi abbia incuriosito, vi lascio qui il link della pagina di Github. Il mio consiglio è quello di provarlo, la sua semplicità ed immediatezza ha fatto scattare in me amore a prima vista!
Oggi vi voglio parlare di un componente della raccolta Jetpack di Android, “DownloadManager”, insieme ad una breve panoramica e guida del suo utilizzo. Potrete consultare un esempio completo attraverso questo repository di Github. Gli snippet di codice successi saranno presi da questo progetto. Inoltre, è disponibile su PlayStore l’applicazione di prova che ho creato appositamente come supporto a questo articolo.
Guida DownloadManager: Di cosa tratta?
DownloadManager è un servizio di sistema Android che gestisce i download HTTP di lunga/breve durata. Non richiede implementazioni di librerie aggiuntive, poiché si trova all’interno del’ SDK di Android. Le applicazioni possono richiedere a questo servizio di scaricare un Uri e di salvarlo in uno specifico percorso, che sia privato dell’applicazione o pubblico.
DownloadManager si occuperà infine di gestire il processo in background di download, delle interazioni HTTP e ripartirà dall’inizio in caso di fallimento o di riavvio del dispositivo.
android.permission.INTERNET Necessario per le applicazioni per accedere ad internet.
(OPZIONALE) android.permission.DOWNLOAD_WITHOUT_NOTIFICATION Necessario per usare DownloadManager senza rendere visibile la notifica (DownloadManager.Request.VISIBILITY_HIDDEN) di download del file. Nel caso si lasciasse la notifica visibile, tale permesso non è necessario.
Come potete vedere, l’utilizzo di questo componente non prevede l’utilizzo di particolari permessi (soprattutto quelli catalogati da Android come “pericolosi”).
Come richiedere un download
Per prima cosa, è necessario creare una istanza della classe DownloadManager. Come tutti i servizi di sistema Android, non viene creata un istanza dal costruttore ma viene richiesta dal contesto:
val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
Ora che abbiamo creato l’oggetto, possiamo inviargli una richiesta di download, creando un oggetto di classe DownloadManager.Request. Questa classe ha l’obiettivo di contenere tutte le informazioni necessarie a DownloadManager per poter gestire il download di un File:
private fun generateDownloadRequest():DownloadManager.Request{
//Creiamo un oggetto DownloadManager.Request e
//gli passiamo l'url per il download del file
val request:DownloadManager.Request = DownloadManager.Request(Uri.parse("http://url_file.pdf"))
//Impostiamo il titolo della notifica che verrà visualizzata
request.setTitle(txt_notification_title.editText?.text.toString())
//Impostiamo la descrizione della notifica che verrà visualizzata
request.setDescription(txt_notification_description.editText?.text.toString())
//impostiamo la destinazione del file, in questo caso sarà nella cartella privata dell'applicazione "Downloads". Il nome del file sarà testFile
request.setDestinationInExternalFilesDir(this, Environment.DIRECTORY_DOWNLOADS,"testFile")
//impostiamo se il download ha il permesso di usare il traffico della rete mobile
request.setAllowedOverMetered(true)
//impostiamo se il download ha il permesso di scaricare anche in caso fossimo in roaming
request.setAllowedOverRoaming(true)
//Build.VERSION_CODES.N o maggiore: Impostiamo se il
//download deve partire solo se il dispositivo è in carica
request.setRequiresCharging(false)
//Build.VERSION_CODES.N o maggiore: Impostiamo se il
//download deve partire solo se il dispositivo è in
//modalità Idle (inattivo)
request.setRequiresDeviceIdle(false)
//Impostiamo se si visualizza o meno la notifica di download
//
//DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED
//Notifica visibile sia durante che a completamento del download
//
//DownloadManager.Request.VISIBILITY_HIDDEN
//Mai visualizzata. Ricordarsi di aggiungere al manifest il permesso
//android.permission.DOWNLOAD_WITHOUT_NOTIFICATION
//
//DownloadManager.Request.VISIBILITY_VISIBLE
//Notifica visibile solo durante il download. A download completato
//scompare
//
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
return request
}
In questo esempio ho cercato di includere tutti i vari parametri che vi può essere utile modificare, con relativa descrizione nei commenti,
Una volta creata la richiesta, sarà possibile passarla al DownloadManager in questo modo:
val id:Long = downloadManager.enqueue(request)
Molto importante è il valore restituito da DownloadManager.enqueue: Si tratta dell’identificativo che il sistema ha assegnato alla richiesta. Sarà importante per capire quando il download è terminato (Sia nel bene che nel male).
Intercettare il completamento del download
Per capire se il download richiesto è terminato è necessario registriare un receiver nel manifest (oppure anche runtime):
Le azioni registrate ed inviate da DownloadManager sono:
android.intent.action.DOWNLOAD_COMPLETE Invia questo evento quando un Download è stato completato.
android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED Invia questo evento quando l’utente clicca sulla notifica mentre il download è ANCORA in corso.
import android.app.DownloadManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
import android.widget.Toast
import com.danielebachicchi.jetpackdownloadmanagerexample.utils.DownloadManagerUtils
import com.danielebachicchi.jetpackdownloadmanagerexample.item.DownloadObject
class DownloadManagerReceiver: BroadcastReceiver() {
override fun onReceive(p0: Context?, p1: Intent?) {
val action:String? = p1?.action
var downloadID:Long = -1
if(DownloadManager.ACTION_DOWNLOAD_COMPLETE == action){
//ritirare l'id del download completato
downloadID = p1.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID,-1)
val downloadManager:DownloadManager = p0!!.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
val downloadObject: DownloadObject? =
DownloadManagerUtils.queryDownloadManagerObject(
downloadManager,
downloadID
)
//Ora abbiamo l'oggetto con tutte le
//informazioni del download, potete
//fare tutto quello che volete!
}else if (DownloadManager.ACTION_NOTIFICATION_CLICKED == action){
downloadID = p1.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID,-1)
//Gestite il caso in cui la notifica del
//download di downloadID è stata cliccata
//dall'utente
}
}
}
fun queryDownloadManagerObject(downloadManager:DownloadManager, id:Long): DownloadObject?{
val cursor:Cursor = downloadManager.query(DownloadManager.Query().setFilterById(id))
var result: DownloadObject? = null
if (cursor.moveToFirst())
result = DownloadObject.fromCursor(cursor)
return result
}
DownloadManager.query ritorna un Cursor con le informazioni del download richiesto (tramite l’id dell’oggetto, passato da DownloadManager alla richiesta del download). Se ritorna almeno un risultato si procede alla conversione del Cursor ad oggetto:
import android.app.DownloadManager
import android.database.Cursor
import java.util.*
class DownloadObject (val uri:String,
val localUri:String,
val bytesDownloaded:Long,
val bytesTotal:Long,
val id:Long,
val title:String,
val description:String,
val date:Date,
val mediaProviderUri:String?,
val mediaType:String,
val reason:String,
val status:Int) {
companion object{
/*
--- DownloadManager Row Example ---
_id : 45
mediaprovider_uri : null
destination : 2
title : Download Test
description : Downloading a file for testing Download Manager API
uri : http://ipv4.download.thinkbroadband.com/10MB.zip
status : 200
hint : null
media_type : application/zip
total_size : 10485760
last_modified_timestamp : 1565972803944
bytes_so_far : 10485760
allow_write : 0
local_uri : content://downloads/all_downloads/45
reason : placeholder
--- End Row ---*/
fun fromCursor(cursor:Cursor): DownloadObject {
val uri = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_URI))
val localUri = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI))
val bytesDownloaded = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR))
val description = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_DESCRIPTION))
val id = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_ID))
val date = Date(cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_LAST_MODIFIED_TIMESTAMP)))
val mediaProviderUri:String? = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_MEDIAPROVIDER_URI))
val mediaType = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_MEDIA_TYPE))
val reason = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_REASON))
val status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))
val title = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_TITLE))
val bytesTotal = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES))
return DownloadObject(
uri,
localUri,
bytesDownloaded,
bytesTotal,
id,
title,
description,
date,
mediaProviderUri,
mediaType,
reason,
status
)
}
}
override fun toString(): String {
return "DownloadObject(\nuri='$uri'\nlocalUri='$localUri'\nbytesDownloaded=$bytesDownloaded\nbytesTotal=$bytesTotal\nid=$id\ntitle='$title'\ndescription='$description'\ndate=$date\nmediaProviderUri=$mediaProviderUri\nmediaType='$mediaType'\nreason='$reason'\nstatus=$status\n)"
}
}
Questo è un oggetto di utility creato da me con l’obiettivo di racchiudere tutte le informazioni che DownloadManager possiede per un determinato download, con relativo metodo di wrapping dal Cursor.
Guida DownloadManager: il repository Git e l’applicazione di esempio!
Vi consiglio caldamente di consultare il codice del repository Github o scaricare l’applicazione esempio direttamente dal PlayStore (lasciatemi qualche stella se vi è stata almeno un poco utile e lasciate un commento con il vostro parere, mi piacerebbe sentire la vostra opinione)
Download Manager Exploration, disponibile sul Play Store!
Download Manager Exploration, disponibile sul Play Store!
Anche oggi è tutto, fatemi sapere se questa guida del componente Android DownloadManager vi è stata utile!
Oggi vi voglio parlare su come organizzare un repository git per i vostri progetti, spiegandovi la mia metodologia. I miei progetti sono quasi esclusivamente applicazioni mobile, ma questa struttura si può estendere a qualsiasi tipo di applicativo.
Il mio metodo
Master
Questo è il branch principale di produzione. Ne esiste solo un per progetto. In questo branch i commit rappresentano le versioni dell’applicativo pubblicate in produzione. In aggiunta aggiungo anche un tag che identifica la versione, per una rilettura comoda e rapida. Se sono presenti delle anomalie nelle versioni in produzione che hanno bisogno di essere corrette nel più breve tempo possibile, creo un branch dal commit del master come “Hotfix”.
Release
Quando decido di portare tutti gli avanzamenti del develop in produzione, creo un branch denominato “release_x.y.z” (dove x.y.z è la prossima versione dell’applicativo). Qui vengono eseguite delle modifiche leggere che riguardano l’aggiornamento della versione. Di solito è il cambio della versione e del build number. Una volta pronti per andare in produzione, unisco questo ramo sia nel “master”(taggando il numero di versione) che nel “develop”, in modo tale da avere un punto sincronizzato trai i rami.
Develop
Questo è il ramo dello sviluppo, il campo di battaglia! Ne esiste solo di uno per progetto. Da qui vengono creati i rami per le nuove funzionalità e sviluppi. Io tendo, quando possibile, a creare un branch per ogni funzionalità che voglio implementare (partendo da develop). Ci possono essere dei commit che non prevedono la creazione di un ramo, ma sono modifiche molto lievi, quali pulizia del codice, aggiunta di qualche commento e similari.
Feature
Una volta pianificato e progettato lo sviluppo di una nuova funzionalità, creo un ramo, partendo da develop con il nome “feature_IDENTIFICATIVO-FEATURE”, dove IDENTIFICATIVO-FEATURE è, per l’appunto, l’identificativo della funzionalità. In caso di applicazione progettate e coordinate su JIRA, per esempio, è l’identificativo della storia relativa allo sviluppo (vi consiglio di integrare GIT sul progetto JIRA, la quale permette la creazione, direttamente dalla dashboard, del branch relativo alla storia stessa).
Hotfix
Questi rami vengono creati quando qualcosa va irrimediabilmente storto. Se sono presenti dei bug in produzione sfuggiti ai test, al quale bisogna porre rimedio subito (interrompendo qualsiasi sviluppo in corso), occorre creare un ramo “hotfix_IDENTIFICATIVO-HOTFIX” (come in develop, identificativo della storia in caso di JIRA od un altro che sia leggibile al volo). Essendo una “toppa” da mettere all’applicazione in produzione, bisogna implementare solo gli sviluppi necessari alla risoluzione, senza incorporare niente del ramo develop che potrebbe essere ancora in sviluppo od in test. Questo ramo, quindi, verrà creato dal master ed, una volta terminato e pubblicato, esso sarà unito sia al ramo master che a quello di develop. Questo è l’unico caso che creo un ramo partendo dalla produzione.
I vantaggi di organizzare un repository git
So cosa state pensando, organizzare un repository git visti i tempi stretti di uno sviluppo tutta questa procedura può occupare un po’ troppo tempo inutilmente. Per esperienza personale, anche se ad una prima occhiata può sembrare tale, sono in disaccordo. Sia che si lavoro come persona singola che si lavori all’interno di un team, capiteranno SEMPRE situazioni in cui siamo costretti a saltare tra una funzionalità ed un altra, risolvere problemi imprevisti o ricompilare versioni vecchie o di test. Nel caso non si pianifichi una logica strutturata di GIT, arriverete ad un punto in cui vi potrebbe essere problematico e/o rischioso passare da un commit ad un altro. Strutturando i rami in questo modo io ho la sicurezza di poter cambiare lo stato del codice senza nessun effetto collaterale ed ho una lettura chiara e precisa dell’andamento evolutivo del progetto.
Spero che la mia esperienza su come organizzare un repository git vi sia stata utile! E voi, come strutturate il vostro repository?
Vi devo confessare una cosa. Sono un amante della pixel art. Il fascino retrò degli anni 80, suscita in me sempre grandi emozioni. Implementare la Pixel Art in Android nelle proprie applicazione è semplice, ma bisogna utilizzare qualche piccola accortezza affinché le proprie immagini non vengano deformate.
Antialiasing: L’incubo della pixel art
da quando la tecnologia si è evoluta, la nemesi della pixel art è sempre stata l’Antialiasing. L’Antialiasing, fatta eccezione per la pixel art, è una cosa positiva.
Quando un immagine dalle piccole dimensioni viene ridimensionata in una più grande, viene applicata, nella maggior parte dei casi, una tecnica per impedire l’ aliasing (“Gradinatura”, “Scalettatura”): esso consiste nell’ammorbidire le linee smussandone i bordi e migliorando l’immagine. La pixel art si basa invece sulla “Scalettatura”. Essa è improntata sul far vedere all’utente i pixel che realizzano l’opera. L’antialiasing, applicata su un immagine in pixel art, la trasforma come se fosse patinata (effetto miope senza occhiali per essere più precisi!), creando un forte disagio all’osservatore.
Dithering
Il dithering è una tecnica che aggiunge all’immagine del “rumore”, minimizzando eventuali distorsioni causate dal ridimensionamento dell’immagine. In realtà questa tecnica viene usata anche per disegnare in pixel art. La pixel art si basa sul colorare il singolo pixel di un determinato colore. Come è quindi possibile in un immagine creare delle sfumature? “Ingannando” l’occhio umano con dei “pattern” (disposizione in un certo ordine dei pixel) che simulino questa graduazione:
Esempi di vari pattern di dithering, usati per creare un effetto sfumatura.
Finché il Dithering è un processo deciso volontariamente dall’autore, nessun problema. Quando il dithering è generato automaticamente dai computer per scalare un immagine, può creare delle mutazioni che rendono l’immagine deformata. Un immagine in pixel art è creata generando delle simmetrie precise tra i vari pixel. Un solo pixel modificato può rovinare l’intera opera.
Visualizzare Pixel Art in Android: L’approccio sbagliato
Proviamo a visualizzare un icona in pixel art come se fosse un immagine come tante, utilizzando una ImageView. L’icona creata da me è di dimensioni 16×16, e la ridimensioneremo a 160dp:
Visualizzare un icona in pixel art in una ImageView senza nessun accorgimento
Come vedete, qualcosa non torna. L’immagine sembra opaca. Questo effetto è l’Antialiasing! Android tenta di smussare le linee, dato che abbiamo aumentato di dieci volte la sua reale dimensione.
Come visualizzare correttamente la pixel art in Android
Per visualizzare correttamente l’immagine, è necessario creare una Bitmap e disabilitare l’antialiasing su di essa. Creiamo nella cartella drawable un xml “sword_pixel_perfect.xml”:
antialias Questa variabile permette la disattivazione/attivazione della tecnica di antialiasing. Impostiamola a false.
dither Possiamo, con questa impostazione, disattivare i dithering.
filter FilterBitmap, se abilitato, esegue delle trasformazioni dell’immagine simile all’ antialiasing. Questo parametro si usa quando viene rotata un immagine, in modo tale da ammorbidire le linee.
Ora che abbiamo creato la giusta risorsa, non occorre altro che sostituire l’immagine dell’ ImageView con questo xml:
Sono felice di condividere con voi tutti una libreria che ho creato per la gestione della Camera su Android, sfruttando le nuove API messe a disposizione (Camera2): Camera 2 Helper Library.
Gestire tutto il flusso di apertura/chiusura della Camera è faticoso e ridondante. Ho pensato quindi di creare questa classe la quale, una volta inizializzata, pensa al “lavoro sporco”, permettendoti di focalizzarti sulle logiche successive all’acquisizione dell’immagine!
Cosa è Camera2 Helper library
Camera 2 Helper Library è una libreria per Android che facilita l’utilizzo delle API di Android SDK riguardanti Camera2. Gestisce tutto il flusso di apertura e chiusura della camera, la richiesta del permesso di utilizzo della camera e ti permette di richiamare dei metodi per la cattura dell’immagine. Richiede una versione minima Android SDK 23.
Una funzionalità aggiuntiva è la Live Mode che permette di processare in background le immagini della Camera in tempo reale. Utile nel caso abbiate bisogno di processare in real-time ciò che la camera sta attualmente vedendo. L’esempio più immediato del suo utilizzo è quello di rilevare automaticamente “Barcode” o “QRCode”: in questo caso la Live Mode fa al caso vostro! Essa vi fornirà, ad intervalli regolari impostati da voi, l’immagine di ciò che la camera sta rilevando.
Dove posso trovarla?
Attualmente Camera 2 Helper Library è in versione alpha ed è possibile visualizzare il codice e leggere la guida su come implementarla nelle proprie applicazione tramite questo link :
Ovviamente qualsiasi richiesta di implementazione o di risoluzione bug è benvenuta! Il mio obiettivo è quello di migliorarla e perfezionarla, in modo che possa essere di aiuto a molti sviluppatori (oltre che a me stesso :D) che hanno avuto un po’ di problemi durante l’utilizzo di queste API (che, secondo la mia opinione, non sono proprio molto intuitive :P)! Spero vi sia utile, fatemi sapere cosa ne pensate!
Da pochi giorni è stata resa disponibile la prima Developer Preview del nuovo Sistema Operativo Android “O” 8.Il nome ufficiale non è ancora reso pubblico, quale nuovo dolce il team di google sceglierà?
Andiamo ad analizzare insieme le prime novità per gli sviluppatori:
Android O: Novità
Limiti Background
Sono stati aggiunti ulteriori limiti automatici su cosa le applicazioni possono fare in background, al fine di ottimizzare la durata della batteria del dispositivo.Le aree toccante sono le seguenti:
Android O introduce i canali di notifica , nuove categorie definite all’interno della applicazione per i contenuti delle notifiche.Questa funzionalità permette agli sviluppatori di avere maggior controllo sulle notifiche aggregandole per categorie e modificando il proprio comportamento a livello di gruppo.Fino ad ora lo sviluppatore doveva cambiare il comportamento delle notifiche singolarmente.
Canale Notifiche Android O.
API Riempimento automatico
Android O aggiunge delle API pubbliche per il riempimento automatico dei valori immessi dall’utente (credenziali, informazioni, password…).L’utente infatti potrà scegliere un applicazione “Riempimento automatico” default per il sistema operativo.Gli sviluppatori che vogliono creare un applicazione di riempimento automatico avranno a disposizione queste API per poter sfruttare queste nuove feature.
Nuove funzionalità PIP
La modalità Picture in Picture di Android permette all’utente di poter continuare a guardare video mentre stanno rispondendo a un messaggio o mentre eseguono altre azioni.
Le applicazioni possono cambiare visualizzazione (dallo stato resume o pause dell’ activity) in modalità PIP quando il sistema operativo lo permette.Potranno così visualizzare le proprie schermate in modalità diversa se la PIP è attivata.
Font resources
Finalmente i fonti sono pienamente supportati all’interno dei xml resources.
Le applicazioni possono ora usare e definire font e famiglie di font negli xml layout.
Icone Adattive
è possibile creare delle icone adattive le quali verranno visualizzate dal sistema in diverse forme.Il sistema provvederà inoltre ad animarle e potranno essere usate nel launcher, nelle shortcuts, dialog e nella schermata di impostazioni.
Icone Adattive Android O.
Colori Wide-gamut per le applicazioni
Da Android O gli sviluppatori possono sfruttare i nuovi dispositivi che supportano i colori Wide-gamut.
Per poter visualizzare immagini in wide-gamut, le applicazioni dovranno abilitare tale funzionalità all’interno del manifest(per singola activity) e caricare una bitmap con un profilo di colori incorporato(AdobeRGB,Pro Photo RGB…).
Connettività
Android O porta miglioramenti dal punto di vista della connettività.
Il Sistema operativo ora supporta i bluetooth codec LDAC, mentre dal punto di vista del WiFI è stato aggiunto il Wi-Fi Aware, conosciuto precedentemente come NAN (Neighbor Awareness Networking) :
Le applicazioni installate nei dispositivi che hanno le caratteristiche hardware adatte potranno comunicare attraverso Wi-Fi senza la presenza di un accesso ad internet.
Navigazione con tastiera
Ritorna in vita la navigazione con tastiera, per poter supportare anche i SO Chrome OS che potranno installare le applicazioni del PlayStore.
AAudio API
Nuove API per le applicazioni che hanno l’esigenza di lavoro sull’audio in maniera più approfondita e a basso livello. AAudio permette infatti alle applicazioni di di leggere e scrivere data attraverso gli stream.
Miglioramenti WebView
è stata introdotta una nuova modalità opzionale che abilita la multiprocessassione all’interno delle webview, processando il contenuto web in un processo isolato.
In Android O è abilitata di default e sono state aggiunte delle API che permettono di gestire gli errori e i crash.
Java 8 API e ottimizzazioni runtime
Supporto per le API della nuova versione java e ottimizzato il processo di Android Runtime, con miglioramenti raddoppiati in alcuni benchmark.
è possibile scaricare l’immagine di Android O tramite questo link, tenendo presente che questa versione è pensata per un uso di testing e non per un uso quotidiano normale.
Raccomando di approfondire i cambiamenti dell’esecuzione in background delle app che potrebbe impattare in maniera pesante sulle vostre app se non analizzato in tempo.
In questo articolo voglio raccontarvi del nostro progetto, “Tower Breaker”, di come è stato sviluppato e pubblicato e di come il nostro fallimentare successo (e il fallimento dei progetti precedenti) sia stata una grande risorsa per la nostra crescita professionale .
Primi Progetti
Tutto è iniziato con l’obiettivo e il desiderio di creare un videogioco per dispositivi mobile. L’esperienza nel settore era minima, ma lo spirito e le intenzioni erano al massimo.
Creai un gruppo di 3 persone (me compreso) e iniziammo a buttare giù delle idee.
White Room
Scheda “White Room”: Il militare
White room era un progetto di un videogioco che su carta era molto interessante. Una ditta segreta , “Chimera Industries”, conduce degli esperimenti su esseri umani volontari. Al termine degli esperimenti la cavia sarebbe stata pagata molto profumatamente.L’esperimento di questo gioco era il seguente : Resistere in una stanza completamente bianca, senza nessun oggetto all’interno, per 40 giorni.
L’obiettivo del giocatore era resistere in questa stanza senza che il proprio personaggio impazzisca.
Cominciammo a buttare giù le basi del gameplay, le prime bozze e la struttura dell’applicazione.
Lavoravo come game designer, programmatore e coordinatore, insieme ad un illustratrice, graphic designer un sound designer.
Inviti progetto “White Room”
Partimmo velocemente, troppo velocemente. Creammo un sacco di immagini da far vedere al pubblico, andammo ad una fiera di sviluppo videogiochi indie vestiti da dottori con camice della “Chimera Industries” (un camice da medico con stampato il logo della Chimera Industries) e distribuimmo delle lettere con all’interno l’invito a participare come cavia agli esperimenti della Chimera Industries.
Purtroppo però, causa enormi errori di progettazione e la nostra inesperienza nel settore , il progetto venne abbandonato.
Tristi e depressi, cominciammo ad analizzare cosa era successo e quali erano stati i nostri errori.
Errori
Scheda White Room: Il Punk.
Questo fu un errore enorme. Iniziammo a progettare il gioco, le meccaniche, la struttura, gli assets, il sound in maniera veloce e grossolana.Partimmo con gli sviluppi senza aver finito il GDD (Game design document), ogni giorno ci svegliavamo con un idea nuova stravolgendo alcune basi stilate qualche giorno prima.Senza volerlo il progetto, nato piccolo, era diventato troppo grande e al di fuori della nostra portata.
Il colpo finale però arrivò più tardi. Creammo un prototipo (decisamente troppo tardi) e scoprimmo una cosa che non dovrebbe mai succedere ad uno stadio così avanzato del progetto : Il videogioco non era divertente. Il gameplay ,nato in un modo e con il corso del tempo era diventato un abominio.Creare una struttura divertente avrebbe richiesto una partenza da zero .Abbandonammo il progetto tristi e delusi.
Riassumendo, una brutta progettazione e l’inizio dello sviluppo prima di aver organizzato e dato un anima al gioco ci ha portato a creare un cosiddetto “Mostro di Frankenstein”,un ammasso di asset e codice che non aveva alcun senso.
GoToHeaven
Icona “Go to Heaven”
Elaborammo gli errori fatti col progetto precedente e iniziammo a lavorare al progetto successivo cercando di non fare gli stessi errori. Progettammo un platform 2D mobile, con protagonista un piccolo diavoletto arrabbiato che voleva uscire dagli inferi ed arrivare fino alle porte del paradiso con l’obiettivo finale di far casino.
La progettazione andò molto meglio, iniziammo a produrre il prototipo solo dopo aver buttato giù tutto il documento di progettazione. Creammo un prototipo molto velocemente.
Anche qui ci accorgemmo di una cosa : il gameplay non era divertente.
Il progetto fu abbandonato, stimammo un gioco formato da tantissime meccaniche buttate in questo pentolone senza avere un senso valido.L’errore principale fu l‘inesperienza di game design.
Cominciammo a studiare, ad informarci meglio sulle teorie di game design, tentando di creare qualcosa di divertente adatto ad uno sviluppo per un team con poca esperienza nel settore. Ci voleva un progetto semplice, piccolo, senza troppe pretese, un progetto che ci aiutasse a partire e a creare un team valido ed efficiente.
Il sound designer nel frattempo se ne andò e rimanemmo solo in due.
Il nostro fallimentare successo: Tower Breaker
Icona Tower Breaker.
Iniziammo a lavorare ad un piccolo progetto : Cercammo delle meccaniche già usate in giochi precedenti e semplici da realizzare. Avevamo bisogno di autostima e dovevamo finire un progetto per farci ridare la sicurezza che in tutti questi mesi avevamo perso.
Progettammo “Tower Breaker”,un piccolo gioco con poche meccaniche, semplice e di facile realizzazione. Il gameplay consiste nell’accoppiare mattoncini dello stesso colore. In basso ci sono 4 torri che l’utente può spostare lateralmente.Dall’alto cadono dei mattoncini colorati e fanno a finire sulle torri. Gameplay già visto e rivisto in molti giochi.
La progettazione fu discreta : avevamo le idee ben chiare di cosa e come il gioco doveva essere prodotto.Finimmo la progettazione e partimmo con gli sviluppi : Avevamo una lista di cose da fare con le relative scadenze molto precisa.Ci buttammo giù a sviluppare/creare a testa bassa senza nessun problema.
Questa buona organizzazione del progetto ci rese possibile lavorare allo sviluppo senza nessun problema.Ognuno aveva le idee chiare di cosa doveva fare e, soprattutto, il progetto non subì cambiamenti durante il corso della creazione.
In 20 giorni abbiamo creato il gioco e, presi dall’entusiasmo lo pubblicammo subito nell’AppStore e PlayStore.
Il gioco ha fatto pochissimi download ed è finito molto presto nel limbo degli store mobile.Non facemmo nessuna campagna pubblicitaria e questo segnò la morte di questo progetto.
Conclusioni
il nostro fallimentare successo
Nonostante questo eravamo carichi, motivati, felici e soddisfatti per questo fallimento. In effetti è stato tutto tranne che un fallimento.Riuscimmo a progettare, creare e pubblicare il nostro primo progetto.
Il completamento di Tower Breaker e il fallimento dei progetti precedenti ci ha conferito delle esperienze enormi in tutti gli ambienti del settore. I progetti futuri, infatti, sono stati organizzati, progettati e realizzati in maniera professionale e commettendo sempre meno errori.
Nell’ambito dello sviluppo videogiochi molti manuali citano questo concetto:
“I primi progetti che creerete saranno brutti e non divertenti, quindi muovetevi a crearli!”
Io adoro Tower Breaker, per me è stato un nostro fallimentare successo che ha arricchito tantissimo le mie esperienze come project manager, game designer, programmatore e come persona.
Di seguito il link per lo store Android, nel caso vogliate darci un occhio!
Voglio concludere citandovi una frase di Samuel Beckett che io tengo come promemoria per qualsiasi obiettivo e nella vita quotidiana :
“Ho provato, ho fallito. Non discutere. Fallisci ancora. Fallisci meglio” – Samuel Beckett