tag:blogger.com,1999:blog-33447217335780726612024-03-13T10:30:43.544-07:00debug is on the tableNelson Glauberhttp://www.blogger.com/profile/02476858089223420894noreply@blogger.comBlogger272125tag:blogger.com,1999:blog-3344721733578072661.post-49630126990935493372020-07-10T07:50:00.001-07:002020-07-10T07:50:16.548-07:00Estou no mediumOlá povo,<br />
<br />
Se você vem me seguindo aqui no blog, saiba que ele está abandonado porque migrei todas as postagens para o <a href="https://medium.com/@nglauber">Medium</a>.<br />
<br />
Então me sigam <a href="https://medium.com/@nglauber">por lá</a>!<br />
<br />
4br4ç05,<br />
nglauberNelson Glauberhttp://www.blogger.com/profile/02476858089223420894noreply@blogger.com0tag:blogger.com,1999:blog-3344721733578072661.post-26153139992345730422018-07-10T14:25:00.002-07:002018-07-10T14:27:46.306-07:00Como eu aprendi (um pouco sobre) RXJavaOlá povo,<br />
<br />
Sempre que eu vejo um artigo legal sobre algo que estou interessado e não tenho tempo de ler naquele momento, eu mando um email pra mim mesmo com o link para ler depois. Eu sei, isso antigo, mas eu sou velho e funciona pra mim ;)<br />
Resolvi compartilhar com vocês uma lista de links sobre RXJava que me ajudaram a entender um pouquinho sobre o assunto.<br />
<br />
Android MeetUp 3 Anos - Programação Reativa Funcional com RxJava (Ubiratan Soares)<br />
<a href="https://www.youtube.com/watch?v=0FpphC6hL5I">https://www.youtube.com/watch?v=0FpphC6hL5I</a><br />
<br />
Refactoring for RxJava (Ubiratan Soares)<br />
<a href="https://www.youtube.com/watch?v=391H38-7JYk">https://www.youtube.com/watch?v=391H38-7JYk</a><br />
<br />
Rxify - a simple spell for complex RxJava operators (Garima Jain)<br />
<a href="https://speakerdeck.com/ragdroid/rxify-a-simple-spell-for-complex-rxjava-operators">https://speakerdeck.com/ragdroid/rxify-a-simple-spell-for-complex-rxjava-operators</a><br />
<br />
Exploring RxJava 2 for Android (Jake Wharton)<br />
<a href="https://www.youtube.com/watch?v=htIXKI5gOQU">https://www.youtube.com/watch?v=htIXKI5gOQU</a><br />
<br />
5 Not So Obvious Things About RxJava (Jag Saund)<br />
<a href="https://medium.com/@jagsaund/5-not-so-obvious-things-about-rxjava-c388bd19efbc">https://medium.com/@jagsaund/5-not-so-obvious-things-about-rxjava-c388bd19efbc</a><br />
<br />
Migrating from RxJava 1.0 to RxJava 2.0 and Learn RxJava by Examples (Amit Shekhar)<br />
<a href="https://blog.mindorks.com/migrating-from-rxjava1-to-rxjava2-5dac0a94b4aa">https://blog.mindorks.com/migrating-from-rxjava1-to-rxjava2-5dac0a94b4aa</a><br />
<br />
An Introduction to Functional Reactive Programming (Dan Lew)<br />
<a href="http://blog.danlew.net/2017/07/27/an-introduction-to-functional-reactive-programming/">http://blog.danlew.net/2017/07/27/an-introduction-to-functional-reactive-programming/</a><br />
<br />
Grokking RxJava (Dan Lew)<br />
<a href="http://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/">http://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/</a><br />
<a href="http://blog.danlew.net/2014/09/22/grokking-rxjava-part-2/">http://blog.danlew.net/2014/09/22/grokking-rxjava-part-2/</a><br />
<a href="http://blog.danlew.net/2014/09/30/grokking-rxjava-part-3/">http://blog.danlew.net/2014/09/30/grokking-rxjava-part-3/</a><br />
<br />
How to make complex requests simple with RxJava in Kotlin (Tamás Kozmér)<br />
<a href="https://blog.mindorks.com/how-to-make-complex-requests-simple-with-rxjava-in-kotlin-ccec004c5d10">https://blog.mindorks.com/how-to-make-complex-requests-simple-with-rxjava-in-kotlin-ccec004c5d10</a><br />
<br />
Don't break the chain: use RxJava's compose() operator (Dan Lew)<br />
<a href="http://blog.danlew.net/2015/03/02/dont-break-the-chain/">http://blog.danlew.net/2015/03/02/dont-break-the-chain/</a><br />
<br />
A Complete Guide To Learn RxJava (Amit Shekhar)<br />
<a href="https://blog.mindorks.com/a-complete-guide-to-learn-rxjava-b55c0cea3631">https://blog.mindorks.com/a-complete-guide-to-learn-rxjava-b55c0cea3631</a><br />
<br />
Multithreading with RxJava (Pierce Zaifman)<br />
<a href="https://android.jlelse.eu/multithreading-with-rxjava-dadddc4f7a63">https://android.jlelse.eu/multithreading-with-rxjava-dadddc4f7a63</a><br />
<br />
Adopting RxJava on Airbnb Android (Felipe Lima)<br />
<a href="https://academy.realm.io/posts/kau-felipe-lima-adopting-rxjava-airbnb-android/">https://academy.realm.io/posts/kau-felipe-lima-adopting-rxjava-airbnb-android/</a><br />
<br />
Começando com ReactiveX no Android (Ashraff Hathibelagal)<br />
<a href="https://code.tutsplus.com/pt/tutorials/getting-started-with-reactivex-on-android--cms-24387">https://code.tutsplus.com/pt/tutorials/getting-started-with-reactivex-on-android--cms-24387</a><br />
<br />
Programação Reativa com RxAndroid (Pedro Di Moura)<br />
<a href="https://speakerdeck.com/pedrodimoura/programacao-reativa-com-rxandroid">https://speakerdeck.com/pedrodimoura/programacao-reativa-com-rxandroid</a><br />
<br />
Replace AsyncTask and AsyncTaskLoader with rx.Observable – RxJava Android Patterns (Ross Hambrick)<br />
<a href="https://stablekernel.com/replace-asynctask-and-asynctaskloader-with-rx-observable-rxjava-android-patterns/">https://stablekernel.com/replace-asynctask-and-asynctaskloader-with-rx-observable-rxjava-android-patterns/</a><br />
<br />
RxJava cheat sheet, with a pinch of Android (Oleg Shelajev)<br />
<a href="https://zeroturnaround.com/rebellabs/rxjava-cheat-sheet-with-a-pinch-of-android/">https://zeroturnaround.com/rebellabs/rxjava-cheat-sheet-with-a-pinch-of-android/</a><br />
<br />
RXJava2 by Example (Victor Grazi)<br />
<a href="https://www.infoq.com/articles/rxjava2-by-example">https://www.infoq.com/articles/rxjava2-by-example</a><br />
<br />
4br4ç05,<br />
nglauberNelson Glauberhttp://www.blogger.com/profile/02476858089223420894noreply@blogger.com2tag:blogger.com,1999:blog-3344721733578072661.post-17541719726992907872017-12-20T17:40:00.002-08:002017-12-20T17:40:26.362-08:00Retrospectiva 2017Olá povo,<br />
<br />
Nos últimos anos tenho procurado escrever um post com a retrospectiva do ano que passou e esse ano não poderia ser diferente. O ano de 2017 foi bem mais calmo do que os dois ou três anteriores.<br />
Em termos profissionais, nada mudou muito. Continuo na <a href="http://mokriya.com/">Mokriya</a>, muito satisfeito com o trabalho home-office. Tive apenas mudanças de projeto e de cliente, mas nada muito drástico. Inclusive eu escrevi esse post <a href="http://www.nglauber.com.br/2017/06/um-ano-de-trabalho-em-casa.html">aqui</a> falando um pouquinho do trabalho remoto.<br />
O que acelerou o ano foi a quantidade de palestras/eventos/viagens que fiz e que vou resumir rapidamente aqui pra vocês.<br />
<h3>
<b>Aulas</b></h3>
Em 2016 parei de dar aulas na graduação e agora estou apenas ministrando aulas (quando convidado) em disciplinas de pós-graduação ou <i>in-company</i>. Esse ano só ministrei uma turma de pós graduação na <a href="http://www.unifacisa.edu.br/">FACISA</a> em Campina Grande - PB. Turma muito boa e interessada.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-OlZYnEFd4ww/WjrjhO2muyI/AAAAAAAAzSg/acQS-et2XPMIiokVFmpAJ9xInSQnOSmFQCLcBGAs/s1600/IMG_20170603_115223.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" data-original-height="900" data-original-width="1600" height="225" src="https://4.bp.blogspot.com/-OlZYnEFd4ww/WjrjhO2muyI/AAAAAAAAzSg/acQS-et2XPMIiokVFmpAJ9xInSQnOSmFQCLcBGAs/s400/IMG_20170603_115223.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-size: x-small;">Turma de pós-gradução em Mobile da FACISA</span></div>
<br />
<h3>
<b>Palestras</b></h3>
Acho que esse foi o ano em que mais fiz palestras: <b>15</b> no total. Abaixo, listei os eventos com algumas fotos ;)<br />
<br />
<u>Android Meetup #6 GDG Recife</u> (13/02)<br />
Local: ThoughtWorks - Recife - PE<br />
Palestra: Desenvolvimento moderno de aplicações Android<br />
<br />
<u>Hangout GDG Rio de Janeiro</u> (25/05)<br />
Local: On-line<br />
Palestra: Constraint Layout<br />
<br />
<u>Aniversário de 4 anos do Meetup de Android do GDG São Paulo</u> (10/06)<br />
Local: Oxigênio Aceleradora - São Paulo - SP<br />
Palestra: The world of Android Animations<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-GI5PrpzaIWw/WjqS83A1twI/AAAAAAAAzQs/l2Epc9FP0-Ux4Kvlvz7owMIQqUOS6dcrQCLcBGAs/s1600/99-DSCF1606.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1067" data-original-width="1600" height="266" src="https://1.bp.blogspot.com/-GI5PrpzaIWw/WjqS83A1twI/AAAAAAAAzQs/l2Epc9FP0-Ux4Kvlvz7owMIQqUOS6dcrQCLcBGAs/s400/99-DSCF1606.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-size: x-small;">Android Meetup 4 anos GDG São Paulo</span></div>
<br />
<u>The Developer's Conference (TDC) São Paulo</u> (19 e 20/06)<br />
Local: Universidade Anhembi Morumbi - São Paulo - SP<br />
Palestra: Persistência de Dados no SQLite com Room<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-W1bd24tRPlk/Wjrcm-ydBzI/AAAAAAAAzQ8/hhfKPL3ehMc4OzVISC_VX_UwwqyW4P9xwCLcBGAs/s1600/IMG_20170720_144433.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1200" data-original-width="1600" height="300" src="https://2.bp.blogspot.com/-W1bd24tRPlk/Wjrcm-ydBzI/AAAAAAAAzQ8/hhfKPL3ehMc4OzVISC_VX_UwwqyW4P9xwCLcBGAs/s400/IMG_20170720_144433.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-size: x-small;">TDC São Paulo</span></div>
<br />
<u>GDG Recife Meetup I/O Recap</u> (10/07)<br />
Local: Centro de Informática da UFPE - Recife - PE<br />
Palestra: Introdução ao Kotlin para Android<br />
<br />
<u>GDG Recife Tech Tour</u> (22/07)<br />
Local: Armazém da Criatividade - Caruaru - PE<br />
Palestra #1: Turbinando o desenvolvimento Android com Kotlin<br />
Palestra #2: Persistência de Dados no SQLite com Room<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-d3HLnQDY9-k/WjrdD13gN1I/AAAAAAAAzRA/RE0OUW1NriUzluxwK6LiDcUauO_nChgbgCLcBGAs/s1600/IMG_20170722_191618.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="765" data-original-width="1024" height="298" src="https://1.bp.blogspot.com/-d3HLnQDY9-k/WjrdD13gN1I/AAAAAAAAzRA/RE0OUW1NriUzluxwK6LiDcUauO_nChgbgCLcBGAs/s400/IMG_20170722_191618.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-size: x-small;">GDG Recife Tech Tour - Caruaru</span></div>
<br />
<u>Google Launchpad Build</u> (05/08)<br />
Local: TECNOPUC - Porto Alegre - RS<br />
Palestra: Turbinando o desenvolvimento Android com Kotlin<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-nmti6aH3x1M/WjrdgyA1l1I/AAAAAAAAzRI/A39LWqpUIdY7v6L8cnyaGNsCzEFnNh1XgCLcBGAs/s1600/web-110.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1067" data-original-width="1600" height="266" src="https://3.bp.blogspot.com/-nmti6aH3x1M/WjrdgyA1l1I/AAAAAAAAzRI/A39LWqpUIdY7v6L8cnyaGNsCzEFnNh1XgCLcBGAs/s400/web-110.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-size: x-small;">Google Launchpad Build - PoA</span></div>
<br />
<u>Androidos</u> (11 e 12/08)<br />
Local: Unipê - João Pessoa - PB<br />
Palestra: Turbinando o desenvolvimento Android com Kotlin<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-mw50FKsdImI/WjreC8uKciI/AAAAAAAAzRQ/ejDbQotMR443LvVZ5XROI1PO7COihdGPwCLcBGAs/s1600/20863576_1824769860872220_5390046571072050267_o.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1067" data-original-width="1600" height="266" src="https://1.bp.blogspot.com/-mw50FKsdImI/WjreC8uKciI/AAAAAAAAzRQ/ejDbQotMR443LvVZ5XROI1PO7COihdGPwCLcBGAs/s400/20863576_1824769860872220_5390046571072050267_o.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-size: x-small;">Androidos - João Pessoa</span></div>
<br />
<u>Google Agency Program</u> (16/08)<br />
Local: Google - São Paulo - SP<br />
Palestra: Turbinando o desenvolvimento Android com Kotlin<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-xzv0DV-9lF0/WjrfMe5wFDI/AAAAAAAAzRY/D01H_XiSChE3RlpFMzTE3tBIC_LEbRj7wCLcBGAs/s1600/IMG_20170816_130159.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="225" src="https://4.bp.blogspot.com/-xzv0DV-9lF0/WjrfMe5wFDI/AAAAAAAAzRY/D01H_XiSChE3RlpFMzTE3tBIC_LEbRj7wCLcBGAs/s400/IMG_20170816_130159.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-size: x-small;">Google São Paulo</span></div>
<br />
<u>Android Dev Conference</u> (26/08)<br />
Local: Hotel Pullman - São Paulo - SP<br />
Palestra: Tudo que você precisa saber sobre Constraint Layout<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-1NMasVjwUlc/WjsMIvN4vBI/AAAAAAAAzTM/lgNKMcJo7M4wk0b0T86f51iiFeXCEW7uACLcBGAs/s1600/Palestrante%2BNelson%2BGlauber%2B-%2BMOKRIYA-%2BDEVAndroid%2B2017%2B-%2B24%2Be%2B25-08-2017%2B-%2BDSC5866.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="967" data-original-width="1600" height="241" src="https://2.bp.blogspot.com/-1NMasVjwUlc/WjsMIvN4vBI/AAAAAAAAzTM/lgNKMcJo7M4wk0b0T86f51iiFeXCEW7uACLcBGAs/s400/Palestrante%2BNelson%2BGlauber%2B-%2BMOKRIYA-%2BDEVAndroid%2B2017%2B-%2B24%2Be%2B25-08-2017%2B-%2BDSC5866.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-size: x-small;">Android Dev Conference</span></div>
<br />
<u>DevFest Maceió</u> (23/09)<br />
Local: Hotel Ritz Lagoa da Anta - Maceió - AL<br />
Palestra: Turbinando o desenvolvimento Android com Kotlin<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-tsfCkBF4LUY/WjrhqvVUoWI/AAAAAAAAzSA/Hc_mkNip_sUqqicIPO8QA_uKtx05Qd_6QCLcBGAs/s1600/IMG_20170923_143358.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1200" data-original-width="1600" height="300" src="https://3.bp.blogspot.com/-tsfCkBF4LUY/WjrhqvVUoWI/AAAAAAAAzSA/Hc_mkNip_sUqqicIPO8QA_uKtx05Qd_6QCLcBGAs/s400/IMG_20170923_143358.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-size: x-small;">DevFest Maceió</span></div>
<br />
<u>DevFest Cerrado</u> (30/09)<br />
Local: Espaço Equatore - Goiânia - GO<br />
Palestra: Turbinando o desenvolvimento Android com Kotlin<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-rhskZqoH4Vk/WjriOMGd9yI/AAAAAAAAzSM/cC7EjVBoWOkCYRuL63ruQ0rTv3YSqj9SgCLcBGAs/s1600/_MG_4046.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1068" data-original-width="1600" height="266" src="https://4.bp.blogspot.com/-rhskZqoH4Vk/WjriOMGd9yI/AAAAAAAAzSM/cC7EjVBoWOkCYRuL63ruQ0rTv3YSqj9SgCLcBGAs/s400/_MG_4046.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-size: x-small;">DevFest Cerrado</span></div>
<br />
<u>DevFest Paraná</u> (11/11)<br />
Local: SEBRAE - Maringá - PR<br />
Palestra: Turbinando o desenvolvimento Android com Kotlin<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-1kegc24gH7I/Wjri2nHRzTI/AAAAAAAAzSU/6xuBFVZpYkEcBuDNX-4qC04mUK9m8AIiQCLcBGAs/s1600/P_20171111_223515.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="225" src="https://3.bp.blogspot.com/-1kegc24gH7I/Wjri2nHRzTI/AAAAAAAAzSU/6xuBFVZpYkEcBuDNX-4qC04mUK9m8AIiQCLcBGAs/s400/P_20171111_223515.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-size: x-small;">DevFest Paraná</span></div>
<br />
<u>DevFest XP</u> (09/12)<br />
Local: InLoco - Recife - PE<br />
Palestra: Desenvolvimento moderno de aplicações Android<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-TYoGtv_94q4/WjrjE4tMOmI/AAAAAAAAzSY/Abhs4A-oIYczCLcvLHjnGxutp5UQhBTUgCLcBGAs/s1600/IMG_20171209_103145924.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1200" data-original-width="1600" height="300" src="https://3.bp.blogspot.com/-TYoGtv_94q4/WjrjE4tMOmI/AAAAAAAAzSY/Abhs4A-oIYczCLcvLHjnGxutp5UQhBTUgCLcBGAs/s400/IMG_20171209_103145924.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-size: x-small;">DevFest XP em Recife</span></div>
<br />
Os slides dessas palestras estão no meu <a href="http://slideshare.net/nglauber">slideshare</a> ;)<br />
<br />
<h3>
<b>Eventos</b></h3>
Participei de dois eventos sensacionais na Cracóvia (Polônia): o GDE Summit, que é o encontro anual dos GDEs (<i>Google Developer Experts</i>); e o Google Developer Days (GDD) Europe.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-CVb-bxUH7H0/WjrgEw08_5I/AAAAAAAAzRo/lA7w04JPh8MYL3uTuNtXd5bryE-wl7IggCLcBGAs/s1600/DSC_1021.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1067" data-original-width="1600" height="266" src="https://2.bp.blogspot.com/-CVb-bxUH7H0/WjrgEw08_5I/AAAAAAAAzRo/lA7w04JPh8MYL3uTuNtXd5bryE-wl7IggCLcBGAs/s400/DSC_1021.JPG" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="text-align: start;"><span style="font-size: x-small;">GDE Summit</span></span></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-mU3suVKlESk/WjrgzgUa4yI/AAAAAAAAzR0/c7RSkK3WBukLfC6ad_FATM0FfhYAOuMqwCLcBGAs/s1600/IMG_20170905_100606.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1200" data-original-width="1600" height="300" src="https://1.bp.blogspot.com/-mU3suVKlESk/WjrgzgUa4yI/AAAAAAAAzR0/c7RSkK3WBukLfC6ad_FATM0FfhYAOuMqwCLcBGAs/s400/IMG_20170905_100606.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="text-align: start;"><span style="font-size: x-small;">GDD Europe</span></span></div>
<br />
Aprendi muito nesses eventos e conheci e reencontrei muita gente bacana de todo o mundo por lá. Realmente foi uma experiência sensacional.<br />
<br />
<h3>
Passeios</h3>
Consegui duas mini-férias esse ano: uma em maio e outra dezembro, então deu para passear um pouquinho com minha esposa <3 p=""><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-M5oe1KHxmHU/WjrkG2eCupI/AAAAAAAAzSo/X1TNMjAX-_I5RzlnvQ9sUAn5i2nboOmpACLcBGAs/s1600/IMG_1615.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1200" data-original-width="1600" height="300" src="https://4.bp.blogspot.com/-M5oe1KHxmHU/WjrkG2eCupI/AAAAAAAAzSo/X1TNMjAX-_I5RzlnvQ9sUAn5i2nboOmpACLcBGAs/s400/IMG_1615.JPG" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="text-align: start;"><span style="font-size: x-small;">Universal Studios - Orlando - EUA</span></span></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-yLL42je0qIA/Wjr9YbhGBmI/AAAAAAAAzS8/B5y3xh8A4jc9bF3yzCGjDuIH86qh6jMpgCLcBGAs/s1600/IMG_20171212_165317404.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1200" data-original-width="1600" height="300" src="https://3.bp.blogspot.com/-yLL42je0qIA/Wjr9YbhGBmI/AAAAAAAAzS8/B5y3xh8A4jc9bF3yzCGjDuIH86qh6jMpgCLcBGAs/s400/IMG_20171212_165317404.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="text-align: start;"><span style="font-size: x-small;">Gramado - RS</span> </span></div>
<br />
Consegui realizar um sonho pessoal de conhecer Londres. Infelizmente só pude passar dois dias, mas foi fantástico e muito marcante. Espero voltar lá com mais tempo algum dia.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-WDm3re6ZUQI/WjrhTXsjUhI/AAAAAAAAzR4/YpymTDFvyu0bREtLzj9UztbbYJDXb2OrACLcBGAs/s1600/IMG_20170917_135419.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1200" data-original-width="1600" height="300" src="https://1.bp.blogspot.com/-WDm3re6ZUQI/WjrhTXsjUhI/AAAAAAAAzR4/YpymTDFvyu0bREtLzj9UztbbYJDXb2OrACLcBGAs/s400/IMG_20170917_135419.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="text-align: start;"><span style="font-size: x-small;">Tower Bridge - Londres - Inglaterra</span></span></div>
<br />
<h3>
Blog, Vídeos, Livro, ... </h3>
Infelizmente o blog ficou um pouco parado em 2017: foram apenas <b>5</b> posts. Entretanto, resolvi focar um pouco mais na criação de vídeos informais sobre coisas que estou estudando. Publiquei apenas <b>8</b> vídeos, mas que tiveram uma quantidade de visualizações muito maior do que os blogposts. Então provavelmente vou continuar trabalhando em mais vídeos para vocês ;)<br />
Em relação ao livro, eu realmente pretendia lançar uma nova edição do "Dominando o Android" no começo de 2018, mas não deu :( demandas altas de trabalho, o grande número de viagens e palestras me fizeram adiar a nova edição do livro para 2018. Mas fiquem ligados, que eu sempre postarei as novidades por aqui ou em minhas redes sociais.<br />
<br />
--<br />
<br />
That's all folks! Essa foi a retrospectiva de 2017! Foi mais um ótimo ano para mim, e espero que tenha sido para vocês também! Que 2018 seja ainda melhor para todos nós.<br />
<br />
4br4ç05,<br />
nglauber</3>Nelson Glauberhttp://www.blogger.com/profile/02476858089223420894noreply@blogger.com0tag:blogger.com,1999:blog-3344721733578072661.post-5166265567532010942017-08-14T11:55:00.003-07:002017-08-15T06:34:05.804-07:00Como eu aprendi Kotlin<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-g-BRBGfGlFE/WZHs9Fyp-NI/AAAAAAAAr4c/uyrvEwzZWVM5iKBm2fCV_expOBxcdMF-QCLcBGAs/s1600/kotlin.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="512" data-original-width="1024" height="200" src="https://3.bp.blogspot.com/-g-BRBGfGlFE/WZHs9Fyp-NI/AAAAAAAAr4c/uyrvEwzZWVM5iKBm2fCV_expOBxcdMF-QCLcBGAs/s400/kotlin.jpg" width="400" /></a></div>
<br />
Olá povo,<br />
<br />
Tenho ministrado palestras sobre Kotlin em vários lugares e uma pergunta muito comum que o pessoal vem fazendo é: <b>onde e como aprender Kotlin? </b><br />
Vou responder essa pergunta descrevendo como eu aprendi o que eu sei até agora.<br />
Meu primeiro contato com Kotlin foi no segundo semestre de 2016 com o <a href="http://antonioleiva.com/google-kotlin"><b>livro do Antonio Leiva</b></a> que me deu uma boa base da linguagem e me motivou a estudar mais sobre o assunto.<br />
Uma vez familiarizado com a linguagem, comecei a ler a <a href="http://kotlinlang.org/"><b>documentação do Kotlin</b></a> que é muito boa.<br />
E navegando pela documentação achei o <a href="http://try.kotlinlang.org/koans"><b>Kotlin Koans</b></a> onde você aprende diversos recursos da linguagem por meio de 42 desafios que podem ser feitos on-line ou por meio do <a href="https://blog.jetbrains.com/kotlin/2016/03/kotlin-educational-plugin/">Kotlin Educational Plugin</a>.<br />
<br />
Após essas etapas iniciais, li um monte de artigos (muitos bem similares) onde cada um foi agregando um pouco aos meus estudos. Então resolvi compartilhar com vocês os links. Qualquer dúvida, é só falar ;)<br />
<br />
Enjoy! :)<br />
<br />
<h3>
POSTS/SLIDES</h3>
<a href="https://www.toptal.com/android/kotlin-boost-android-development">Ten Kotlin Features To Boost Android Development</a><br />
<br />
<a href="https://medium.com/keepsafe-engineering/kotlin-the-good-the-bad-and-the-ugly-bf5f09b87e6f">Kotlin: The Good, The Bad, and The Ugly</a><br />
<br />
<a href="https://blog.mindorks.com/why-you-must-try-kotlin-for-android-development-e14d00c8084b">Why You Must Try Kotlin For Android Development ?</a><br />
<br />
<a href="https://hackernoon.com/5-small-things-you-probably-dont-know-about-kotlin-255261940de6">5 small things you probably don’t know about Kotlin</a><br />
<br />
<a href="https://hackernoon.com/how-effective-java-may-have-influenced-the-design-of-kotlin-part-1-45fd64c2f974#.w5cupmgzm">How “Effective Java” may have influenced the design of Kotlin</a><br />
<br />
<a href="https://speakerdeck.com/rafaeltoledo/kotlin-1-dot-1-o-que-vem-por-ai">Kotlin 1.1: o que vem por aí?</a><br />
<br />
<a href="https://blog.aritraroy.in/why-you-should-start-using-kotlin-to-supercharge-your-android-development-in-2017-61db1f11d666">Why You Should Start Using Kotlin to Supercharge Your Android Development in 2017</a><br />
<br />
<a href="https://medium.com/@macastiblancot/android-coroutines-getting-rid-of-runonuithread-and-callbacks-cleaner-thread-handling-and-more-234c0a9bd8eb">Android Coroutines with Kotlin: getting rid of runOnUiThread and Callbacks; cleaner thread handling; and more</a><br />
<br />
<a href="http://blog.triadworks.com.br/por-que-o-kotlin-existe">Por que o Kotlin foi criado?</a><br />
<br />
<a href="https://medium.com/google-developers/lessons-learned-while-converting-to-kotlin-with-android-studio-f0a3cb41669">Lessons learned while converting to Kotlin with Android Studio</a><br />
<br />
<a href="http://beust.com/weblog/2015/10/30/exploring-the-kotlin-standard-library/">Exploring the Kotlin standard library</a><br />
<br />
<a href="https://android.jlelse.eu/handsome-codes-with-kotlin-6e183db4c7e5">Handsome codes with Kotlin</a><br />
<br />
<a href="https://hackernoon.com/kotlin-a-deeper-look-8569d4da36f">Kotlin — A deeper look</a><br />
<br />
Palestras do Jake Wharton<br />
<a href="https://speakerdeck.com/jakewharton/advancing-android-development-with-the-kotlin-language-oredev-2015">Nov/2015</a><br />
<a href="https://speakerdeck.com/jakewharton/android-development-with-kotlin-androidkw-number-001">Dez/2015</a><br />
<a href="https://speakerdeck.com/jakewharton/10-kotlin-tricks-in-10-ish-minutes-devoxx-us-2017">Mar/2017</a><br />
<a href="https://speakerdeck.com/jakewharton/its-a-kotlin-kotlin-kotlin-world-london-android-meetup-july-2017">Jul/2017</a><br />
<br />
<a href="https://www.slideshare.net/intelliyole/kotlin-bytecode-generation-and-runtime-performance">Kotlin Bytecode Generation and Runtime Performance</a><br />
<br />
<a href="https://proandroiddev.com/understanding-generics-and-variance-in-kotlin-714c14564c47">Understanding Generics and Variance in Kotlin</a><br />
<br />
<a href="https://android.jlelse.eu/inline-noinline-crossinline-what-do-they-mean-b13f48e113c2">inline, noinline, crossinline — What do they mean?</a><br />
<br />
<a href="https://kotlin.link/articles/An-in-depth-look-at-Kotlin%E2%80%99s-initializers.html">An in-depth look at Kotlin’s initializers</a><br />
<br />
<a href="https://fabiomsr.github.io/from-java-to-kotlin/classes.html">Compare Java to Kotlin </a><br />
<br />
<a href="https://blog.jetbrains.com/kotlin/2017/07/kotlin-workshop-material-for-you-to-use/">Kotlin Workshop Material for you to use</a><br />
<br />
<a href="https://github.com/Kotlin/anko/wiki/Anko-Coroutines">Anko Coroutines</a><br />
<br />
<h3>
VÍDEOS</h3>
<a href="https://www.youtube.com/watch?v=HZonRC5jgx4">Kotlin para Android: O Despertar Da Força</a><br />
<br />
<a href="https://www.youtube.com/watch?v=DZTbV73CxWE">Live code with Kotlin: construindo o primeiro app usando Kotlin</a><br />
<br />
<a href="https://www.youtube.com/watch?v=lpPbCWpBM3I">Kotlin in Real Projects</a><br />
<br />
<a href="https://academy.realm.io/posts/learning-path-kotlin-in-depth/">Learn Path: Kotlin in depth</a><br />
<br />
Muito provavelmente, vocês encontrarão todos esses links no site <a href="https://kotlin.link/">https://kotlin.link/</a>.<br />
<br />
Ah! Mas obviamente todos esses links não valeriam de muita coisa sem praticar. Por isso, desde o ano passado, todos os meus projetos (pessoais e profissionais) foram (e estão sendo) em Kotlin! \o/<br />
<br />
Qualquer dúvida, deixe seu comentário ;)<br />
<br />
4br4ç05,<br />
nglauberNelson Glauberhttp://www.blogger.com/profile/02476858089223420894noreply@blogger.com5tag:blogger.com,1999:blog-3344721733578072661.post-25310132524724936452017-07-05T09:24:00.003-07:002017-10-05T10:58:44.750-07:00Falando de Android por aí :)Olá pessoal,<br />
<br />
Passei aqui apenas para deixar a lista dos próximos eventos que eu participarei. Estão todos convidados! :)<br />
<br />
<img border="0" data-original-height="1024" data-original-width="1024" height="200" src="https://3.bp.blogspot.com/-Hght7vTANWM/WVvowMiBQiI/AAAAAAAArQw/gF5_I3gE1qMj9wo2HH1zufbPhuIibkA2gCLcBGAs/s200/highres_457763395.jpeg" width="200" />
<br />
<span style="font-size: large;"><span style="color: red;">✔︎ </span>Meetup: I/O Recap do GDG Recife</span><br />
<b>10/07/2017 - 19:15</b><br />
<a href="https://www.meetup.com/GDG-Recife/events/241094820">https://www.meetup.com/GDG-Recife/events/241094820</a><br />
Palestra: Turbinando o Desenvolvimento Android com Kotlin<br />
<br />
<img border="0" data-original-height="268" data-original-width="540" height="158" src="https://1.bp.blogspot.com/-kaGdxLoFrdA/WVvlsUbY8BI/AAAAAAAArQk/ez2FaTl8Qtwcu2T41AL7eEW9x8-qtEkzwCLcBGAs/s320/tdcsp_p.png" width="320" />
<br />
<span style="font-size: large;"><span style="color: red;">✔︎ </span>The Developers Conference (TDC) São Paulo - Trilha Android</span><br />
<b>19/07/2017 - 15:40</b><br />
<a href="http://www.thedevelopersconference.com.br/tdc/2017/saopaulo/trilha-android">http://www.thedevelopersconference.com.br/tdc/2017/saopaulo/trilha-android</a><br />
Palestra: Persistência de dados no SQLite com Room<br />
<br />
<br />
<img border="0" data-original-height="512" data-original-width="512" height="200" src="https://1.bp.blogspot.com/-QYh06lVru6Q/WXJ2YaM2m0I/AAAAAAAArhU/9KWdgOOohsYrWsRjzcYbyMjYWs69xTyAACLcBGAs/s200/techtour.png" width="200" />
<br />
<span style="font-size: large;"><span style="color: red;">✔︎</span> GDG Tech Tour Caruaru: I/O Recap</span><br />
<b>22/07/2017 - 13:00</b><br />
<a href="https://www.facebook.com/events/1266327943476326">https://www.facebook.com/events/1266327943476326</a><br />
Palestra: Turbinando o Desenvolvimento Android com Kotlin<br />
<br />
<br />
<img border="0" data-original-height="250" data-original-width="965" height="82" src="https://3.bp.blogspot.com/-aPRTPRlFTLY/WXGBA8YIGdI/AAAAAAAArgA/cy3M7kep9uUHjb6Yvu-DSJ_Q7O3bRvLtwCLcBGAs/s320/launchpad.png" width="320" />
<br />
<span style="font-size: large;"><span style="color: red;">✔︎ </span>Google Launchpad Build Porto Alegre</span><br />
<b>05/08/2017</b><br />
<a href="https://events.withgoogle.com/google-launchpad-build-porto-alegre-05-de-agosto/">https://events.withgoogle.com/google-launchpad-build-porto-alegre-05-de-agosto/</a><br />
Palestra: Turbinando o Desenvolvimento Android com Kotlin<br />
<br />
<br />
<img border="0" data-original-height="536" data-original-width="536" height="320" src="https://1.bp.blogspot.com/-BqehkgJ_ewM/WVvmi41ElaI/AAAAAAAArQo/WiKDyMrfnQcanu9vdaXTxo8zbcOJ2O4UgCLcBGAs/s320/androidos.png" width="320" />
<br />
<span style="font-size: large;"><span style="color: red;">✔︎ </span>Androidos Day em João Pessoa</span><br />
<b>11/08/2017 - 18:00 (Mini-Curso)</b><br />
<b>12/08/2017 - 13:30</b><br />
<a href="http://androidosday.com/">http://androidosday.com/</a><br />
Palestra: Turbinando o Desenvolvimento Android com Kotlin<br />
<br />
<img border="0" data-original-height="224" data-original-width="688" height="104" src="https://3.bp.blogspot.com/-35HJvVI--oo/WWUGQj1a0uI/AAAAAAAArUk/IpKrF9m9YX0jKBpdRbNY_PVanLei7UheACLcBGAs/s320/agency.png" width="320" />
<br />
<span style="font-size: large;"><span style="color: red;">✔︎ </span>Google Developer Agency Day em São Paulo</span><br />
<b>16/08/2017</b><br />
<a href="https://events.withgoogle.com/google-developers-agency-day/">https://events.withgoogle.com/google-developers-agency-day/</a><br />
Palestra: Turbinando o Desenvolvimento Android com Kotlin<br />
<br />
<img border="0" data-original-height="176" data-original-width="181" src="https://1.bp.blogspot.com/-MMtzdpWpUEY/WWlJ7RIAuBI/AAAAAAAArXA/fSA9yVU23e0GvQEntGTIxrrCVV7wolouQCLcBGAs/s1600/androiddevconf.png" />
<br />
<span style="font-size: large;"><span style="color: red;">✔︎ </span>Android Dev Conference</span><br />
<b>24 e 25/08/2017</b><br />
<a href="https://eventos.imasters.com.br/android-devconference">https://eventos.imasters.com.br/android-devconference</a><br />
Palestra: Tudo o que você precisa saber sobre ConstraintLayout<br />
<br />
<br />
<img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://2.bp.blogspot.com/-mzUjfe6ikF4/WVvoHw8EY9I/AAAAAAAArQs/fjISeEMp7dw5OUD2oeCyk1ZDWoFbPENaQCLcBGAs/s320/devfestmaceio.png" width="320" />
<br />
<span style="font-size: large;"><span style="color: red;">✔︎ </span>DevFest Maceió</span><br />
<b>23/09/2017</b><br />
<a href="https://devfest.gdgmaceio.org/">https://devfest.gdgmaceio.org/</a><br />
Palestra: Turbinando o Desenvolvimento Android com Kotlin<br />
<br />
<img border="0" data-original-height="106" data-original-width="267" height="126" src="https://3.bp.blogspot.com/-FNLp6_73kZE/WYKaZUQf5nI/AAAAAAAArqs/YbBE-hk6GkssVKf-RhiPKzgz4pxAM8hpQCLcBGAs/s320/Screen%2BShot%2B2017-08-03%2Bat%2B00.36.29.png" width="320" /><br />
<span style="font-size: large;"><span style="color: red;">✔︎ </span>DevFest Cerrado em Goiânia</span><br />
<b>30/09/2017</b><br />
<a href="https://devfestcerrado.com.br/">https://devfestcerrado.com.br/</a><br />
Palestra: Turbinando o Desenvolvimento Android com Kotlin<br />
<br />
<img border="0" data-original-height="292" data-original-width="600" height="155" src="https://4.bp.blogspot.com/-nW59f4EkTpg/WdZydL6wChI/AAAAAAAAvi8/9b9YctkL-181e3WaIlEVMC9s0o34uKR4gCLcBGAs/s320/home.png" width="320" />
<br />
<span style="font-size: large;">DevFest Paraná (Londrina)</span><br />
<b>11/11/2017</b><br />
<a href="https://devfestpr.org/">https://devfestpr.org/</a><br />
Palestra: ?<br />
<br />
4br4ç05,<br />
nglauberNelson Glauberhttp://www.blogger.com/profile/02476858089223420894noreply@blogger.com0tag:blogger.com,1999:blog-3344721733578072661.post-52964654099819383782017-07-03T18:16:00.002-07:002017-07-03T18:18:08.100-07:00Introdução ao Kotlin com Android Studio<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-gqRYU_4o29w/WVrsVeuZAXI/AAAAAAAArPo/Kcb6GDyH6pAzUz6ymPNMHcuWi9s64XGogCLcBGAs/s1600/android_kotlin.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="465" data-original-width="807" height="184" src="https://4.bp.blogspot.com/-gqRYU_4o29w/WVrsVeuZAXI/AAAAAAAArPo/Kcb6GDyH6pAzUz6ymPNMHcuWi9s64XGogCLcBGAs/s320/android_kotlin.png" width="320" /></a></div>
Olá pessoal,<br />
<br />
Decidi fazer esse vídeo para ajudar aqueles que querem dar os primeiros passos com Kotlin utilizando Android Studio.<br />
<iframe width="560" height="315" src="https://www.youtube.com/embed/R99z12zLFRw" frameborder="0" allowfullscreen></iframe>
Qualquer dúvida, deixe seu comentário. ;)<br />
<br />
4br4ço5,<br />
nglauberNelson Glauberhttp://www.blogger.com/profile/02476858089223420894noreply@blogger.com0tag:blogger.com,1999:blog-3344721733578072661.post-53132615160785715862017-06-24T21:36:00.000-07:002017-06-24T21:36:09.978-07:00Um ano de trabalho em casa<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-lZkU0uXIgI8/WU8uivpePdI/AAAAAAAArJY/2gCTWcdp5a8MoHWFIICVBPHc10_aBQfWgCLcBGAs/s1600/large_Mokriya-Logo.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" data-original-height="120" data-original-width="400" height="96" src="https://4.bp.blogspot.com/-lZkU0uXIgI8/WU8uivpePdI/AAAAAAAArJY/2gCTWcdp5a8MoHWFIICVBPHc10_aBQfWgCLcBGAs/s320/large_Mokriya-Logo.png" width="320" /></a></div>
Olá povo,<br />
<br />
Após quase um ano trabalhando home-office full-time, resolvi compartilhar com vocês um pouco de como está sendo essa nova experiência na minha carreira.<br />
<br />
Em maio de 2016, recebi o convite do meu amigo <a href="https://twitter.com/douglasdrumond">Douglas Drummond</a> para me candidatar a uma vaga na empresa que ele estava trabalhando. A Mokriya (<a href="http://www.mokriya.com/">www.mokriya.com</a>) é uma empresa indiana, mas com sede na cidade de <a href="https://www.google.com.br/maps/place/Mokriya/@37.3188589,-122.032442,17z/data=!3m1!4b1!4m5!3m4!1s0x808fb5a9b1dbaf7d:0xb07258144c48f87e!8m2!3d37.3188547!4d-122.030248?hl=pt-BR">Cupertino</a>, Califórnia. Foi fundada em 2010 e adquirida pela <a href="http://www.nagarro.com/">Nagarro</a> no final de 2016. Entre os principais clientes estão: Google, Twitter, Intel, AT&T, Verizon, Sony, SanDisk, Salesforce, Estee Lauder, entre outros.<br />
<br />
Eu tinha acabado de sair do <a href="http://www.cesar.org.br/">CESAR</a> e entrado na <a href="http://www.mesainc.com.br/">MESA</a>, mas a proposta de trabalhar de casa foi muito atraente, então resolvi me candidatar. Após três etapas de entrevistas, fui aprovado e comecei minha nova jornada.<br />
<br />
Como essa é a minha primeira experiência em home-office, não sei como funciona em outras empresas, mas na Mokriya temos confiança em cada membro da equipe. Digo isso porque algumas pessoas já me perguntaram se eu tinha que ficar com a webcam ligada, ou reportar meu trabalho todo dia, etc. Eu reporto minhas atividades apenas durante as reuniões diárias (ou semanais, dependendo do projeto). Como normalmente a equipe é pequena, não dá para "enrolar", você tem que ser comprometido, focado e eficiente. Acho que isso serve para todos os trabalhos, mas na minha experiência atual isso ficou mais evidente.<br />
<br />
Uma coisa importantíssima de trabalhar remoto é ter foco e disciplina, pois em casa é muito mais fácil se distrair, seja com projetos pessoais ou com qualquer bobeira na internet. O que eu procuro fazer é sempre estabelecer um horário de trabalho e as metas para o meu dia, então eu só paro quando esses objetivos são alcançados.<br />
<br />
Existem vários benefícios em trabalhar remoto. O primeiro deles é que eu posso trabalhar onde e como quiser. Pode ser em casa, de bermuda e chinelo, escutando música alta e gritando que nem um louco (é... eu faço isso). Ou posso viajar para um evento ou para o interior ver meus pais. Ou posso ainda ir para um co-working ou para a piscina do prédio (pq eu nunca fiz isso???).<br />
<div>
<br /></div>
Outro grande benefício é não pegar o trânsito infernal de Recife que é <a href="http://exame.abril.com.br/brasil/onde-mais-se-perde-tempo-parado-no-transito-no-brasil/">um dos piores do Brasil</a>. Isso me fez ganhar pelo menos 2 horas por dia para aproveitar como eu quiser :)<div>
<br /><div>
Mas esses benefícios não seriam nada, se o trabalho em si não fosse motivador. Desde que entrei na Mokriya, sempre trabalhei em projetos interessantes (uns mais que os outros, claro). E em cada um deles, uma equipe com pessoas de diversas partes do mundo: Bulgária, Polônia, Índia, Estados Unidos, Portugal, ... e com isso, consigo treinar meu inglês com os mais diferentes sotaques. :)</div>
<div>
<br /></div>
<div>
Além dos motivos que mencionei acima, trabalhar na Mokriya me permitiu realizar um sonho profissional que era: "trabalhar no Google". Nunca fui funcionário do Google, mas estava alocado em um projeto e passei algumas semanas trabalhando no Googleplex em Mountain View. Eu já tinha ido ao Google algumas vezes para eventos do programa de GDE (<a href="https://developers.google.com/experts/">Google Developer Expert</a>) e durante o Google I/O, mas trabalhar lá (participar de reuniões, conhecer o ambiente, fazer as refeições, andar nas bikes :) e ver tudo aquilo por dentro foi sensacional.. indescritível.<br />
<br />
Mas nem tudo são flores. Trabalhar de casa tem seus pontos negativos, e para mim, a solidão é o pior de todos. Por onde trabalhei, sempre gostava de conversar com todo mundo, tomar aquele café com a equipe nos intervalos, falar como foi o fim de semana, etc. Em casa, você até tem o café, mas não rola aquela conversa bacana. Entretanto eu tento minimizar esse problema almoçando semanalmente com os meus velhos amigos do CESAR. ;)<br />
<br />
Às vezes, os fuso-horários diferentes para reuniões são bem inconvenientes. Para mim não foi tão complicado, o pior que me aconteceu foram reuniões às 20h ou no horário do almoço/janta, já para alguns colegas, as reuniões era à 1h ou 2h da manhã... #tenso<br />
Trabalhar nos feriados do Brasil é bem chato, mas venhamos e convenhamos, temos feriados demais por aqui e desde que entrei na Mokriya, eu trabalhei em TODOS! :'(<br />
Meu contrato é de consultoria, então não tenho os "benefícios" da CLT como: 13º, FGTS e INSS (eu pago por fora) e além disso, tenho que pagar o plano de saúde por fora.<br />
<br />
Depois desse tempo trabalhando remoto, cheguei a conclusão que, para empresas e profissionais de desenvolvimento de software, não faz nenhum sentido sair de casa e ir para um local específico trabalhar. Se você tiver uma boa estrutura, tudo pode ser feito de casa. Minha comunicação do dia-a-dia é feita por <a href="https://slack.com/">Slack</a>, as reuniões são feitas com o <a href="https://zoom.us/">Zoom</a>, código no GitHub, ... isso sem contar Google Drive, Dropbox, <a href="https://zeplin.io/">Zeplin</a>, <a href="https://hockeyapp.net/">HockeyApp</a>, etc. para compartilhamento de arquivos.<br />
<br />
É isso pessoal! Estou muito feliz nesse meu emprego e agradeço a Deus todos os dias por ele.<br />
Qualquer dúvida, deixem seus comentários que eu vou atualizando o post ;)<br />
<br />
4br4ç05,<br />
nglauber</div>
</div>
Nelson Glauberhttp://www.blogger.com/profile/02476858089223420894noreply@blogger.com3tag:blogger.com,1999:blog-3344721733578072661.post-23845692198790508882017-05-22T05:50:00.000-07:002017-05-24T05:01:29.112-07:00Architecture Components do AndroidOlá povo,<br />
<br />
O Google I/O 2017 foi repleto de novidades fantásticas para a plataforma Android. Entre elas, duas foram fundamentais para os desenvolvedores Android: <b>Kotlin</b> e <b>Architecture Components</b>.<br />
<a href="https://1.bp.blogspot.com/-Ux80e5jcekA/WSLcTseW_DI/AAAAAAAAqt4/mtl8Nj9K-sMvFV8e5QYQklrrNG6TdWDGACLcB/s1600/desktoproom-2x.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="279" src="https://1.bp.blogspot.com/-Ux80e5jcekA/WSLcTseW_DI/AAAAAAAAqt4/mtl8Nj9K-sMvFV8e5QYQklrrNG6TdWDGACLcB/s320/desktoproom-2x.png" width="320" /></a><br />
Apesar de podermos desenvolver aplicativos Android utilizando Kotlin nas versões anteriores do Android Studio por meio de um plugin instalado à parte, agora ao criar um projeto poderemos selecionar a linguagem de nossa preferência (Java ou Kotlin).<br />
<br />
Os Architecture Components vieram para facilitar a implementação de aplicativos de uma forma mais robusta e padronizada, e nesse post vou mostrar um exemplo simples de como utilizar essas APIs.<br />
<br />
<br />
<h2>
Adicionando as dependências</h2>
Para implementar esse projeto, eu utilizei o Android Studio 2.4 com o plugin 1.1.2 do Kotlin. Mas se quiser utilizar a versão preview do Android Studio 3.0 eu creio que você não terá muitos problemas. Crie um novo projeto com suporte a Kotlin e nomeie a sua activity principal como ListPeopleActivity. Com o projeto criado, vamos fazer as seguintes mudanças no <i>build.gradle</i> do seu <b>projeto</b> como a seguir:<br />
<br />
<pre class="brush: java" name="code">buildscript {
ext.gradle_version = '2.3.2'
ext.kotlin_version = '1.1.2-3'
ext.anko_version = '0.8.2'
ext.support_version = '25.3.1'
ext.arch_lifecycle_version = "1.0.0-alpha1"
ext.arch_room_version = "1.0.0-alpha1"
repositories {
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:$gradle_version"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
jcenter()
maven { url 'https://maven.google.com' }
mavenCentral()
}
}
</pre>
Aqui criamos uma série de variáveis com as versões das bibliotecas que utilizaremos no projeto. Isso não é obrigatório, mas facilita a legibilidade do arquivo e evita erros e conflitos entre versões das bibliotecas. Adicionamos a versão do Gradle, do Kotlin, do Anko (que é uma biblioteca para facilitar o acesso aos componentes de UI), da support library e finalmente, das bibliotecas de arquitetura (lifecycle e room).<br />
Na lista de dependências, além do gradle, adicionamos o plugin do Kotlin. Por fim, na lista de repositórios, devemos adicionar o "maven.google.com".<br />
<br />
Agora vá até o <i>build.gradle</i> do <b>módulo</b> da sua aplicação e faça os seguintes ajustes.<br />
<br />
<pre class="brush: java" name="code">apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
// Nada muda aqui...
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile "org.jetbrains.anko:anko-common:$anko_version"
compile "com.android.support:appcompat-v7:$support_version"
compile "com.android.support:design:$support_version"
compile "com.android.support:support-v4:$support_version"
compile "android.arch.lifecycle:runtime:$arch_lifecycle_version"
compile "android.arch.lifecycle:extensions:$arch_lifecycle_version"
compile "android.arch.persistence.room:runtime:$arch_room_version"
kapt "android.arch.lifecycle:compiler:$arch_lifecycle_version"
kapt "android.arch.persistence.room:compiler:$arch_room_version"
}
</pre>
Além do plugin do Android, estamos aplicando o plugin do Kotlin e o de extensões para o Kotlin. Na seção de dependências, adicionamos todas as bibliotecas que utilizaremos nesse projeto. O <b>kapt</b> (<a href="https://kotlinlang.org/docs/reference/kapt.html">Kotlin Annotation Process Tool</a>) substitui o "annotationProcessor" para projeto sem Kotlin. Perceba que utilizamos o kapt para as bibliotecas de arquitetura, pois ela geram o código em tempo de compilação. Isso deve ser feito para outras bibliotecas que fazem esse trabalho (Dagger, DataBinding, etc.).<br />
<h2>
Definindo o acesso ao banco</h2>
Para simplificar o tutorial e facilitar o entendimento, nosso aplicativo será um cadastro simples de pessoas, onde cada pessoa possuirá o primeiro nome, sobrenome e idade. Para tal, definiremos a classe a seguir:<br />
<br />
<pre class="brush: java" name="code">import android.arch.persistence.room.Entity
import android.arch.persistence.room.PrimaryKey
import java.io.Serializable
@Entity
data class Person(
@PrimaryKey(autoGenerate = true) var id: Long = 0L,
var firstName: String = "",
var lastName: String = "",
var age: Int = 0) : Serializable
</pre>
Criamos uma data class anotada com <b>@Entity</b>, o que indica que objetos dessa classe serão persistidos. Todos os parâmetros têm valores default, então podemos criar objetos dessa classe passando nenhum parâmetro. Uma outra curiosidade nessa classe é que devemos adicionar um atributo como <b>@PrimaryKey</b>, nesse exemplo, ele será gerado automaticamente, então definimos a propriedade autoGenerate para true.<br />
<br />
Agora vamos definiremos a DAO, a interface que possuirá os métodos para interagir com o banco de dados.
<br />
<pre class="brush: java" name="code">import android.arch.lifecycle.LiveData
import android.arch.persistence.room.*
import android.arch.persistence.room.OnConflictStrategy.IGNORE
@Dao
interface PeopleDao {
@Insert(onConflict = IGNORE)
fun insertPerson(person: Person)
@Update
fun updatePerson(person: Person)
@Delete
fun deletePerson(vararg people: Person)
@Query("SELECT * FROM Person ORDER BY firstName")
fun listAll(): LiveData<List<Person>>
}
</pre>
Nossa interface está anotada com <b>@Dao</b> e os métodos de inserir, atualizar, excluir e listar estão anotados respectivamente com <b>@Insert</b>, <b>@Update</b>, <b>@Delete</b> e <b>@Query</b>. O método insertPerson(Person) possui em sua anotação a propriedade onConflict definida como IGNORE, indicando que caso haja um conflito no momento da inserção o registro será ignorado.<br />
Apesar de não estarmos utilizando esse recurso, no método deletePerson() permitimos excluir vários registros ao mesmo tempo utilizando um <b>vararg</b>.
A parte mais interessante é o método listAll() que retorna um <b>LiveData</b> de uma lista de objetos Person.<br />
Utilizando o LiveData quando qualquer registro for inserido, alterado ou excluído, a lista será automaticamente atualizada.<br />
O último passo para podermos utilizar o Room, é definir uma classe que herda de <b>RoomDatabase</b> como mostraremos a seguir.
<br />
<pre class="brush: java" name="code">import android.arch.persistence.room.Database
import android.arch.persistence.room.Room
import android.arch.persistence.room.RoomDatabase
import android.content.Context
@Database(
entities = arrayOf(Person::class),
version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun peopleDao(): PeopleDao
companion object {
private val DB_NAME = "dbPeople"
private var INSTANCE: AppDatabase? = null
fun getDatabase(context: Context): AppDatabase? {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
DB_NAME)
.allowMainThreadQueries()
.build()
}
return INSTANCE
}
fun destroyInstance() {
INSTANCE = null
}
}
}
</pre>
Na anotação <b>@Database</b> informamos quais entidades serão persistidas utilizando a propriedade "entities" e a versão do banco. Perceba que essa é uma classe abstrata, pois assim como a nossa interface DAO, a implementação será gerada em tempo de compilação.<br />
Essa classe será um singleton que é inicializado por meio do método <b>Room.databaseBuilder</b> onde passamos o contexto, a referência à nossa classe e o nome do banco. Perceba que estamos utilizando o método allowMainThreadQueries() que não é recomendado, pois devemos realizar as consultas em uma thread separada, mas não estamos fazendo isso aqui para simplificar o exemplo.<br />
O método peopleDao() retornará uma instância da implementação do nosso DAO.<br />
<br />
<h2>
Definindo os ViewModels</h2>
Definida nossa camada de acesso ao banco, partiremos agora para a definição dos ViewModels do nosso pequeno projeto. Primeiramente, vamos criar uma classe que será utilizada na tela de listagem e será responsável por manter o resultado da busca durante a rotação da tela do aparelho sem ter que fazer uma nova consulta ao banco.
<br />
<pre class="brush: java" name="code">import android.app.Application
import android.arch.lifecycle.AndroidViewModel
import android.arch.lifecycle.LiveData
class ListPeopleViewModel(app : Application) : AndroidViewModel(app) {
var livePeople: LiveData<List<Person>>? = null
var db: PeopleDao? = null
private fun getDao() : PeopleDao? {
if (db == null) {
db = AppDatabase.getDatabase(getApplication())?.peopleDao()
}
return db
}
fun getPeople(): LiveData<List<Person>> {
if (livePeople == null) {
livePeople = getDao()?.listAll()
}
return livePeople as LiveData<List<Person>>
}
}
</pre>
Nossa classe herda de <b>AndroidViewModel</b> e está mantendo a LiveData da lista de Person. Dessa forma, caso a lista seja nula, carregamos do banco, caso contrário apenas a retornamos.<br />
Faremos uma classe similar para a tela de cadastro, mas apenas para isolar o acesso ao banco da nossa UI.
<br />
<pre class="brush: java" name="code">import android.app.Application
import android.arch.lifecycle.AndroidViewModel
class PersonFormViewModel(app : Application) : AndroidViewModel(app) {
private var db: PeopleDao? = null
private fun peopleDao() : PeopleDao? {
if (db == null) {
db = AppDatabase.getDatabase(getApplication())?.peopleDao()
}
return db
}
fun savePerson(person : Person) {
if (person.id == 0L) {
peopleDao()?.insertPerson(person)
} else {
peopleDao()?.updatePerson(person)
}
}
fun deletePerson(person: Person) {
peopleDao()?.deletePerson(person)
}
}
</pre>
O método savePerson(Person) irá inserir o objeto se o seu id for igual a zero ou atualizá-lo caso contrário. Já o método deletePerson(Person) excluirá o objeto Person.<br />
<br />
<h2>
Juntando tudo</h2>
Finalmente vamos implementar as telas da aplicação, começando pela tela de listagem.
<br />
<pre class="brush: java" name="code">import android.arch.lifecycle.LifecycleRegistry
import android.arch.lifecycle.LifecycleRegistryOwner
import android.arch.lifecycle.Observer
import android.arch.lifecycle.ViewModelProviders
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.widget.ArrayAdapter
import kotlinx.android.synthetic.main.activity_list_people.*
class ListPeopleActivity : AppCompatActivity(), LifecycleRegistryOwner {
var lifecycleRegistry = LifecycleRegistry(this)
override fun getLifecycle(): LifecycleRegistry = lifecycleRegistry
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_list_people)
fab.setOnClickListener {
PersonFormFragment().show(supportFragmentManager, "form")
}
val model = ViewModelProviders.of(this).get(ListPeopleViewModel::class.java)
model.getPeople().observe(this, Observer { people ->
listview.adapter = ArrayAdapter<Person>(
this@ListPeopleActivity,
android.R.layout.simple_list_item_1,
people)
listview.setOnItemClickListener { _, _, position, _ ->
val person = people?.get(position)
if (person != null) {
PersonFormFragment.newInstance(person).show(supportFragmentManager, "form")
}
}
})
}
override fun onDestroy() {
AppDatabase.destroyInstance()
super.onDestroy()
}
}
</pre>
Nossa classe herda de AppCompatActivity e implementa <b>LifecycleRegistryOwner</b>. Essa interface requer que implementemos o método <b>getLifecycle()</b> que retorna um objeto <b>LifecycleRegistry</b>. Poderíamos simplesmente herdade de <b>LifecycleActivity</b>, mas essa classe herda de FragmentActivity, então perderíamos a ActionBar padrão que é adicionada.<br />
<br />
Não vou mostrar o layout dessa activity aqui, mas ele possui apenas uma ListView com o id @+id/listview e uma FloatingActionButton com o id @+id/fab. Perceba que estamos acessando os componentes de UI usando simplesmente o seu id. Isso é feito graças ao kotlinx (<a href="https://kotlinlang.org/docs/tutorials/android-plugin.html">Kotlin Extensions</a>).<br />
<br />
Ao clicar no FAB, o PersonFormFragment (que mostraremos a seguir) é exibido.
Perceba que estamos obtendo o ViewModel dessa tela por meio da classe <b>ViewModelProviders</b> e passando o nome da classe.<br />
Com o ViewModel, obtemos a lista de pessoas e chamamos o método <b>observe</b> para sabermos quando a listagem mudou. Nesse caso, definimos um adapter para o nosso listView e atribuímos o evento de click no item. Perceba que como não estamos utilizando todos os parâmetros, estamos utilizando o "_" no lugar dos respectivos nomes.<br />
<br />
A última classe é tela de cadastro que é um DialogFragment.
<br />
<pre class="brush: java" name="code">import android.arch.lifecycle.ViewModelProviders
import android.content.Context
import android.os.Bundle
import android.support.v4.app.DialogFragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import kotlinx.android.synthetic.main.fragment_person_form.*
class PersonFormFragment : DialogFragment() {
lateinit var person : Person
lateinit var viewModel : PersonFormViewModel
override fun onAttach(context: Context?) {
super.onAttach(context)
viewModel = ViewModelProviders.of(this).get(PersonFormViewModel::class.java)
person = arguments?.getSerializable(EXTRA_PERSON) as? Person ?: Person()
}
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater?.inflate(R.layout.fragment_person_form, container, false)
}
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
edtFirstName.setText(person.firstName)
edtLastName.setText(person.lastName)
edtAge.setText(if (person.age != 0 || person.id != 0L) person.age.toString() else "")
btnRemove.visibility = if (person.id == 0L) View.GONE else View.VISIBLE
btnCancel.setOnClickListener { dismiss() }
btnSave.setOnClickListener { savePerson() }
btnRemove.setOnClickListener { removePerson() }
}
private fun removePerson() {
viewModel.deletePerson(person)
dismiss()
}
private fun savePerson() {
person.firstName = edtFirstName.text.toString()
person.lastName = edtLastName.text.toString()
person.age = edtAge.text.toString().toInt()
viewModel.savePerson(person)
dismiss()
}
companion object {
val EXTRA_PERSON = "person"
fun newInstance(person: Person) : PersonFormFragment {
val args = Bundle()
args.putSerializable(EXTRA_PERSON, person)
val f = PersonFormFragment()
f.arguments = args
return f
}
}
}
</pre>
Mais uma vez estamos omitindo o arquivo de layout aqui, mas ele possui basicamente três EditText (edtFirstName, edtLastName e edtAge) para os respectivos campos da classe Person, e três botões (btnSave, btnRemove, btnCancel) para salvar, remover e cancelar o dialog. Sendo que o segundo só estará habilitado se estivermos editando uma pessoa.<br />
No onAttach estamos inicializando nosso ViewModel e obtendo o objeto Person passado da tela de listagem. Se não tivermos passado esse objeto, criamos um novo. Perceba que nessa instrução estamos usando "?" nos arguments, pois ele pode ser null, no "as?" pois não pode ser feito um cast de null para Person (isso se chama save cast) e por fim utilizamos o "elvis operator", pois caso a instrução anterior seja nula, criamos um novo objeto Person.<br />
Nada de especial no onCreateView, mas no onViewCreated temos apenas uma instrução interessante para quem é novo em Kotlin que é o if/else retornando um valor (seria o mesmo que o ternário do Java "?:").<br />
Nos métodos removePerson chamamos nosso ViewModel para excluir o objeto e no savePerson para salvar o objeto. Por fim, nossa classe possui o "factory method" newInstance para passarmos o objeto pessoa como parâmetro para a tela de cadastro no caso de uma alteração.<br />
<br />
O resultado deve ficar como a seguir:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-P4RiNRUNVmQ/WSLc4FDijiI/AAAAAAAAquI/8RIhF_AEKrosR_pCta6YabMS84k9daMEgCLcB/s1600/tela1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://3.bp.blogspot.com/-P4RiNRUNVmQ/WSLc4FDijiI/AAAAAAAAquI/8RIhF_AEKrosR_pCta6YabMS84k9daMEgCLcB/s320/tela1.png" width="179" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-fYKJ9xZhFnU/WSLc4KhFPAI/AAAAAAAAquA/auTzB1qBu3sT98a5D1BVWB-NA8XQXf8BgCLcB/s1600/tela2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://2.bp.blogspot.com/-fYKJ9xZhFnU/WSLc4KhFPAI/AAAAAAAAquA/auTzB1qBu3sT98a5D1BVWB-NA8XQXf8BgCLcB/s320/tela2.png" width="180" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-jMwBhpQ4a0I/WSLc4F-mWjI/AAAAAAAAquE/THkUU5R5wG8X7uGzWRNbHH-Cx_FHUOFEACLcB/s1600/tela3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://1.bp.blogspot.com/-jMwBhpQ4a0I/WSLc4F-mWjI/AAAAAAAAquE/THkUU5R5wG8X7uGzWRNbHH-Cx_FHUOFEACLcB/s320/tela3.png" width="180" /></a></div>
Essa foi uma pequena introdução de como utilizar as bibliotecas de arquitetura lançadas no Google IO de 2017 utilizando Kotlin. Obviamente existe muito mais a explorar, mas espero que esse post já ajude a dar os primeiros passos com essas APIs.<br />
<br />
Para saber mais, assistam esses vídeos do Google IO e a documentação dessas libs. ;)<br />
<br />
Architecture Components - Introduction<br />
<a href="https://www.youtube.com/watch?v=FrteWKKVyzI">https://www.youtube.com/watch?v=FrteWKKVyzI</a><br />
<br />
Architecture Components - Solving the Lifecycle Problem<br />
<a href="https://www.youtube.com/watch?v=bEKNi1JOrNs">https://www.youtube.com/watch?v=bEKNi1JOrNs</a><br />
<br />
Architecture Components - Persistence and Offline<br />
<a href="https://www.youtube.com/watch?v=MfHsPGQ6bgE">https://www.youtube.com/watch?v=MfHsPGQ6bgE</a><br />
<br />
Documentação oficial<br />
<a href="https://developer.android.com/topic/libraries/architecture/index.html">https://developer.android.com/topic/libraries/architecture/index.html</a><br />
<br />
4br4ç05,<br />
nglauberNelson Glauberhttp://www.blogger.com/profile/02476858089223420894noreply@blogger.com4tag:blogger.com,1999:blog-3344721733578072661.post-20032642235924579612017-03-03T17:30:00.000-08:002017-03-09T11:41:18.336-08:00RXJava + Kotlin + Retrofit + Star Wars API<center>
<img border="0" height="200" src="https://2.bp.blogspot.com/-XDoOQwDhQI8/WLoPWDrKl8I/AAAAAAAAmRA/BR3PD4Cp_aAPGvWe-EKuy6biRwxOdvDuwCLcB/s200/kotlinlogo.png" width="200" />
<img border="0" height="200" src="https://1.bp.blogspot.com/-nfpf8J16HO4/WLoPWBs4DlI/AAAAAAAAmRE/b83aO-ZVfmMv8rYjeQgrOwbhX_sBE7C8ACLcB/s200/rxjavalogo.png" width="200" /></center>
<br />
<br />
Olá povo,<br />
<br />
Há algum tenho venho estudando dois tópicos relacionados a Android que vem me deixando bem empolgado: <b>Kotlin</b> e <b>RX Java</b>.<br />
Kotlin é uma liguagem dinâmica para JVM desenvolvida pela JetBrains que traz diversas features não existentes no Java. E o RX Java, bem a grosso modo, é uma biblioteca nos ajuda a trabalhar com sequência de dados que podem estar em threads separadas de uma maneira bem mais simples.<br />
<br />
O intuito deste post <b>não</b> é como dar os primeiros passos com RX ou Kotlin, e sim documentar e compartilhar o que eu aprendi ao tentar implementar um exemplo "simples" com esse conjunto de linguagem+biblioteca+api.<br />
<br />
Nesse post vou mostrar:<br />
- Como acessar a <a href="https://swapi.co/" target="_blank">Star Wars API</a> utilizando a biblioteca Retrofit;<br />
- Fazer as requisições utilizando RX Java + Retrofit;<br />
- e todo o código é escrito em Kotlin.<br />
<br />
<h3>
</h3>
<h3>
Configuração do projeto</h3>
Primeira coisa que você deve fazer é instalar o <u>plugin do Kotlin</u> no Android Studio. Você pode seguir esse tutorial aqui:<br />
<a href="https://blog.jetbrains.com/kotlin/2013/08/working-with-kotlin-in-android-studio/">https://blog.jetbrains.com/kotlin/2013/08/working-with-kotlin-in-android-studio/</a><br />
A versão atual no momento da escrita desse post é a <i>1.1.0-release-Studio2.3-1</i><br />
<br />
Instalado o plugin, crie um novo projeto no Android Studio.<br />
Deixe o build.gradle do seu projeto como a seguir:<br />
<pre class="brush: java" name="code">buildscript {
ext.kotlin_version = '1.1.0'
ext.appcompat_version = '25.1.0'
ext.retrofit_version = '2.2.0'
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
jcenter()
}
}
</pre>
<br />
O que temos de diferente aqui é que estamos criando algumas variáveis com as versões do Kotlin e da biblioteca de compatibilidade. E na seção de dependências adicionamos o plugin do Kotlin para o Gradle.<br />
Vá agora até o build.gradle do módulo, faça as seguintes alterações:
<br />
<pre class="brush: java" name="code">apply plugin: 'com.android.application'
apply plugin: "kotlin-android"
android {
...
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
// Dependência da linguagem Kotlin
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
// AppCompat
compile "com.android.support:appcompat-v7:$appcompat_version"
// RXJava
compile 'io.reactivex:rxjava:1.2.5'
// RXAndroid para termos acesso a main thread do Android
compile 'io.reactivex:rxandroid:1.2.1'
// Retrofit
compile "com.squareup.retrofit2:retrofit:$retrofit_version"
// Adapter do Retrofit para retornar objetos observáveis
compile "com.squareup.retrofit2:adapter-rxjava:$retrofit_version"
// Converter do Retrofit para utilizar o Gson para tratar a resposta do servidor
compile "com.squareup.retrofit2:converter-gson:$retrofit_version"
// Interceptor para visualizar os logs das requisições do Retrofit
compile 'com.squareup.okhttp3:logging-interceptor:3.6.0'
...
}
</pre>
Aplicamos o plugin do Kotlin e adicionamos as dependências que utilizaremos no projeto. A motivação de cada uma está comentada acima.<br />
<br />
<h3>
</h3>
<h3>
A API do Star Wars</h3>
Vamos utilizar nesse exemplo dois endpoints da <a href="https://swapi.co/" target="_blank">API do Star Wars</a>: films e people.<br />
Se fizermos uma requisição para <a href="http://swapi.co/api/films">http://swapi.co/api/films</a> o resultado será o JSON com a lista dos filmes de Star Wars.<br />
<pre class="brush: jscript" name="code">{
"count": 7,
"next": null,
"previous": null,
"results": [
{
"characters": [
"http://swapi.co/api/people/1/",
...
],
"created": "2014-12-10T14:23:31.880000Z",
"director": "George Lucas",
"edited": "2014-12-12T11:24:39.858000Z",
"episode_id": 4,
"opening_crawl": "It is a period of civil war..",
"planets": [
"http://swapi.co/api/planets/1/",
...
],
"producer": "Gary Kurtz, Rick McCallum",
"release_date": "1977-05-25",
"species": [
"http://swapi.co/api/species/1/",
...
],
"starships": [
"http://swapi.co/api/starships/2/",
...
],
"title": "A New Hope",
"url": "http://swapi.co/api/films/1/",
"vehicles": [
"http://swapi.co/api/vehicles/4/",
...
]
},
// Aqui viriam os demais filmes...
]
}
</pre>
Se utilizarmos <a href="http://swapi.co/api/films/1">http://swapi.co/api/films/1</a> ele trará apenas o filme primeiro filme.<br />
Percebam que os campos "characters", "planets", "species", "starships" e "vehicles" retornam um array de strings, onde cada string representa o endereço para aquela determinada informação. Sendo assim, se acessarmos a URL <a href="http://swapi.co/api/people/1/">http://swapi.co/api/people/1/</a> teremos o resultado abaixo.<br />
<pre class="brush: jscript" name="code">{
"name": "Luke Skywalker",
"height": "172",
"mass": "77",
"hair_color": "blond",
"skin_color": "fair",
"eye_color": "blue",
"birth_year": "19BBY",
"gender": "male",
"homeworld": "http://swapi.co/api/planets/1/",
"films": [
"http://swapi.co/api/films/6/",
"http://swapi.co/api/films/3/",
"http://swapi.co/api/films/2/",
"http://swapi.co/api/films/1/",
"http://swapi.co/api/films/7/"
],
"species": [
"http://swapi.co/api/species/1/"
],
"vehicles": [
"http://swapi.co/api/vehicles/14/",
"http://swapi.co/api/vehicles/30/"
],
"starships": [
"http://swapi.co/api/starships/12/",
"http://swapi.co/api/starships/22/"
],
"created": "2014-12-09T13:50:51.644000Z",
"edited": "2014-12-20T21:17:56.891000Z",
"url": "http://swapi.co/api/people/1/"
}
</pre>
Note que temos uma referência cruzada aqui. O filme possui a lista de personagens e o personagem possui uma lista dos filmes (no campo "films") em que ele participou.<br />
Entendida a API, vamos começar a brincar com ela!<br />
<br />
<h3>
</h3>
<h3>
Definindo as classes de modelo</h3>
Um dos recursos que eu gosto bastante do Kotlin é a possibilidade de criar <u>data classes</u>, que são os nosso famosos POJOs. É possível criar várias classes públicas no mesmo arquivo e no Kotlin temos o conceito de propriedade, ou seja, não é preciso definir os gets e sets (embora você possa customiza-los).<br />
Crie o arquivo DataClassesWeb.kt (ou o nome que preferir) que conterá as classes que representarão o retorno das requisições que faremos a API.<br />
<pre class="brush: java" name="code">package br.com.nglauber.starwarsrx.model.api
import com.google.gson.annotations.SerializedName
data class FilmResult(val results : List<Film>)
data class Film (val title : String,
@SerializedName("episode_id")
val episodeId : Int,
@SerializedName("characters")
val personUrls : List<String>)
data class Person(val name : String,
val gender : String)
</pre>
<br />
A classe <b>FilmResult</b> representará o retorno da chamada que faremos a lista de filmes. Ela possui a propriedade <b>results</b> que é uma lista de <b>Film</b>. A classe Film, por sua vez, possui o título, o id do episódio e a lista das URLs para obtermos as informações dos personagens. Por fim, a classe <b>Person</b> possui o nome e o gênero do personagem.<br />
<br />
Agora crie mais um arquivo chamado DataClasses.kt com as classes "de negócio" da nossa aplicação.<br />
<pre class="brush: java" name="code">package br.com.nglauber.starwarsrx.model
data class Movie (val title : String,
val episodeId : Int,
val characters : MutableList<Character>)
data class Character(val name : String,
val gender : String){
override fun toString(): String {
return "${name} / ${gender}"
}
}
</pre>
Como podemos observar, essas classes são bem parecidas, mas preferi separar as classes de retorno de API, das que serão utilizadas na UI.<br />
<br />
<h3>
</h3>
<h3>
Definindo as chamadas à API com Retrofit</h3>
Nesse exemplo, vamos utilizar apenas dois endpoints da API do Star Wars: o que retorna a listagem de filmes; e o que obtém o personagem pelo seu id. Sendo assim, crie o arquivo StarWarsApiDef.kt e deixe-o como a seguir:<br />
<pre class="brush: java" name="code">package br.com.nglauber.starwarsrx.model.api
import retrofit2.http.GET
import retrofit2.http.Path
import rx.Observable
interface StarWarsApiDef {
@GET("films")
fun listMovies() : Observable<FilmResult>
@GET("people/{personId}")
fun loadPerson(@Path("personId") personId : String) : Observable<Person>
}
</pre>
Os métodos seguem o que está especificado na API do Star Wars. Para obtermos a lista de filmes, realizamos uma requisição do tipo GET, para o endpoint "films" que retorna um objeto (FilmResult) que possui uma lista de filmes (Film). E para obter um personagem específico, utilizamos o endpoint "<i>people/id_do_personagem</i>".<br />
Percebam que estamos retornando um <b>Observable<FilmResult></b> e um <b>Observable<Person></b>. A classe Observable é um dos principais componentes do RXJava (senão o principal). Se você não está familiarizado com esses conceitos, sugiro assistir as palestras do Ubiratan Soares que é uma verdadeira aula sobre o assunto (veja os links no final do post).<br />
<br />
Definida a classe com os endpoints, vamos criar a implementação que utilizará esses endpoints. Crie o arquivo StarWarsApi.kt e deixe-o como a seguir.<br />
<pre class="brush: java" name="code">package br.com.nglauber.starwarsrx.model.api
import br.com.nglauber.starwarsrx.model.Character
import br.com.nglauber.starwarsrx.model.Movie
import com.google.gson.GsonBuilder
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import rx.Observable
import java.util.*
class StarWarsApi {
val service: StarWarsApiDef
init {
val logging = HttpLoggingInterceptor()
logging.level = HttpLoggingInterceptor.Level.BODY
val httpClient = OkHttpClient.Builder()
httpClient.addInterceptor(logging)
val gson = GsonBuilder().setLenient().create()
val retrofit = Retrofit.Builder()
.baseUrl("http://swapi.co/api/")
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.client(httpClient.build())
.build()
service = retrofit.create<StarWarsApiDef>(StarWarsApiDef::class.java)
}
fun loadMovies(): Observable<Movie>? {
return service.listMovies()
.flatMap { filmResults -> Observable.from(filmResults.results) }
.map { film ->
Movie(film.title, film.episodeId, ArrayList<Character>())
}
}
}
</pre>
Essa classe possui um atributo chamado <b>service</b> do tipo StarWarsApiDef (que criamos anteriormente). Dentro do bloco <b>init{}</b> fazemos a inicialização e configuração do serviço do Retrofit. Adicionamos o <b>HttpLoggingInterceptor</b> ao OkHttpClient para podermos visualizar no Logcat as requisições e as respostas feitas pelo retrofit. Instanciamos o <b>GsonBuilder</b> para que o JSON retornado seja tratado pela biblioteca Gson. Utilizamos o <b>RxJavaCallAdapterFactory</b> para o Retrofit retornar o resultado em forma de objetos observáveis. Por fim, utilizamos o Retrofit.Builder para criar a instância do serviço.
<br />
O operador <b>flatMap</b> permite iterar sobre um Observable e retornar um novo Observable. É isso que estamos fazendo no método loadMovies. Estamos chamando o método listMovies() do nosso serviço que retorna um Observable de <u>FilmResult</u>, então utilizamos o operador flatMap para obter o FilmResult e geramos um novo Observable de <u>Film</u> com os filmes por meio do método Observable.from(). Em seguida, iterarmos por cada filme (Film) da lista (que é um Observable de Film) e o transformamos em um Observable de <u>Movie</u>, que é o tipo de retorno do método.<br />
<br />
<h3>
Chamando o serviço na Activity</h3>
Vamos ver como acessar o nosso serviço na interface gráfica. Se você ainda não converteu sua activity para Kotlin, faça isso acessando o menu "Code > Convert Java File to Kotlin file". E deixe sua activity como a seguir.<br />
<pre class="brush: java" name="code">class MainActivity : AppCompatActivity() {
lateinit var listView : ListView
lateinit var movieAdapter : ArrayAdapter<String>
var movies = mutableListOf<String>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
listView = ListView(this)
setContentView(listView)
movieAdapter = ArrayAdapter(this,
android.R.layout.simple_list_item_1, movies)
listView.adapter = movieAdapter
val api = StarWarsApi()
api.loadMovies()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe ({ movie ->
movies.add("${movie.title} -- ${movie.episodeId}")
}, { e ->
e.printStackTrace()
},{
movieAdapter.notifyDataSetChanged()
})
}
}
</pre>
Perceba que estamos invocando o método loadMovies() da nossa API. Como vimos anteriormente, esse método retorna um Observable, ou seja, um observável. Nossa tela observará esse objeto, então ela será um <b>Observer</b>. Estamos dizendo que queremos que esse objeto seja criado em <u>background</u> na thread de I/O usando o método <b>subscribeOn(Schedulers.io())</b>. Ele será criado em background, mas queremos observá-lo na <u>main thread do Android</u>, o que nos permitirá atualizar a tela.
Ao chamarmos o método <b>subscribe</b>, temos 3 expressões <u>lambda</u>: <b>onNext</b>, que é chamado a cada novo objeto Movie retornado; <b>onError</b> disparado se algum erro ocorrer; e o <b>onCompleted</b> quando a sequência de objetos termina.<br />
No onNext estamos adicionando os filmes na lista (em formato de string para simplificar) e no onCompleted estamos atualizando o adapter para exibir a listagem na tela.
<br />
Execute a aplicação e você deverá ver a lista de filmes.<br />
<br />
<h3>
</h3>
<h3>
Mas cada filme não deveria ter os seus respectivos personagens?</h3>
Sim. Mas para uma tela de listagem isso demora um bocado, pois cada filme tem vários personagens. Então seria melhor na tela de detalhe exibir os personagens. Mas fiquei curioso em saber como fazer isso com RX e resolvi fazer o teste. Vamos voltar ao arquivo StarWarsApi.kt e adicione o seguinte método.<br />
<pre class="brush: java" name="code">fun loadMoviesFull(): Observable<Movie> {
return service.listMovies()
.flatMap { filmResults -> Observable.from(filmResults.results) }
.flatMap { film ->
Observable.zip(
Observable.just(Movie(film.title, film.episodeId, ArrayList<Character>())),
Observable.from(film.personUrls)
.flatMap { personUrl ->
service.loadPerson(Uri.parse(personUrl).lastPathSegment)
}
.map { person ->
Character(person!!.name, person.gender)
}
.toList(),
{ movie, characters ->
movie.characters.addAll(characters)
movie
})
}
}
</pre>
Olha que loucura isso! :)<br />
Fazemos a requisição da lista de filmes, e para cada filme temos que pegar a lista de URLs dos personagens e apenas quando cada objeto filme estiver completo, é passamos para o próximo. Para fazer isso, utilizamos o operador <b>zip()</b>, pois ele junta o resultado de dois Observables e <u>retorna um novo Observable</u>.<br />
Podemos testar isso agora na nossa Activity.<br />
<pre class="brush: java" name="code">api.loadMoviesFull()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe ({
movie ->
movies.add("${movie.title} -- ${movie.episodeId}\n ${movie.characters.toString() }")
}, {
e ->
e.printStackTrace()
},{
movieAdapter?.notifyDataSetChanged()
})
</pre>
Na listagem deve aparecer o filme e os respectivos personagens. Essa requisição deve demorar vários segundos.<br />
<br />
<h3>
Fazendo cache!</h3>
Alguns personagens aparecem em vários filmes. Por isso seria interessante fazermos cache dos dados desses personagens para não fazermos requisições desnecessárias. Vamos fazer um pequeno ajuste no StarWarsApi.kt.<br />
<pre class="brush: java" name="code">var peopleCache = mutableMapOf<String, Person>()
fun loadMoviesFull(): Observable<Movie> {
return service.listMovies()
.flatMap { filmResults -> Observable.from(filmResults.results) }
.flatMap { film ->
val movieObj = Movie(film.title, film.episodeId, ArrayList<Character>())
Observable.zip(
Observable.just(movieObj),
Observable.from(film.personUrls)
.flatMap { personUrl ->
Observable.concat(
getCache(personUrl),
service.loadPerson(Uri.parse(personUrl).lastPathSegment)
.doOnNext { person ->
peopleCache.put(personUrl, person)
}
).first()
}
.map { person ->
Character(person!!.name, person.gender)
}.toList(),
{ movie, characters ->
movie.characters.addAll(characters)
movie
})
}
}
private fun getCache(personUrl : String) : Observable<Person?>? {
return Observable.from(peopleCache.keys)
.filter { key ->
key == personUrl
}
.map { key ->
peopleCache[key]
}
}
</pre>
O atributo <b>peopleCache</b> armazena as instâncias de <b>Person</b>. Então na hora que estamos varrendo a lista de personagens utilizamos o operador <b>concat().first()</b> para pegar o primeiro objeto do cache (se existir) ou da API. Quando buscamos da API, adicionamos o objeto no cache, isso é feito no método <b>doOnNext()</b>. Agora estamos fazendo o cache em memória. Mas poderíamos (e deveríamos) fazer em disco.<br />
Como comentei anteriormente, a implementação sem cache demora bastante (uns 30 segundos), mas essa implementação com cache foi bem melhor. Mesmo assim, acho que não seria legal esperar esse tempo todo para trazer a listagem. Seria melhor exibir a listagem de personagens na tela de detalhe de um único filme. Entretanto foi interessante para explorar o potencial do RX com múltiplas requisições.<br />
<br />
Ao terminar de escrever o post, notei que tinha muita informação, então resolvi fazer um vídeo mostrado passo a passo a construção do exemplo e tentando explicar melhor a implementação. Espero que gostem :)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/ZxhX7a-ScUs" width="560"></iframe></div>
<br />
<br />
Qualquer dúvida, deixem seus comentários.<br />
<br />
4br4ç05,<br />
nglauber<br />
<br />
<h3>
Referências</h3>
<b>Site oficial do Kotlin</b><br />
<a href="https://kotlinlang.org/">https://kotlinlang.org/</a><br />
<br />
<b>Site oficial do RXJava</b><br />
<a href="https://github.com/ReactiveX/RxJava">https://github.com/ReactiveX/RxJava</a><br />
<br />
<b>StarWars API</b><br />
<a href="https://swapi.co/">https://swapi.co</a><br />
<br />
<b>Apresentações Ubiratan Soares</b><br />
<i>Programação Reativa Funcional com RxJava</i><br />
Vídeo: <a href="https://www.youtube.com/watch?v=0FpphC6hL5I">https://www.youtube.com/watch?v=0FpphC6hL5I</a><br />
Slides: <a href="https://speakerdeck.com/ubiratansoares/rxjava-for-android">https://speakerdeck.com/ubiratansoares/rxjava-for-android</a><br />
<i>Refactoring for RxJava</i><br />
Vídeo: <a href="https://www.youtube.com/watch?v=391H38-7JYk">https://www.youtube.com/watch?v=391H38-7JYk</a><br />
Sllides: <a href="https://speakerdeck.com/ubiratansoares/refactoring-for-rxjava">https://speakerdeck.com/ubiratansoares/refactoring-for-rxjava</a><br />
<br />
<b>Dan Lew cache in RX</b><br />
<a href="http://blog.danlew.net/2015/06/22/loading-data-from-multiple-sources-with-rxjava/">http://blog.danlew.net/2015/06/22/loading-data-from-multiple-sources-with-rxjava/</a><br />
<br />
<b>Livro de RX</b><br />
<a href="http://www.oreilly.com/programming/free/rxjava-for-android-app-development.csp">http://www.oreilly.com/programming/free/rxjava-for-android-app-development.csp</a><br />
<br />
<b>Livro de Kotlin</b><br />
<a href="https://antonioleiva.com/kotlin/">https://antonioleiva.com/kotlin/</a>Nelson Glauberhttp://www.blogger.com/profile/02476858089223420894noreply@blogger.com6tag:blogger.com,1999:blog-3344721733578072661.post-53813359156312128562016-12-23T05:39:00.001-08:002016-12-24T07:47:45.427-08:002016 foi Loko!Olá povo,<br />
<br />
Primeiramente peço desculpas por não ter postado muito esse ano, mas se vocês lerem esse texto, vão entender um pouquinho o motivo. 2016 foi um ano muito intenso, com muitas mudanças que exigiram bastante de mim.<br />
<br />
<b>Dominando o Android 2ª edição</b><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://novatec.com.br/livros/dominando-android-2ed/" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://2.bp.blogspot.com/-JYDegQgQJD0/WF0hvxB1miI/AAAAAAAAlgs/pNwgPrpSHDksshWAdiJHP2kQ4zzqDTwVgCLcB/s200/capa-ampliada-9788575224632.jpg" width="143" /></a></div>
<b><br /></b>
No começo do ano saiu a <a href="http://novatec.com.br/livros/dominando-android-2ed/" target="_blank">2ª edição do meu livro, o "Dominando o Android"</a> com uma série de correções e novos conteúdos. Muitas coisas que eu gostaria ter colocado na primeira edição entraram nessa edição e eu fiquei muito feliz com o resultado. Ainda não estou trabalhando na 3ª edição, mas creio que teremos novidades em junho ou julho de 2017.<br />
<br />
<b>Despedida do CESAR</b><br />
<b><br /></b>
Sem sombra de dúvida o evento mais marcante para mim foi <a href="http://www.nglauber.com.br/2016/03/tchau-cesar.html" target="_blank">minha saída do CESAR</a>. No mês de março, após 10 anos, resolvi pedir demissão, simplesmente porque o que a instituição esperava de mim não estava alinhado com os meus objetivos profissionais. Então, ao invés de ficar reclamando pelos corredores ou fazer um trabalho mau feito, resolvi sair. Mas o CESAR estará sempre no meu coração por ser um lugar muito legal e cheio de pessoas bacanas. Tanto que, pelo menos uma vez por semana, saio de casa apenas para almoçar com amigos que trabalham ou trabalharam no CESAR :)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-Ju2jLfe_SH0/WF0oYxB_3TI/AAAAAAAAliE/lACJP0gppfE7TEhmUSlCulkdsvPvmEtswCLcB/s1600/image.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://1.bp.blogspot.com/-Ju2jLfe_SH0/WF0oYxB_3TI/AAAAAAAAliE/lACJP0gppfE7TEhmUSlCulkdsvPvmEtswCLcB/s320/image.jpg" width="320" /></a></div>
<div style="text-align: center;">
<span style="font-size: xx-small;">Equipe do projeto HP no CESAR</span></div>
<br />
<b>Mesa e Mokriya</b><br />
Quando estava querendo sair do CESAR, meu amigo <a href="http://blog.leocad.io/" target="_blank">Leocádio Tiné</a> me convidou para para substituí-lo lá na <a href="http://www.mesainc.com.br/" target="_blank">Mesa</a>. Uma empresa muito bacana aqui de Recife com cerca de 12 pessoas. Então em abril comecei a trabalhar lá. O desafio era bem diferente do CESAR, onde normalmente tínhamos equipes com várias pessoas trabalhando em um mesmo projeto. A demanda de aplicativos lá era/é bem grande e cada desenvolvedor ficava responsável por fazer um app do começo ao fim. Achei isso bem bacana e isso me motivou bastante.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-Ys5sXX54-5A/WF0lnLg4-8I/AAAAAAAAlhc/C0QfiZXLF1AmiOgvR3fDjKckQB7WgA0dQCLcB/s1600/13254311_1014988095259867_6401394747392985059_n.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://3.bp.blogspot.com/-Ys5sXX54-5A/WF0lnLg4-8I/AAAAAAAAlhc/C0QfiZXLF1AmiOgvR3fDjKckQB7WgA0dQCLcB/s320/13254311_1014988095259867_6401394747392985059_n.jpg" width="256" /></a></div>
<div style="text-align: center;">
<span style="font-size: xx-small;">Equipe da Mesa</span></div>
<br />
Com certeza eu ficaria na Mesa por um bom tempo, devido ao ambiente e o trabalho que eram bem legais. Mas o meu grande amigo <a href="https://www.linkedin.com/in/douglasdrumond" target="_blank">Douglas Drummond</a> ficou sabendo que eu tinha saído do CESAR e me convidou para entrar no processo seletivo da <a href="http://mokriya.com/" target="_blank">Mokriya</a>, uma empresa de Cupertino onde a maioria dos funcionários trabalha remotamente. Participei do processo, fui aprovado e como a proposta foi bem interessante, tive que deixar a Mesa para trabalhar de casa, mais uma experiência nova para mim... Tenho que escrever um post sobre isso :)<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-h1ukH01VxY0/WF0l549TZxI/AAAAAAAAlhg/YZUDyHZN3b8d3DVduppExUIrY6A8JRhLwCLcB/s1600/2016_06_01%2B10_05%2BAM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://4.bp.blogspot.com/-h1ukH01VxY0/WF0l549TZxI/AAAAAAAAlhg/YZUDyHZN3b8d3DVduppExUIrY6A8JRhLwCLcB/s320/2016_06_01%2B10_05%2BAM.png" width="320" /></a></div>
<div style="text-align: center;">
<span style="font-size: xx-small;">Pranil, eu e Sunil. Donos da Mokriya.</span></div>
<br />
Quero aproveitar para agradecer a Antônio, Arthur e Tiago que me deram essa oportunidade de fazer parte da Mesa. Infelizmente foi por pouco tempo, mas foi muito bom. E um agradecimento muito especial a Douglas por ter apostado em mim, mesmo tendo conversado pessoalmente comigo apenas duas ou três vezes. Valeu mesmo! ;)<br />
<br />
<b>Eventos nacionais</b><br />
Em termos de eventos, esse ano eu não participei tanto, comparado aos anos anteriores.<br />
Em parceria com o <a href="http://www.gdgrecife.com/" target="_blank">GDG Recife</a>, pelo segundo ano consecutivo eu fui mentor do <a href="http://developerstudyjams.com/" target="_blank">Study Jam</a> que é uma iniciativa do Google em parceria com a <a href="https://www.udacity.com/" target="_blank">Udacity</a> para ministrar cursos gratuitos sobre tecnologias Google. No meu caso, de Android. :P<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-Fp3hmovZ21Y/WF0k208Ks-I/AAAAAAAAlhQ/Ls6TyjfBS7EXLGtU1riKSPh3nHi3xcVJQCLcB/s1600/P_20160409_104947_BF.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://3.bp.blogspot.com/-Fp3hmovZ21Y/WF0k208Ks-I/AAAAAAAAlhQ/Ls6TyjfBS7EXLGtU1riKSPh3nHi3xcVJQCLcB/s320/P_20160409_104947_BF.jpg" width="320" /></a></div>
<div style="text-align: center;">
<span style="font-size: xx-small;">Turma do Study Jam</span></div>
<br />
No mês de junho participei de um dos eventos de Android que eu mais gosto: o <a href="http://www.androidosday.com/" target="_blank">Androidos day</a>. O evento esse ano contou com a participação de três GDEs de Android. Além de mim, o <a href="https://developers.google.com/experts/people/marcelo-ricardo-quinta" target="_blank">Quinta</a> e o <a href="https://developers.google.com/experts/people/ubiratan-soares" target="_blank">Bira</a> também estavam lá e enriqueceram demais o evento com suas palestras. Sem contar os outros palestrantes que deram show lá! Parabéns <a href="https://plus.google.com/+JosiasPaes" target="_blank">Josias</a> e demais integrantes da organização! Foi muito legal o evento. <br />
E você que está lendo esse post, não perca a edição de 2017! ;)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-Ce_vPhRz4VY/WF0kaYRYP0I/AAAAAAAAlhI/u3U5YbHtnGsS0jr_HjEEpVwPNDocyWpdACLcB/s1600/13568865_1357142834301594_9073007948028833740_o.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://1.bp.blogspot.com/-Ce_vPhRz4VY/WF0kaYRYP0I/AAAAAAAAlhI/u3U5YbHtnGsS0jr_HjEEpVwPNDocyWpdACLcB/s320/13568865_1357142834301594_9073007948028833740_o.jpg" width="320" /></a></div>
<div style="text-align: center;">
<span style="font-size: xx-small;">Palestrantes e organização do Androidos</span></div>
<br />
A partir do mês de julho, o GDG Recife começou a fazer mensalmente <a href="https://www.meetup.com/GDG-Recife/" target="_blank">meetups de Android</a> onde participei com algumas palestras e sugerindo o conteúdo que seria apresentado. Infelizmente não consegui organizar nada para Novembro e Dezembro, mas prometo voltar com essa iniciativa em 2017.<br />
<br />
Em Outubro teve o <a href="https://devfestne.com.br/" target="_blank">GDG DevFest Nordeste</a> que foi simplesmente fodástico! O evento aconteceu em um resort em Maceió. Um lugar fenomenal, duas trilhas de conteúdo, muuuuita gente boa palestrando. Enfim, um evento memorável. Parabéns <a href="https://developers.google.com/experts/people/juarez-filho.html" target="_blank">Juarez</a>, Juninho e todos da organização! ;)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-lxIdpjSroOg/WF0j80xBmrI/AAAAAAAAlhE/i_PUQMwF74oIOC-fXUh7GhrjrmWs4UpFQCLcB/s1600/IMG_0651.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="213" src="https://4.bp.blogspot.com/-lxIdpjSroOg/WF0j80xBmrI/AAAAAAAAlhE/i_PUQMwF74oIOC-fXUh7GhrjrmWs4UpFQCLcB/s320/IMG_0651.jpg" width="320" /></a></div>
<span style="font-size: xx-small;">Palestrantes e organização do DevFest Nordeste</span><br />
<br />
<b>Eventos internacionais</b><br />
Em maio participei mais uma vez do <a href="https://events.google.com/io2016/" target="_blank">Google I/O</a>. Dessa vez em Mountain View (nos outros anos foi em San Francisco) e em um local ao ar livre: o <a href="http://www.mountainviewamphitheater.com/" target="_blank">Shoreline Amphitheatre</a>. O evento foi bem bacana, onde o pessoal apresentou todas (ou quase todas) as novidades do Google: Google Home, Alo/Duo, Tango/Daydream, Firebase, Android Wear 2.0, Android Studio 2.2, Android N, ... Enfim, foi muito foda como sempre. \o/<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-7GavltjWBZo/WF0mmeFgzGI/AAAAAAAAlhs/8nUH54wpxIUIzK4-lzwoo39QeJsPomW5gCLcB/s1600/P_20160518_090815_BF.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://2.bp.blogspot.com/-7GavltjWBZo/WF0mmeFgzGI/AAAAAAAAlhs/8nUH54wpxIUIzK4-lzwoo39QeJsPomW5gCLcB/s320/P_20160518_090815_BF.jpg" width="320" /></a></div>
<span style="font-size: xx-small;">Eu no Google I/O</span><br />
<br />
No mês de novembro, participei do GDE Summit lá no Google, em Mountain View. Esse evento é fantástico para conhecer e rever GDEs de todo mundo, pois muitos deles já trabalharam no Google ou desempenham um papel fundamental na comunidade mundial de desenvolvedores. Além é claro, de ter contato direto com o advocates e o pessoal de produto do Google. Então você pode conversar e saber mais sobre a tendências e a direção que as tecnologias do Google estão tomando. Acho que esse foi o melhor summit que já participei. Muito conteúdo, dicas, conversas e coisas novas para brincar como o Google Home, Tango e Android Things.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-ENVUTrdrgKM/WF0m57MjY8I/AAAAAAAAlhw/qHIGLUMrxVUkjfgoCTWIrev1hFCf-fMKACLcB/s1600/IMG-20161114-WA0059.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="213" src="https://4.bp.blogspot.com/-ENVUTrdrgKM/WF0m57MjY8I/AAAAAAAAlhw/qHIGLUMrxVUkjfgoCTWIrev1hFCf-fMKACLcB/s320/IMG-20161114-WA0059.jpg" width="320" /></a></div>
<span style="font-size: xx-small;">GDEs brasileiros no GDE Summit</span><br />
<br />
Também participei do <a href="https://devfest2016.gdgsv.com/" target="_blank">DevFest Silicon Valley</a>. Foi legal, mas nem se compara aos nossos DevFests ;)<br />
<br />
<b>Certificação Android</b><br />
No Google I/O desse ano, o Google<a href="https://www.youtube.com/watch?v=Yu2oGere_Mc" target="_blank"> anunciou uma parceria com a Udacity para elaborar uma certificação oficial</a> para desenvolvedores Android: a <a href="https://www.udacity.com/google-certifications" target="_blank">Associate Android Developer</a>. Eu fiz esse exame e escrevi <a href="http://www.nglauber.com.br/2016/08/certificacao-android-da-udacity.html" target="_blank">esse post aqui</a> para explicar como funciona todo o processo.<br />
<br />
<b>Aulas na Unibratec</b><br />
Esse ano assumi apenas uma turma de pós graduação, mas que durou quase 4 meses. A coordenação da Unibratec está de parabéns por ter designado 72 horas para a disciplina de Android. Isso me permitiu trabalhar muito bem o conteúdo e foi muito enriquecedor porque a turma estava muito interessada.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-0X0ji-swons/WF0uEJOKMkI/AAAAAAAAlio/fR7B3OBEsk8Ue9dTmw4ffFvdC7og0QYDACLcB/s1600/P_20161015_143910.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="180" src="https://1.bp.blogspot.com/-0X0ji-swons/WF0uEJOKMkI/AAAAAAAAlio/fR7B3OBEsk8Ue9dTmw4ffFvdC7og0QYDACLcB/s320/P_20161015_143910.jpg" width="320" /></a></div>
<span style="font-size: xx-small;">Turma de pós graduação da Unibratec</span><br />
<br />
E para finalizar o ano, mais uma despedida: <a href="http://www.nglauber.com.br/2016/11/tchau-unibratec.html" target="_blank">esse será meu último semestre como professor da Unibratec</a>. Devido a viagens que eu tenho que fazer no meu emprego atual e por precisar dedicar mais tempo a algumas outras coisas da minha carreira, tive que me desligar da Unibratec. Ainda ficarei com as turmas de pós-graduação, mas vou dar um tempo nas turmas de graduação.<br />
<br />
--<br />
<br />
Bem pessoal, essa foi a retrospectiva de 2016. E como vocês puderam ver, esse ano foi muito LOKO! Mas foi muito bom para mim.<br />
Espero que 2017 seja muito bom também para nós todos.<br />
<br />
4br4ç05,<br />
nglauberNelson Glauberhttp://www.blogger.com/profile/02476858089223420894noreply@blogger.com2tag:blogger.com,1999:blog-3344721733578072661.post-6251388688910563552016-11-30T19:21:00.000-08:002016-11-30T19:21:22.518-08:00Tchau Unibratec :(Olá povo,<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-8Ab9orBB14s/WDzOZdHnt1I/AAAAAAAAlA4/ZozL3uHgsaESe0lWat27bwGBz2s1Wiq-QCLcB/s1600/unibratec_logo.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="66" src="https://3.bp.blogspot.com/-8Ab9orBB14s/WDzOZdHnt1I/AAAAAAAAlA4/ZozL3uHgsaESe0lWat27bwGBz2s1Wiq-QCLcB/s320/unibratec_logo.jpg" width="320" /></a></div>
2016 foi realmente o ano de encerrar ciclos na minha vida profissional. Depois de ter saído do CESAR, chegou a vez de dar mais um "até breve".<br />
<br />
Após mais de 5 anos, esse será o meu último semestre como professor da Unibratec. Foi um grande orgulho para mim, lecionar na instituição na qual fui aluno entre os anos de 2002 e 2004 no "Curso Técnico de Informática com ênfase em Desenvolvimento de Software" (ou simplesmente CTI-DS). Mas acima de tudo, foi uma ótima experiência ser docente no ensino superior e trocar conhecimento com centenas de alunos durante todo esse tempo.<br />
<br />
Comecei a ministrar aulas na Unibratec no segundo semestre de 2011, sempre com a disciplina de Programação Mobile (PGM) do curso de Análise e Desenvolvimento de Sistemas (ADS). E apesar de já ter sido convidado a ministrar outras disciplinas, preferi ficar apenas com Mobile, que é o assunto que motiva e no qual minha carreira está direcionada.<br />
<br />
Exitem várias razões me fizeram tomar a decisão de dar uma pausa nas aulas. A principal delas é que estou viajando a trabalho com uma certa frequência e está difícil conciliar as aulas com o trabalho. Outro motivo é que estou querendo estudar alguns assuntos que estão despertando meu interesse. Também preciso escrever a próxima edição do livro, melhorar meu inglês, ... enfim, preciso de mais tempo. :)<br />
<br />
Apesar dessa pausa nas aulas de graduação, continuarei ministrando aulas em turmas de pós-graduação (se me chamarem :), pois o período de dedicação (elaboração de material, correção de prova e trabalhos, lançamento de notas, etc) é menor para essas turmas.<br />
<br />
É isso pessoal! Queria agradecer a todos que me ajudaram durante esses mais de cinco anos em que fiz parte do corpo docente da Unibratec: aos meus ex-alunos, aos professores, a assistência pedagógica e todos que os que cruzaram o meu caminho nessa jornada. Mas queria deixar um agrdecimento especial aos coordenadores Hemir e a Aldo por toda paciência, aprendizado e experiência que me passaram durante esses anos.<br />
<br />
Um grande abraço e até breve!<br />
<br />
4br4ç05,<br />
nglauberNelson Glauberhttp://www.blogger.com/profile/02476858089223420894noreply@blogger.com3tag:blogger.com,1999:blog-3344721733578072661.post-54816286253503374242016-11-12T12:15:00.000-08:002016-11-12T12:15:26.315-08:00Android Libs: Data Binding + Retrofit + RXJava + Retrolambda +EventBus Olá povo,<br />
<br />
Como estou com preguiça de escrever, estou gravando alguns vídeos com conteúdos que eu acho relevante e colocando no YouTube.<br />
No vídeo abaixo eu mostro como dar os primeiros passos com as bibliotecas Data Binding, Retrofit, RXJava, Retrolambda e EventBus.<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/-HnOY4_aBHs" width="560"></iframe>
<br />
<b>Data Binding</b><br />
<a href="http://pt.slideshare.net/nglauber/dominando-o-data-binding-no-android-67628162" target="_blank">Meus slides</a><br />
<a href="http://www.nglauber.com.br/2016/05/android-data-binding.html" target="_blank">Post no blog</a><br />
<br />
<b>Retrofit</b><br />
<a href="https://square.github.io/retrofit/">https://square.github.io/retrofit/</a><br />
<br />
<b>RXJava</b><br />
<a href="https://github.com/ReactiveX/RxAndroid">https://github.com/ReactiveX/RxAndroid</a><br />
Slides do Ubiratan:<br />
<a href="https://speakerdeck.com/ubiratansoares/rxjava-for-android">https://speakerdeck.com/ubiratansoares/rxjava-for-android</a><br />
<a href="https://speakerdeck.com/ubiratansoares/refactoring-for-rxjava">https://speakerdeck.com/ubiratansoares/refactoring-for-rxjava</a><br />
<br />
<b>Retrolambda</b><br />
<a href="https://github.com/orfjackal/retrolambda">https://github.com/orfjackal/retrolambda</a><br />
Plugin do Gradle:<br />
<a href="https://github.com/evant/gradle-retrolambda">https://github.com/evant/gradle-retrolambda</a><br />
<br />
<b>EventBus</b><br />
<a href="https://github.com/greenrobot/EventBus">https://github.com/greenrobot/EventBus</a><br />
<br />
<b>OMDB API</b>: <a href="http://www.omdbapi.com/">http://www.omdbapi.com/</a><br />
<b>JSONViewer</b>: <a href="http://jsonviewer.stack.hu/">http://jsonviewer.stack.hu/</a><br />
<br />
<b>Código-fonte desse exemplo</b>:<br />
<a href="https://github.com/nglauber/playground/tree/master/android/ProjetoFilmesLibs">https://github.com/nglauber/playground/tree/master/android/ProjetoFilmesLibs</a><br />
<br />
<b>Referências</b>:<br />
<a href="https://guides.codepath.com/android/Consuming-APIs-with-Retrofit">https://guides.codepath.com/android/Consuming-APIs-with-Retrofit</a><br />
<a href="http://www.vogella.com/tutorials/Retrolambda/article.html">http://www.vogella.com/tutorials/Retrolambda/article.html</a><br />
<br />
4br4ç05,<br />
nglauberNelson Glauberhttp://www.blogger.com/profile/02476858089223420894noreply@blogger.com0tag:blogger.com,1999:blog-3344721733578072661.post-17235867940806670152016-09-20T07:23:00.000-07:002016-09-21T08:14:33.833-07:00Constraint Layout no Android Studio 2.2Olá pessoal,<br />
<br />
Com o lançamento do Android Studio 2.2, resolvi fazer um vídeo falando um pouquinho sobre o novo gerenciador de layouts do Android: o ConstraintLayout.<br />
O vídeo é um pouquinho longo (25min) e mostra como criar um layout de uma tela do aplicativo do Netflix utilizando o ConstraintLayout no novo editor visual do Android Studio.<br />
Como é algo relativamente novo, qualquer feedback é muito bem vindo.<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/dvr-yTklOUY" width="560"></iframe>
<br />
Esqueci de falar no vídeo que o ConstraintLayout é compatível com o Android 2.3 e superior.<br />
<b><br /></b>
<b>Mais informações:</b><br />
<br />
<ul>
<li><a href="http://android-developers.blogspot.com.br/2016/09/android-studio-2-2.html">http://android-developers.blogspot.com.br/2016/09/android-studio-2-2.html</a></li>
<li><a href="https://developer.android.com/training/constraint-layout/index.html">https://developer.android.com/training/constraint-layout/index.html</a></li>
<li><a href="http://tools.android.com/tech-docs/layout-editor">http://tools.android.com/tech-docs/layout-editor</a></li>
</ul>
<br />
<br />
<b>Bug report:</b><br />
<br />
<ul>
<li>No vídeo (4:50), podemos notar que o <i>wrap_content</i> não respeita as margens estipuladas. Meu amigo <a href="https://twitter.com/Diego_Figue" target="_blank">Diego Nascimento</a> notou esse problema em outras situações e reportou o bug para o Google. Eu adicionei meu comentário lá e vamos aguardar o que eles vão fazer.<br /><a href="https://code.google.com/p/android/issues/detail?id=223308">https://code.google.com/p/android/issues/detail?id=223308</a></li>
</ul>
<br />
<br />
<b>Errata:</b><br />
<br />
<ul>
<li>No vídeo foi quando falei "Auto run" ao invés de "Instant Run".</li>
</ul>
<br />
<br />
Qualquer dúvida, deixem seus comentários.<br />
<br />
4br4ç05,<br />
nglauberNelson Glauberhttp://www.blogger.com/profile/02476858089223420894noreply@blogger.com12tag:blogger.com,1999:blog-3344721733578072661.post-32717275417514579362016-09-05T15:29:00.002-07:002016-10-25T06:34:04.740-07:00DevFest Nordeste 2016<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://devfestne.com.br/" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://2.bp.blogspot.com/-rnjT3UjzRdo/V83s0KLkQRI/AAAAAAAAiwc/8DqSlllAsUsxzEX16PeZCcgPQIJGwdeiwCLcB/s320/Screen%2BShot%2B2016-09-05%2Bat%2B7.07.26%2BPM.png" width="269" /></a></div>
<span style="background-color: white; color: #333333; font-family: "arial" , "tahoma" , "helvetica" , "freesans" , sans-serif; font-size: 14.85px; line-height: 20.79px;"><br /></span>
<span style="background-color: white; color: #333333; font-family: "arial" , "tahoma" , "helvetica" , "freesans" , sans-serif; font-size: 14.85px; line-height: 20.79px;"><br /></span>
<span style="background-color: white; color: #333333; font-family: "arial" , "tahoma" , "helvetica" , "freesans" , sans-serif; font-size: 14.85px; line-height: 20.79px;">Olá povo,</span><br />
<div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px; line-height: 20.79px;">
<br />
O <a href="https://developers.google.com/events/devfest/" style="color: #007710; text-decoration: none;" target="_blank">GDG DevFest</a> é uma iniciativa do Google em parceira com os GDGs (Google Developer Groups) de todo o mundo para oferecer grandes eventos para a comunidade de desenvolvedores. A temporada dos DevFests acontece entre 1 de setembro e 30 de novembro, e no Brasil ele acontecerá em diversas regiões do país. No nordeste, o evento já aconteceu em Aracajú (2014), Recife (2015) e esse ano o <a href="https://devfestne.com.br/" style="color: #007710; text-decoration: none;" target="_blank">DevFest Nordeste</a> será realizado em Maceió-AL, nos dias <b>21 e 22 de outubro</b> no <a href="http://www.pratagy.com.br/o-resort" target="_blank">Pratagy Beach Resort</a> e contará com <a href="https://devfestne.com.br/speakers/" target="_blank">grandes palestrantes</a> de várias partes do Brasil. Serão discutidos temas relacionados as tecnologias web, backend, mobile, UX e muito mais.<br />
Eu participarei do evento com a palestra "<b>Dominando o Data Binding no Android</b>" onde veremos como otimizar a implementar a lógica da interface gráfica do aplicativo de forma mais fácil, rápida e menos sujeita a bugs. Para conferir a programação completa, <a href="https://devfestne.com.br/schedule/" target="_blank">clique aqui</a>.<br />
<br />
Para obter mais informações, consulte o <a href="https://devfestne.com.br/" style="color: #007710; text-decoration: none;" target="_blank">site oficial do evento</a>.<br />
Você não vai perder essa oportunidade de aprender e fazer parte dessa grande comunidade de desenvolvedores, não é?<br />
<br />
Nos vemos lá! ;)<br />
<br />
<b>[EDITADO 25/10/2016]</b><br />
<br />
Slides da minha palestra.<br />
<iframe allowfullscreen="" frameborder="0" height="420" marginheight="0" marginwidth="0" scrolling="no" src="//www.slideshare.net/slideshow/embed_code/key/4oMt4bF5ZW3cTA" style="border-width: 1px; border: 1px solid #ccc; margin-bottom: 5px; max-width: 100%;" width="510"> </iframe> <br />
<div style="margin-bottom: 5px;">
<br /></div>
Fotos do evento <a href="https://goo.gl/photos/ATMAU8YwxA1UpcWJ7" target="_blank">aqui</a>.
</div>
<div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px; line-height: 20.79px;">
<br /></div>
<div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px; line-height: 20.79px;">
4br4ç05,</div>
<div style="background-color: white; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px; line-height: 20.79px;">
nglauber</div>
Nelson Glauberhttp://www.blogger.com/profile/02476858089223420894noreply@blogger.com0tag:blogger.com,1999:blog-3344721733578072661.post-59644013508459523602016-08-30T12:32:00.000-07:002018-04-10T08:55:15.712-07:00Certificação Android da UdacityOlá pessoal,<br />
<br />
No Google I/O 2016 foi anunciada a primeira certificação para desenvolvedores Android. O nome da prova é <a href="https://www.udacity.com/google-certifications">Associate Android Developer Exam</a> e é mantida pela <a href="https://www.udacity.com/">Udacity</a> em parceria com o Google. O intuito dessa certificação é tornar mais fácil para empresas encontrar desenvolvedores qualificados, e obviamente, "provar" que o desenvolvedor possui o conhecimento necessário para criar aplicativos seguindo os padrões recomendados.<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/Yu2oGere_Mc" width="560"></iframe><br />
<br />
Por curiosidade, resolvi fazer essa prova para saber o nível de dificuldade e até orientar meus alunos e demais desenvolvedores interessados na certificação. Sendo assim, o objetivo desse post é dar meu feedback sobre essa prova.<br />
A preparação sugerida para a prova é fazer os <a href="https://www.udacity.com/courses/android">cursos da Udacity</a>, que em sua maioria tem o material (vídeos) gratuitos. Mas você pode fazer os cursos de formação conhecidos como Nanodegree, que atualmente estão divididos em <a href="https://br.udacity.com/course/android-basics-nanodegree-by-google--nd803">Android Basics</a> e <a href="https://br.udacity.com/course/android-developer-nanodegree-by-google--nd801">Android Developer</a>.<br />
<br />
O básico possui:<br />
<ul>
<li><a href="https://br.udacity.com/course/new-android-fundamentals--ud851">Android Fundamentals</a></li>
<li><a href="https://br.udacity.com/course/android-basics-user-input--ud836">User Inpu</a>t</li>
<li><a href="https://br.udacity.com/course/android-basics-multi-screen-apps--ud839">Multi-screen apps</a></li>
<li><a href="https://br.udacity.com/course/android-basics-data-storage--ud845">Data Storage</a></li>
<li><a href="https://br.udacity.com/course/android-basics-networking--ud843">Networking</a></li>
</ul>
<div>
Enquanto que o segundo contém:</div>
<ul>
<li><a href="https://www.udacity.com/course/developing-android-apps--ud853">Developing Android Apps</a></li>
<li><a href="https://www.udacity.com/course/gradle-for-android-and-java--ud867">Gradle for Android</a></li>
<li><a href="https://www.udacity.com/course/material-design-for-android-developers--ud862">Material Design for Android Developers</a></li>
<li><a href="https://br.udacity.com/course/android-performance--ud825">Performance</a></li>
<li><a href="https://www.udacity.com/course/advanced-android-app-development--ud855">Advanced Android Development</a></li>
</ul>
Creio que apenas os cursos básicos são necessários para a prova, então recomendo bastante vocês assistirem. Mas pela minha experiência nessa prova, se você aprendeu Android com o <a href="http://novatec.com.br/livros/google-android-5ed/">livro do Ricardo Lecheta</a> ou com o "<a href="http://novatec.com.br/livros/dominando-android-2ed/">Dominando o Android</a>", fará esse exame sem problema. Creio que esses outros cursos serão abordados nas próximas certificações (que ainda não foram lançadas).<br />
<br />
A avaliação é bem diferente das provas de certificação Java (Mobile, Programmer e Web) da Sun/Oracle, pois não é composta de uma série de perguntas de múltipla escolha e com um monte de "pegadinhas". Na certificação Android você recebe um projeto (bem pequeno, com cerca de 12 classes) com algum código já pronto. Então você precisa implementar funcionalidades e corrigir alguns bugs. O prazo para isso é de 48 horas, então reserve um tempo livre para fazer o exame.<br />
<br />
Os assuntos utilizados na prova estão em um nível bem básico. São eles: criação de layouts simples, utilização da pasta de recursos, criação de <a href="http://www.nglauber.com.br/2016/01/recylerview-no-android.html">adapter para RecyclerView</a>, definição de menu, <a href="http://www.nglauber.com.br/2013/11/contentprovider-cursorloader-e.html">ContentProvider usando SQLite</a>, <a href="http://www.nglauber.com.br/2011/08/armazenando-configuracoes-no-android.html">SharedPreferences</a>, trabalhar em background, IntentService, JobScheduler, Notification, <a href="http://www.nglauber.com.br/2012/01/widgets-service.html">AppWidgets</a> e testes com Espresso. Ou seja, a prova é bem abrangente, mas não exige um profundo conhecimento desses assuntos (principalmente sobre testes). Achei interessante eles não cobrirem/pedirem nada sobre fragments.<br />
<br />
Uma vez preparado para realizar a prova, basta se registrar e pagar a taxa. O valor é de US$149 mas, segundo a Udacity, até 31 de dezembro de 2016, o valor cobrado será de US$ 99.<br />
Ao começar a prova, será exibida uma lista de requisitos a serem implementados na aplicação. Você terá que: alterar telas existentes, criar uma nova tela, implementar um adapter, alterar um Content Provider, executar um Service, disparar notificações, agendar serviços, corrigir alguns bugs e escrever um teste simples.<br />
<br />
Concluído o projeto, você terá que enviá-lo em um arquivo zip (apague as pastas build e app/build), escanear um documento seu (eu usei minha CNH) e enviá-lo em formato PDF. Feito isso, será feita uma avaliação (tanto automática, quanto por pessoas) do seu projeto e você receberá o resultado para saber se seu projeto cumpre o que foi requisitado ou se precisa de modificações. Se ele cumprir todas as especificações, você irá para a fase de entrevista onde você poderá explicar como você implementou seu projeto. Pelo que sei, por enquanto a entrevista é apenas em inglês, mas parece que a Udacity já está trabalhando para fazê-la também em português. Sendo aprovado na entrevista, você receberá sua certificação. Segundo a Udacity, espera-se que todo esse processo dure no máximo 45 dias.<br />
<br />
Ainda estou aguardando o resultado da avaliação e consequentemente a entrevista. Assim que for andando o processo, vou atualizando o post aqui.<br />
<br />
Enfim, achei o nível da prova bem bacana para uma certificação de entrada. E acho que qualquer dev com 1 ano (ou até menos) de experiência consegue fazer.<br />
<br />
<b>[ATUALIZAÇÃO 27/09/2016]</b><br />
Recebi um email da Udacity para “verificar minha identidade”. Basicamente eles pedem para que você envie uma foto da sua identidade/carteira de motorista/passaporte. Pode ser tirada com a webcam do computador ou escaneada. Enviei a foto do passaporte para não ter o risco de problema com “documento brasileiro”. Após enviar a foto do documento, eles pedem uma foto sua. Tirei ambas com a webcam e enviei. Minutos depois, recebi um email informando que minha identificação tinha sido confirmada com sucesso. Agora creio que a próxima etapa será a entrevista<br />
<br />
<b>[ATUALIZAÇÃO 17/10/2016]</b><br />
Após 47 dias, ainda não recebi nenhum feedback sobre a certificação. Após dar aquela boa e velha "xingada no twitter", o JP (o cara do vídeo acima) respondeu...<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://twitter.com/nglauber/status/788072736291717120" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="259" src="https://1.bp.blogspot.com/-Lro7uC_k8VI/WAUc7LcXi6I/AAAAAAAAjJ4/6oA1UkfLJCwnVC31RGqF8N3ez8Nr-_5dwCLcB/s400/udacity_twitter.png" width="400" /></a></div>
Vamos ver quanto tempo demora esse "breve".<br />
<br />
<b>[ATUALIZAÇÃO 25/10/2016]</b><br />
Eis que após 55 dias recebo o email para agendar a entrevista da Certificação Android. Tentei três datas diferentes e em todas só havia um horário disponível.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-vPEnrHqtkFw/WA__RFs9hhI/AAAAAAAAjfE/4TRIuPmz4S8sR0CXPt4f_8UpG1NKdTAzgCLcB/s1600/Screen%2BShot%2B2016-10-25%2Bat%2B9.51.23%2BPM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="256" src="https://2.bp.blogspot.com/-vPEnrHqtkFw/WA__RFs9hhI/AAAAAAAAjfE/4TRIuPmz4S8sR0CXPt4f_8UpG1NKdTAzgCLcB/s400/Screen%2BShot%2B2016-10-25%2Bat%2B9.51.23%2BPM.png" width="400" /></a></div>
No email eles deixam bem claro que você precisará de um documento para comprovar sua identidade (passaporte ou carteira de motorista de preferência). Você precisará de uma webcam e de uma boa conexão de internet para não haver problema de comunicação na entrevista.<br />
Fiz o agendamento para o dia 28/10 às 20h (horário de Brasília). Vamos ver como me saio :)<br />
<br />
<b>[ATUALIZAÇÃO 28/10/2016]</b><br />
Chegou o dia da entrevista. Esqueci de mencionar na atualização anterior que no momento do agendamento, é informado o link para o aplicativo Zoom (<a href="https://zoom.us/">https://zoom.us/</a>) que você deve ter instalado na sua máquina.<br />
Minha entrevista durou exatos 5 minutos :) isso mesmo. O camarada me deu boa-noite, pediu para eu mostrar minha identidade e disse que iria me fazer 5 perguntas. Todas extremamente fáceis e nada de mostrar ou explicar código.<br />
Respondidas as questões, o entrevistador me deu os parabéns, disse que eu era um dos primeiros a terminar o processo e que em breve eu receberia um email com mais informações.<br />
<br />
<b>[ATUALIZAÇÃO 04/11/2016]</b><br />
Após uma semana de espera, chegou o email da Udacity com o resultado da avaliação.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-C3KH7xcbL9Q/WB1EJDPHqRI/AAAAAAAAjso/n5o7bALFEDUq2dULht_ZBh1dFRrRlfQQQCLcB/s1600/Screen%2BShot%2B2016-11-04%2Bat%2B11.28.09%2BPM.png" imageanchor="1"><img border="0" height="358" src="https://1.bp.blogspot.com/-C3KH7xcbL9Q/WB1EJDPHqRI/AAAAAAAAjso/n5o7bALFEDUq2dULht_ZBh1dFRrRlfQQQCLcB/s320/Screen%2BShot%2B2016-11-04%2Bat%2B11.28.09%2BPM.png" width="400" /></a></div>
Agora tenho que aguardar outro email para pegar minha badge... :)<br />
<br />
<b>[ATUALIZAÇÃO 23/11/2016]</b><br />
Chegou o email com a badge! Até que é bonitinha :)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-ISWJSlcFlYE/WDXx2BYDsNI/AAAAAAAAkw4/iYqNxEAuh6Ad3PMJ5PVWbjtOSEtLPSRLgCLcB/s1600/google_certificate.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://3.bp.blogspot.com/-ISWJSlcFlYE/WDXx2BYDsNI/AAAAAAAAkw4/iYqNxEAuh6Ad3PMJ5PVWbjtOSEtLPSRLgCLcB/s200/google_certificate.png" width="200" /></a></div>
<br />
No email, chegam as instruções para você adicionar a certificação no seu perfil do LinkedIn. Basicamente você clicar em um link, então o LinkedIn vai perguntar se você quer adicionar essa certificação ao seu perfil e pronto! Vai aparecer isso no seu perfil, na seção de certificações :)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-Fj2dr_RhR4U/WDXyZy7kvKI/AAAAAAAAkw8/igKXK7ZTtlM4VEFV1g59wziRXs0EQQXlgCLcB/s1600/Screen%2BShot%2B2016-11-23%2Bat%2B11.47.13.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="83" src="https://2.bp.blogspot.com/-Fj2dr_RhR4U/WDXyZy7kvKI/AAAAAAAAkw8/igKXK7ZTtlM4VEFV1g59wziRXs0EQQXlgCLcB/s400/Screen%2BShot%2B2016-11-23%2Bat%2B11.47.13.png" width="400" /></a></div>
<br />
Acho que esse será o último update desse post. Ciclo completo depois de 83 dias (ao contrário dos 45 prometidos pela Udacity). Espero que venham novas certificações mais exigentes e mais organizadas que essa.<br />
<br />
<b>[ATUALIZAÇÃO 22/11/2017]</b><br />
Há um tempo atrás recebi um email da Udacity pedindo algumas informações, entre elas o tamanho de camiseta. Eis que um ano após a certificação, acabam de chegar alguns "mimos" :)<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-i9ImmtoPfhE/WhW8g1K0T6I/AAAAAAAAwig/Yk3cc9MxBUYVjbQ6IqaYI9isN3XpN9AuACLcBGAs/s1600/P_20171122_145928.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="225" src="https://4.bp.blogspot.com/-i9ImmtoPfhE/WhW8g1K0T6I/AAAAAAAAwig/Yk3cc9MxBUYVjbQ6IqaYI9isN3XpN9AuACLcBGAs/s400/P_20171122_145928.jpg" width="400" /></a></div>
Uma camiseta, um bonequinho do Android e um adesivo. Achei bacaninha :) Valeu Google/Udacity!<br />
<br />
4br4ç05,<br />
nglauberNelson Glauberhttp://www.blogger.com/profile/02476858089223420894noreply@blogger.com14tag:blogger.com,1999:blog-3344721733578072661.post-1339954440761347532016-07-12T04:47:00.003-07:002016-07-12T04:47:40.552-07:00Minhas Aulas de Android no YouTubeOlá pessoal,<br />
<br />
Quando estava preparando minhas aulas de iOS (isso mesmo, já ministrei aulas de iOS ;) procurei por uma boa referência atualizada sobre o assunto e achei as <a href="http://web.stanford.edu/class/cs193p/cgi-bin/drupal/">aulas da Universidade de Stanford</a> disponíveis no iTunes. Ótimas por sinal.<br />
Seguindo esse exemplo, resolvi também gravar minhas aulas de Android na <a href="http://www.unibratec.edu.br/">Unibratec</a> e disponibiliza-las no YouTube. A gravação não tem nenhuma edição, é apenas meu desktop com o áudio no microfone embutido do computador. São 19 vídeos com mais de 20h de conteúdo.<br />
As aulas mostram desde os conceitos mais básicos, com a construção de pequenos aplicativos de exemplo, até o desenvolvimento de um projeto simples, mas que possui: uma UI diferenciada para smartphones e tablets; acesso a um servidor web para realizar a leitura de um arquivo JSON; utilização de algumas bibliotecas famosas do Android como Butter Knife, Otto, OkHttp, etc.; e persistência de dados no banco de dados SQLite.<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/videoseries?list=PLk7CjQD6o8xzViD0KKV1eeB-wc0he-TH3" width="560"></iframe>
<br />
Espero que gostem!<br />
<br />
4br4ç05,<br />
nglauberNelson Glauberhttp://www.blogger.com/profile/02476858089223420894noreply@blogger.com4tag:blogger.com,1999:blog-3344721733578072661.post-72797839040479692402016-06-20T16:04:00.001-07:002016-07-08T04:27:27.880-07:00Androidos 2016Olá pessoal,<br />
<br />
No próximo dia 02 de julho, na Unipê em João Pessoa - PB, participarei pelo segundo ano consecutivo do <a href="http://www.androidosday.com/">Androidos</a>! Um dos melhores eventos do Nordeste voltados para desenvolvedores Android e abordará temas muito legais do desenvolvimento de aplicações Android tais como: arquitetura, programação reativa, serviços de cloud, e muito mais.<br />
Este ano, o Androidos contará com a participação de três <a href="https://developers.google.com/experts/">Google Developer Experts</a> em Android do Brasil: <a href="https://developers.google.com/experts/people/marcelo-quinta">Marcelo Quinta</a> (de Goiás), <a href="https://developers.google.com/experts/people/ubiratan-soares">Ubiratan Soares</a> (de São Paulo) e <a href="https://developers.google.com/experts/people/nelson-glauber">eu</a> ;) Isso sem falar na ilustre participação do Josias Paes (Unipê) e do Gustavo Soares (IFPB).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-ZLHpjxaQLBg/V2hyz5gVw7I/AAAAAAAAhkQ/7AK7vhrvF2owJwtVzd3HMHxNOpXqk4fdwCLcB/s1600/androidos_glauber.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://3.bp.blogspot.com/-ZLHpjxaQLBg/V2hyz5gVw7I/AAAAAAAAhkQ/7AK7vhrvF2owJwtVzd3HMHxNOpXqk4fdwCLcB/s400/androidos_glauber.png" width="400" /></a></div>
<br />
Farei duas participações durante o evento: a primeira com uma palestra sobre Data Binding e log em seguida participarei de um Fireside chat, onde o público poderá fazer perguntas e tirar suas próprias dúvidas sobre Android.<br />
<br />
Se você é desenvolvedor Android, não poder perder essa oportunidade. Mais informações no <a href="http://www.androidosday.com/">site do evento</a>. Nos vemos lá!<br />
<br />
<b>[EDITADO 08/07/2016]</b><br />
Slides da minha palestra.<br />
<iframe allowfullscreen="" frameborder="0" height="357" marginheight="0" marginwidth="0" scrolling="no" src="//www.slideshare.net/slideshow/embed_code/key/sSQnJjBav4gJDH" style="border-width: 1px; border: 1px solid #ccc; margin-bottom: 5px; max-width: 100%;" width="429"> </iframe> <br />
<br />
O código-fonte do projeto mostrado nos slides está no meu GitHub.<br />
<a href="https://github.com/nglauber/playground/tree/master/android/LivrosFirebase">https://github.com/nglauber/playground/tree/master/android/LivrosFirebase</a><br />
<br />
4br4ç05,<br />
nglauber<br />
<br />Nelson Glauberhttp://www.blogger.com/profile/02476858089223420894noreply@blogger.com1tag:blogger.com,1999:blog-3344721733578072661.post-18330376513751857202016-06-17T13:30:00.000-07:002016-07-08T04:15:09.870-07:00Firebase com AndroidOlá povo,<br />
<br />
Estava com vontade de escrever uns posts sobre Firebase aqui para o blog, mas achei que eles ficariam muito grandes. Então resolvi gravar alguns vídeos, com praticamente nenhuma edição, exceto alguns cortes que eu fiz pela demora na compilação/execução da aplicação.<br />
<br />
É uma série com quatro vídeos onde abordo três dos principais serviços do Firebase: autenticação (Auth), armazenamento de arquivos (Storage) e o banco de dados em tempo real (RealTime database).<br />
<br />
A ideia aqui foi criar um cadastro de livros do zero, onde o usuário deve realizar o login utilizando sua conta do Google para acessar a aplicação. O cadastro permite digitar o título do livro, o autor, e fazer o upload da capara do livro, obtida tirando uma foto com a câmera do aparelho. Para persistir as informações utilizamos o banco de dados do firebase, e para armazenar as imagens das capas do livro usamos o serviço de storage. O resultado ficou como abaixo:<br />
<br />
<div style="text-align: center;">
<a href="https://3.bp.blogspot.com/-pplenCESoxs/V2RbA48ntMI/AAAAAAAAhfc/H5otGFgCDL06D83Q6Mqy5vFE0EUxpHlnQCLcB/s1600/d1.png"><img border="0" height="200" src="https://3.bp.blogspot.com/-pplenCESoxs/V2RbA48ntMI/AAAAAAAAhfc/H5otGFgCDL06D83Q6Mqy5vFE0EUxpHlnQCLcB/s200/d1.png" width="101" /></a> <a href="https://3.bp.blogspot.com/-cdyOGD9UQSY/V2RbAyN1uTI/AAAAAAAAhfY/D4R0hVuXec8IWXcZjYEMNnliMh_5PSqcQCLcB/s1600/d2.png"><img border="0" height="200" src="https://3.bp.blogspot.com/-cdyOGD9UQSY/V2RbAyN1uTI/AAAAAAAAhfY/D4R0hVuXec8IWXcZjYEMNnliMh_5PSqcQCLcB/s200/d2.png" width="101" /></a></div>
<br />
Como vocês podem/vão notar, não me preocupei muito com a UI. Mas isso é uma melhoria que posso fazer nos próximos vídeos. Se vocês gostarem, posso evoluir esse aplicativo e/ou mostrando mais recursos do Firebase (como o analytics, crash report, login com facebook, etc).<br />
Então, deixem seus comentários e espero que gostem! ;)<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/videoseries?list=PLk7CjQD6o8xyc2ZqwKPu0vOczGiheIrnR" width="560"></iframe>
<br />
<br />
<b>[EDITADO 08/07/2016]</b><br />
O código desse exemplo (melhorado) encontra-se no meu GitHub:<br />
<a href="https://github.com/nglauber/playground/tree/master/android/LivrosFirebase">https://github.com/nglauber/playground/tree/master/android/LivrosFirebase</a><br />
<br />
4br4ç05,<br />
nglauberNelson Glauberhttp://www.blogger.com/profile/02476858089223420894noreply@blogger.com6tag:blogger.com,1999:blog-3344721733578072661.post-9765067739015865222016-06-13T06:49:00.001-07:002016-06-13T06:51:38.385-07:00Chrome Custom Tabs para AndroidOlá povo,<br />
<br />
Mais um post rápido :) Quando precisamos abrir uma URL dentro de uma aplicação Android normalmente utilizamos uma Intent como a seguir:<br />
<pre class="brush: java">Intent it = new Intent(Intent.ACTION_VIEW,
Uri.parse("http://www.nglauber.com.br"));
startActivity(it);
</pre>
O problema de usar essa abordagem é que exige o carregamento de outro aplicativo (o que é pesado computacionalmente falando) e fará com que o usuário deixe seu aplicativo, o que não é bom. Outro ponto negativo é que uma vez que você não tem acesso ao outro aplicativo (o browser nesse caso), você não consegue fazer nenhuma customização.<br />
<br />
Uma outra abordagem é utilizar o componente WebView.<br />
<pre class="brush: java" name="code">WebView webView = (WebView)findViewById(R.id.webView);
webView.loadUrl("http://www.nglauber.com.br");
webView.setWebViewClient(new WebViewClient() {
public boolean shouldOverrideUrlLoading(WebView view, String url){
view.loadUrl(url);
return false;
}
});
</pre>
Nesse caso você possui um controle maior, uma vez que tudo é feito dentro do seu aplicativo. Entretanto, você terá que fazer vários controles para a navegação (como eu fiz aqui tratando o redirecionamento de página). Um problema aqui é que o WebView não compartilha a sessão do browser.<br />
<br />
Chrome Custom Tabs junta os pontos positivos de ambas as abordagens, pois permite o controle da experiência web dentro do seu aplicativo. Com essa API podemos: definir transições customizadas; personalizar título; adicionar ações personalizadas; fazer um pré-carregamento da página para agilizar o carregamento; entre outros.<br />
Como pode-se imaginar, esse recurso só funcionará se o usuário possuir o Chrome instalado no aparelho. Caso contrário, será disparada a Intent que mostramos no início do post.<br />
Para usar o Chrome Custom Tabs, a primeira coisa a fazer é adicionar a dependência no build.gradle.
<br />
<pre class="brush: java" name="code">dependencies {
...
compile 'com.android.support:customtabs:23.3.0'
}
</pre>
Então é só chamar a aba como a seguir:
<br />
<pre class="brush: java" name="code">String url = "http://www.nglauber.com.br";
CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
// Definindo a cor da toolbar (opcional)
builder.setToolbarColor(ActivityCompat.getColor(this, R.color.colorPrimary));
// Adicionando uma ação/menu (opcional)
Bitmap icon = BitmapFactory.decodeResource(
getResources(), android.R.drawable.ic_menu_agenda);
PendingIntent pit = PendingIntent.getActivity(
this, 0, new Intent(this, MainActivity.class), 0);
// Ação
builder.setActionButton(icon, "Ação", pit, true);
// Menu
builder.addMenuItem("Menu", pit);
// Definindo animações (opcional)
builder.setStartAnimations(this,
R.anim.slide_in_right, R.anim.slide_out_left);
builder.setExitAnimations(this,
R.anim.slide_in_left, R.anim.slide_out_right);
// Abrindo a "aba"
CustomTabsIntent customTabsIntent = builder.build();
customTabsIntent.launchUrl(this, Uri.parse(url));
</pre>
<br />
Como podemos observar, é bastante simples criar as custom tabs do Chrome. Criamos uma instância da classe CustomTabsIntent.Builder e com ela criamos podemos customizar a aba da maneira que desejarmos. Primeiro definimos a cor do título usando o método setToolbarColor(int). Em seguida instancimamos um Bitmap e uma PendingIntent que podem ser utilizadas para adicionar uma ação ou uma opção de menu na barra de título. A ação é adicionada por meio do método setActionButton(Bitmap,String,PendingIntent,boolean) e o menu por meio do método addMenuItem(String,PendingIntent).<br />
As animações foram definidas utilizando o método setStartAnimations e setExitAnimations. Essas animações foram definidas como a seguir:<br />
<pre class="brush: xml" name="code">// res/anim/slide_in_left.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="-100%p" android:toXDelta="0"
android:duration="@android:integer/config_mediumAnimTime"/>
</set>
// res/anim/slide_in_right.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="100%p" android:toXDelta="0"
android:duration="@android:integer/config_mediumAnimTime"/>
</set>
// res/anim/slide_out_left.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="0" android:toXDelta="-100%p"
android:duration="@android:integer/config_mediumAnimTime"/>
</set>
// res/anim/slide_out_right.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="0" android:toXDelta="100%p"
android:duration="@android:integer/config_mediumAnimTime"/>
</set>
</pre>
Por fim, criamos uma instância da classe CustomTabsIntent e com o método launchUrl(Activity, Uri) abrimos essa nova aba.<br />
Abaixo podemos ver a aplicação em execução.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-2Hu4ZjVdwjc/V165Ye6gWtI/AAAAAAAAhdc/4Aqir9xtLQwyXSjlxbjKRkAY__AHh3HcgCLcB/s1600/ezgif.com-resize.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://2.bp.blogspot.com/-2Hu4ZjVdwjc/V165Ye6gWtI/AAAAAAAAhdc/4Aqir9xtLQwyXSjlxbjKRkAY__AHh3HcgCLcB/s320/ezgif.com-resize.gif" width="180" /></a></div>
<span id="goog_1908393145"></span><span id="goog_1908393146"></span><br />
Mais detalhes aqui:<br />
<a href="https://developer.chrome.com/multidevice/android/customtabs">https://developer.chrome.com/multidevice/android/customtabs</a><br />
<br />
4br4ç05,<br />
nglauberNelson Glauberhttp://www.blogger.com/profile/02476858089223420894noreply@blogger.com0tag:blogger.com,1999:blog-3344721733578072661.post-43469644987867865712016-06-07T14:00:00.001-07:002016-06-07T14:00:33.995-07:00Carregando imagens da galeria do AndroidOlá povo,
<br />
<br />
Nesse post rápido vou mostrar como carregar imagens da galeria de mídia do Android. O único detalhe a observar nesse post é que estamos carregando imagens tanto locais quando as que estão em algum serviço da nuvem como DropBox e Google Photos. Por isso, para começar coloque as permissões necessárias no AndroidManifest.xml.<br />
<pre class="brush: xml" name="code"><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/>
</pre>
Lembrando que para o Android 6 (API Level 23) você deve checar essas permissões em tempo de execução.<br />
<br />
Em seguida, inicie uma nova activity utilizando a ação ACTION_GET_CONTENT.<br />
<pre class="brush: java" name="code">private static final int IMAGE_GALLERY_REQUEST = 1;
...
public void selectImageClick(View view) {
if (ActivityCompat.checkSelfPermission(this,
Manifest.permission.READ_EXTERNAL_STORAGE) ==
PackageManager.PERMISSION_GRANTED) {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(
Intent.createChooser(
intent,
getString(R.string.select_picture_title)),
IMAGE_GALLERY_REQUEST);
} else {
ActivityCompat.requestPermissions(this,
new String[]{ Manifest.permission.READ_EXTERNAL_STORAGE },
IMAGE_GALLERY_REQUEST);
}
}
</pre>
Ao selecionarmos uma imagem, o método onActivityResult será chamado.<br />
<pre class="brush: java" name="code">@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == IMAGE_GALLERY_REQUEST && resultCode == RESULT_OK){
new LoadImageTask(this).execute(data.getData());
}
}
</pre>
<br />
Como falamos anteriormente, carregaremos as imagens do sistema de arquivos ou da web. Por isso, devemos realizar essa operação fora da UI thread, então definimos a classe LoadImageTask.
<br />
<pre class="brush: java" name="code">class LoadImageTask extends AsyncTask<Uri, Void, Bitmap> {
WeakReference<PickImageActivity> mActivity;
public LoadImageTask(PickImageActivity activity) {
this.mActivity = new WeakReference<>(activity);
}
public PickImageActivity getActivity() {
return mActivity.get();
}
@Override
protected Bitmap doInBackground(Uri... params) {
if (getActivity() != null) {
try {
return BitmapFactory.decodeStream(
getActivity().getContentResolver().openInputStream(params[0]));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
return null;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
if (getActivity() != null){
getActivity().showImage(bitmap);
}
}
}
</pre>
Temos algumas coisas interessantes aqui. A primeira é essa classe não deve ser uma inner class da Activity/Fragment, pois uma inner class sempre contém uma referência para sua outer class. Uma vez que ela não é uma inner class, devemos passar a referência da activity/fragment para essa classe. Estamos utilizando uma WeakReference para armazenar a instância da activity, o motivo disso é evitar o leak de memória, já que se o usuário sair da tela antes da imagem ser baixada aquela memória ficará alocada desnecessariamente durante algum tempo.<br />
Outra coisa interessante é que estamos utilizando o método openInputStream() da classe ContentResolver para obter a imagem propriamente dita.<br />
A imagem retornada terá o seu tamanho original, se você preferir, pode utilizar a técnica de redimensionamento de imagem que eu mostrei <a href="http://www.nglauber.com.br/2011/08/galeria-de-midia-do-android.html">nesse post</a>.<br />
Por fim, no onPostExecute, invocamos o método showImage da Activity.
<br />
<pre class="brush: java" name="code">public void showImage(Bitmap bitmap) {
ImageView imageView = (ImageView)findViewById(R.id.imageView);
if (imageView != null){
imageView.setImageBitmap(bitmap);
}
}
</pre>
Qualquer dúvida, deixe seu comentário ;)<br />
<br />
4br4ç05,<br />
nglauberNelson Glauberhttp://www.blogger.com/profile/02476858089223420894noreply@blogger.com2tag:blogger.com,1999:blog-3344721733578072661.post-51247308100642792542016-05-02T06:25:00.001-07:002016-07-08T03:56:31.765-07:00Android Data BindingOlá pessoal,<br />
<br />
A API de DataBinding foi lançada no Google I/O de 2015 e tem o intuito de facilitar a vida dos desenvolvedores removendo da Activity/Fragment muita da lógica de UI feitas por esses componentes. Apesar de ter sido lançado juntamente com o Android 6, a API de Data Binding é uma biblioteca separada do sistema operacional e pode ser utilizada a partir do Android 2.1 (API Level 7). Três coisas que por si só já valem a utilização do Data Binding são: (1) não utilizar findViewById [tchau <a href="http://www.nglauber.com.br/2015/04/explorando-libs-famosas-butter-knife.html">ButterKnife</a>], (2) associação de eventos a componentes e (3) sincronização de valores do model para a view.<br />
<br />
Apesar desses três pontos já serem fantásticos, o Data Binding tem muito mais a oferecer. O objetivo desse post é mostrar como dar os primeiros passos com essa API por meio de um exemplo simples, mas funcional. O <a href="https://github.com/nglauber/playground/tree/master/android/ExemploDataBinding">código-fonte desse exemplo está disponível no meu github</a>, por isso, vou omitir algumas partes do código que não estão relacionadas ao Data Binding.<br />
<br />
<h3>
Classes básicas e configuração</h3>
O exemplo consta de uma listagem de livros utilizando a API do <a href="https://developers.google.com/books/">Google Books</a>. Então defini duas classes básicas: Book e Thumbnail.<br />
<pre class="brush: java" name="code">// Thumbnail.java
@Parcel
public class Thumbnail {
private String smallThumbnail;
private String thumbnail;
// gets e sets...
}
// Book.java
@Parcel
public class Book {
private String title;
private String subtitle;
private String publisher;
private String description;
private String[] authors;
private String publishedDate;
private int pageCount;
private Thumbnail imageLinks;
// gets e sets
}
</pre>
<br />
A única observação sobre essa classe é que estou utilizando a biblioteca <a href="https://github.com/johncarl81/parceler">Parceler</a> que facilita a implementação do <a href="http://www.nglauber.com.br/2010/01/android-passando-objetos-em-intents.html">Parcelable do Android</a>. No mais, são classes Java simples.<br />
<br />
A única configuração que deve ser feita no projeto Android é adicionar o recurso de Data Binding ao <i>build.gradle</i> do módulo da sua aplicação.<br />
<pre class="brush: java" name="code">android {
...
dataBinding {
enabled = true
}
</pre>
Pronto! Podemos começar a implementar a UI do nosso projeto.
<br />
<br />
<h3>
Um adapter utilizando Data Binding</h3>
A primeira tela do exemplo exibe uma listagem de livros retornadas pela API do Google Books. No nosso exemplo, exibiremos a capa, título e os autores do livro. Para exibir essa listagem, usaremos um adapter, no qual o arquivo de layout utilizado para representar cada linha da lista é exibido a seguir (<i>res/layout/item_book.xml</i>).<br />
<pre class="brush: xml" name="code"><layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="java.util.Arrays"/>
<variable
name="book"
type="nglauber.android.databinding.model.Book" />
</data>
<RelativeLayout ... >
<ImageView ...
android:id="@+id/image_capa"
android:src="@{book.imageLinks.smallThumbnail}"/>
<TextView ...
android:id="@+id/text_titulo"
android:text="@{book.title}" />
<TextView ...
android:id="@+id/text_autor"
android:text="@{Arrays.toString(book.authors)}" />
</RelativeLayout>
</layout>
</pre>
<br />
Os arquivos de layout que utilizam o recurso de Data Binding devem iniciar com a tag <b><layout></b>. Dentro da tag <b><data></b> devemos declarar as variáveis e importar as classes que serão utilizadas no arquivo de layout. Perceba que estamos declarando uma <b><variable></b> chamada "book" da classe Book que criamos anteriormente.<br />
No TextView que exibirá o título do livro, setamos a propriedade <span style="font-family: "courier new" , "courier" , monospace;">android:text</span> com o valor <span style="font-family: "courier new" , "courier" , monospace;">@{book.title}</span>. Desta forma, o título do livro será exibido automaticamente no componente.<br />
Ótimo, mas se você notar na nossa classe Book, o atributo "authors" é um array de strings. Para exibir os autores separado por vírgula, podemos utilizar a classe <span style="font-family: "courier new" , "courier" , monospace;">java.utils.Arrays</span>. Mas como usá-las dentro de um arquivo de layout? Basta importamos essa classe por meio da tag <b><import></b>.<br />
Outro ponto curioso nesse arquivo é que no ImageView estamos definindo na propriedade <span style="font-family: "courier new" , "courier" , monospace;">android:src</span> a imagem que será exibida. Mas se você desenvolve em Android (nem que seja a um pouquinho de tempo) deve lembrar que nessa propriedade devemos passar uma imagem que está dentro do projeto (um Drawable normalmente) e não uma URL. Mas no nosso exemplo, temos apenas o link da imagem. E agora?<br />
Com data binding, podemos definir adapters para propriedades utilizando BinderAdapters! Vejamos a classe a seguir:<br />
<br />
<pre class="brush: java" name="code">public class ImageBinding {
@BindingAdapter({"android:src"})
public static void loadImage(ImageView imageView, String url){
Glide.with(imageView.getContext()).load(url).into(imageView);
}
}
</pre>
Com a anotação <span style="font-family: "courier new" , "courier" , monospace;">@BindingAdapter</span>, informamos que estamos tratando a propriedade <span style="font-family: "courier new" , "courier" , monospace;">android:src</span> recebendo a url como parâmetro. O carregamento da imagem é feito utilizando a biblioteca <a href="https://github.com/bumptech/glide">Glide</a>. Tudo isso é feito "automagicamente" pelo plugin do Data Binding! Muito bom hein!?<br />
Veremos agora como utilizar esse arquivo de layout em um adapter.
<br />
<pre class="brush: java" name="code">public class BookAdapter extends
RecyclerView.Adapter<BookAdapter.ViewHolder> {
List<Book> mBooks;
BookClickListener mListener;
public BookAdapter(List<Book> books,
BookClickListener listener) {
mBooks = books;
mListener = listener;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
ItemBookBinding binding = DataBindingUtil.inflate(
LayoutInflater.from(parent.getContext()),
R.layout.item_book,
parent,
false);
final ViewHolder vh = new ViewHolder(binding);
vh.itemView.setOnClickListener(... );
return vh;
}
@Override
public void onBindViewHolder(ViewHolder holder, int pos) {
Book book = mBooks.get(pos);
holder.binding.setBook(book);
holder.binding.executePendingBindings();
}
@Override
public int getItemCount() {
return mBooks != null ? mBooks.size() : 0;
}
public static class ViewHolder extends
RecyclerView.ViewHolder {
ItemBookBinding binding;
public ViewHolder(ItemBookBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
}
}
</pre>
Esse é um adapter de uma RecyclerView, se você ainda não mexeu com esse componente, dê uma olhada <a href="http://www.nglauber.com.br/2016/01/recylerview-no-android.html">nesse post aqui</a>.<br />
Percebam que na classe ViewHolder, temos uma instância da classe <span style="font-family: "courier new" , "courier" , monospace;">ItemBookBinding</span>. Mas de onde saiu essa classe? Ela é gerada automaticamente pelo plugin do Data Binding e é baseada no nome do arquivo de layout (<i>res/layout/item_book.xml</i> => ItemBook + Binding) e contém todas as Views declaradas nele! <span style="color: red;">ADEUS findViewById!!!</span><br />
Notem que estamos passando o elemento raiz do arquivo de layout utilizando o método <b>getRoot()</b>.<br />
No método onCreateViewHolder, utilizamos o método inflate da classe <b>DataBindingUtil</b> para carregar o arquivo de layout e obter o objeto ItemBookBinding.<br />
Por fim, no método onBindViewHolder, onde devemos preencher as views do layout, nós simplesmente atribuímos o objeto Book do ItemBookBinding (que definimos na tag <data>) no ViewHolder. O data binding vai preencher todas as View baseado no objeto Book! <3</data>. Para que essa atualização seja feita imediatamente, invocamos o método executePendingBindings().<br />
<data><br /></data>
<br />
<h3>
Fragment de listagem</h3>
Para utilizar o adapter que acabamos de criar, crie um novo fragment (BookListFragment) e deixe o arquivo de layout (<i>res/layout/fragment_book_list.xml</i>) como a seguir:<br />
<pre class="brush: xml" name="code"><layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".BookListFragment">
<android.support.v7.widget.RecyclerView
android:id="@+id/list_livro"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_manager='@{"linear"}' />
</FrameLayout>
</layout>
</pre>
Temos uma propriedade interessante nesse arquivo de layout: <span style="font-family: "courier new" , "courier" , monospace;">app:layout_manager</span>, pois ela na verdade não existe nativamente! #confuso. Mas assim como fizemos com o carregamento da imagem, vamos utilizar o BinderAdapter para resolver essa propriedade para nós. Veja a classe a seguir.<br />
<pre class="brush: java" name="code">public class LayoutManagerBiding {
@BindingAdapter({"bind:layout_manager"})
public static void setLayoutManager(
RecyclerView recyclerView, String layout){
setLayoutManager(recyclerView, layout, 1);
}
@BindingAdapter({"bind:layout_manager", "bind:columns"})
public static void setLayoutManager(
RecyclerView recyclerView,
String layout, int columns){
if ("linear".equalsIgnoreCase(layout)){
recyclerView.setLayoutManager(
new LinearLayoutManager(
recyclerView.getContext(),
LinearLayoutManager.VERTICAL, false));
} else if ("grid".equalsIgnoreCase(layout)){
recyclerView.setLayoutManager(
new GridLayoutManager(
recyclerView.getContext(), columns));
}
}
} </pre>
Essa classe configurará o layout manager do RecyclerView. Aqui, apenas para fins de exemplo, estamos tratando os layouts linear e de grid. Sendo que para utilizarmos grid, podemos opcionalmente passar a propriedade (também customizada) <span style="font-family: "courier new" , "courier" , monospace;">app:columns</span>.
<br />
<br />
Vamos agora para a implementação da classe do Fragment.<br />
<pre class="brush: java" name="code">public class BookListFragment extends Fragment {
List<Book> mBooks;
BookAdapter mAdapter;
BookTask mTask;
FragmentListBookBinding mBinding;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
mBooks = new ArrayList<>();
}
@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
mBinding = DataBindingUtil.inflate(
inflater, R.layout.fragment_list_book, container, false);
mAdapter = new BookAdapter(mBooks, mListener);
mBinding.listLivro.setAdapter(mAdapter);
return mBinding.getRoot();
}
// Os dados são baixados assincronamente
// utilizando a API do Google Books
public void search(String term){
mTask = new BookTask(this);
mTask.execute(term);
}
public void setLivros(List<Book> books){
if (books != null) {
mBooks.clear();
mBooks.addAll(books);
}
mAdapter.notifyDataSetChanged();
}
// ...
}
</pre>
Assim como fizemos no Adapter utilizamos a classe DataBindingUtil para carregar o arquivo de layout e obter a instância da "binding class" FragmentListBookBinding.<br />
<br />
<h3>
Activity principal</h3>
Na activity principal, temos um campo onde o usuário poderá digitar o nome do livro a ser pesquisado. Ao clicar no botão iniciamos que é processada no fragment de listagem.<br />
Vejamos o arquivo de layout (activity_book.xml)<br />
<pre class="brush: xml" name="code"><layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="handler"
type="nglauber.android.databinding.BookActivity"/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".BookActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="@+id/edit_search"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{handler::onSearchClick}"
android:src="@android:drawable/ic_menu_search" />
</LinearLayout>
<fragment
android:id="@+id/fragment_list"
android:name="nglauber.android.databinding.BookListFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
</layout>
</pre>
Definimos aqui uma variável "handler" que é do tipo BookActivity. Com essa variável podemos definir QUALQUER evento no próprio arquivo de layout. No nosso exemplo definimos que o método onSearchClick da activity (o handler) será chamado quando o botão for clicado utilizando a propriedade android:onClick. Perceba que a separação do objeto com o nome do método é feita por "::".<br />
Vamos ver o código da BookActivity.<br />
<pre class="brush: java" name="code">public class BookActivity extends AppCompatActivity
implements BookClickListener {
ActivityBookBinding mBinding;
BookListFragment mListFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(
this, R.layout.activity_book);
mBinding.setHandler(this);
mListFragment = (BookListFragment)
getSupportFragmentManager()
.findFragmentById(R.id.fragment_list);
}
public void onSearchClick(View view){
mListFragment.search(
mBinding.editSearch.getText().toString());
}
@Override
public void onBookClick(Book book) {
Intent it = new Intent(this, BookDetailActivity.class);
Parcelable p = Parcels.wrap(book);
it.putExtra(BookDetailActivity.EXTRA_BOOK, p);
startActivity(it);
}
}
</pre>
No onCreate, utilizamos mais uma vez a classe DataBindingUtil, mas dessa vez chamando o método setContentView. Notem mais uma vez a classe que foi gerada: ActivityBookBinding. Setamos o "handler" do mBinding para podemos tratar o evento de clique que é feito no método onSearchClick (como definimos no arquivo de layout).<br />
<br />
<h3>
Detalhes do Livro</h3>
Na tela de detalhes do livro temos oito campos que devem ser exibidos para o usuário. Sem o data binding teríamos que pegar cada referência do componente de UI via findViewById, e atribuir o conteúdo ao componente. Aqui é outro ponto onde o benefício do Data Binding é mais evidente.<br />
Vejamos o arquivo de layout (<br />
<pre class="brush: xml" name="code"><layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="java.util.Arrays" />
<variable
name="book"
type="nglauber.android.databinding.model.Book" />
</data>
<ScrollView ... >
<LinearLayout ... >
<ImageView ...
android:id="@+id/img_capa"
android:src="@{book.imageLinks.thumbnail}" />
<TextView ...
android:id="@+id/text_titulo"
android:text="@{book.title}" />
<TextView ...
android:id="@+id/text_titulo"
android:text="@{book.title}" />
<TextView ...
android:id="@+id/text_subtitulo"
android:text="@{book.subtitle}" />
<TextView ...
android:id="@+id/text_autor"
android:text="@{@string/format_authors(Arrays.toString(book.authors))}" />
<TextView ...
android:id="@+id/text_ano"
android:text="@{@string/format_publish_date(book.publishedDate)}" />
<TextView ...
android:id="@+id/text_pages"
android:text="@{@string/format_pages(book.pageCount)}" />
<TextView ...
android:id="@+id/text_description"
android:text="@{book.description}" />
</LinearLayout>
</ScrollView>
</layout>
</pre>
Todas a propriedades do livro estão sendo atribuídas diretamente no arquivo de layout. A curiosidade aqui fica por conta das strings formatadas que utilizamos para os atributos autores, data de publicação e número de páginas. Essas strings estão no res/values/strings.xml como a seguir.
<br />
<pre class="brush: java" name="code"><string name="format_authors">Autores: %1$s</string>
<string name="format_pages">Páginas: %1$d</string>
<string name="format_publish_date">Data de publicação: %1$s</string>
</pre>
Vamos agora para o fragment de detalhe BookDetailFragment.
<br />
<pre class="brush: java" name="code">public class BookDetailFragment extends Fragment {
private static final String EXTRA_BOOK = "livro";
public static BookDetailFragment newInstance(Book book) {
BookDetailFragment fragment = new BookDetailFragment();
Bundle args = new Bundle();
Parcelable p = Parcels.wrap(book);
args.putParcelable(EXTRA_BOOK, p);
fragment.setArguments(args);
return fragment;
}
@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
Book book = null;
if (getArguments() != null) {
Parcelable p = getArguments().getParcelable(EXTRA_BOOK);
book = Parcels.unwrap(p);
}
View view = inflater.inflate(
R.layout.fragment_detail_book, container, false);
FragmentDetailBookBinding fdlb =
FragmentDetailBookBinding.bind(view);
fdlb.setBook(book);
return view;
}
}
</pre>
Nada de especial nessa classe que já não vimos anteriormente. Mas já pensou como ela seria sem o data binding? :) 8 findViewById + 7 setText + 1 Image load...<br />
Abaixo temos a aplicação em execução.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-fxuAUsIy3-w/VyZxweVvp0I/AAAAAAAAgU0/XiLIFMPi0Jc5w-5MScDkb6Y_3rNkCAufQCLcB/s1600/device-2016-05-01-181148.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://3.bp.blogspot.com/-fxuAUsIy3-w/VyZxweVvp0I/AAAAAAAAgU0/XiLIFMPi0Jc5w-5MScDkb6Y_3rNkCAufQCLcB/s320/device-2016-05-01-181148.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
Aplicação no Nexus 7</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-qKSJ_Tqe338/VyZxwS3rmUI/AAAAAAAAgUs/TBAyAzPVqG8NohgMihSGtl6JlYRmQ0cFQCLcB/s1600/device-2016-05-01-181300.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://3.bp.blogspot.com/-qKSJ_Tqe338/VyZxwS3rmUI/AAAAAAAAgUs/TBAyAzPVqG8NohgMihSGtl6JlYRmQ0cFQCLcB/s320/device-2016-05-01-181300.png" width="180" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
Tela de Listagem no Nexus 5</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-H6PH0ff-w1I/VyZxwXE_bLI/AAAAAAAAgUw/5d6HBszlEaUGKflb-cGcG6swa3PiitcuwCLcB/s1600/device-2016-05-01-181321.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://3.bp.blogspot.com/-H6PH0ff-w1I/VyZxwXE_bLI/AAAAAAAAgUw/5d6HBszlEaUGKflb-cGcG6swa3PiitcuwCLcB/s320/device-2016-05-01-181321.png" width="180" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
Tela de detalhes no Nexus 5</div>
<br />
Ao terminar de escrever esse artigo só fiquei com uma dúvida: Data Binding, porque você não existe desde a versão 1.5 :) onde você estava???<br />
<br />
<h3>
Mais informações</h3>
Minha palestra sobre Data Binding<br />
<a href="http://www.nglauber.com.br/2016/06/androidos-2016.html">http://www.nglauber.com.br/2016/06/androidos-2016.html</a><br />
Palestra sobre Data Binding no Android Dev Summit (em inglês)<br />
<a href="https://www.youtube.com/watch?v=NBbeQMOcnZ0">https://www.youtube.com/watch?v=NBbeQMOcnZ0</a><br />
Hangout com o Neto Marin (Google Developer Advocate)<br />
<a href="https://www.youtube.com/watch?v=JWpn4yyIJxc">https://www.youtube.com/watch?v=JWpn4yyIJxc</a><br />
Data Binding Guide (Documentação oficial)<br />
<a href="http://developer.android.com/intl/pt-br/tools/data-binding/guide.html">http://developer.android.com/intl/pt-br/tools/data-binding/guide.html</a>
<br />
<br />
Qualquer dúvida, deixem seus comentários.<br />
<br />
4br4ç05,<br />
nglauberNelson Glauberhttp://www.blogger.com/profile/02476858089223420894noreply@blogger.com18tag:blogger.com,1999:blog-3344721733578072661.post-64427608632281376372016-04-04T17:17:00.000-07:002016-06-07T12:35:29.813-07:00Carregando imagens de outras fontes com o PicassoOlá pessoal,<br />
<br />
As bibliotecas de carregamento de imagens são utilizadas em praticamente todos os projetos Android. Elas facilitam o carregamento de imagens a partir de diversas fontes como: web, sistema de arquivos e até de banco de dados. Possuem recursos de redimensionamento, crop, placeholder, animação, etc. As mais famosas desse segmento são: <a href="http://square.github.io/picasso/">Picasso</a>, <a href="https://github.com/bumptech/glide">Glide</a> e <a href="https://github.com/nostra13/Android-Universal-Image-Loader">Universal Image Loader (UIL)</a>. Todas são ótimas, já resolvi uns problemas com uma, outros problemas com outra e por aí vai.<br />
No projeto que estou atualmente estou usando o Picasso, e estava precisando carregar imagens de um local não suportado nativamente por ele: o <a href="http://developer.android.com/intl/pt-br/google/play/expansion-files.html">arquivo de expansão de APK</a>.<br />
Graças ao grande mestre <a href="http://jakewharton.com/">Jake Wharton</a>, podemos implementar isso de uma forma muito fácil. Basta criar uma subclasse de RequestHandler.<br />
<pre class="brush: java" name="code">import com.squareup.picasso.Request;
import com.squareup.picasso.RequestHandler;
public class MeuRequestHandler extends RequestHandler {
public MeuRequestHandler() {
}
@Override
public boolean canHandleRequest(Request data) {
// Retorne true se essa classe pode tratar a leitura da imagem
return data != null
&& data.uri != null
&& data.uri.getScheme() != null
&& data.uri.getScheme().startsWith("ngvl");
}
@Override
public Result load(Request request, int networkPolicy)
throws IOException {
Bitmap imagem = metodoQueCarregaSeuBitmap(request.uri);
Result result = new Result(imagem, Picasso.LoadedFrom.DISK);
return result;
}
}
</pre>
Essa classe possui apenas dois métodos:<br />
<ul>
<li><b>canHandleRequest(Request)</b> define se essa classe é capaz de carregar uma determinada imagem. Nesse exemplo, estou usando o parâmetro Request para checar se o endereço (definido por uma Uri) começa com "ngvl". </li>
<li>Já o método <b>load(Request, int)</b> carrega a imagem em si e a retorna por meio de um objeto Result, que recebe o Bitmap da imagem e de onde ela foi carregada (memória, disco ou rede).</li>
</ul>
Criado o handler, basta adicioná-lo a uma instância do Picasso.<br />
<pre class="brush: java" name="code">Picasso = mPicassoInstance =
new Picasso.Builder(mContext.getApplicationContext())
.addRequestHandler(new MeuRequestHandler())
.build();
</pre>
Com isso, você está adicionando uma nova fonte de imagens à sua instância do Picasso, ou seja, você pode usar todos os schemas já suportados pelo Picasso (http, file, content, ...) e esse que acabamos de criar.<br />
Agora, se invocarmos o código a seguir, o nosso handler tratará essa requisição.<br />
<pre class="brush: java" name="code">mPicassoInstance
.load("ngvl://minhaimagem/logo.jpg")
.into(imageView);
</pre>
<br />
<b><a href="https://plus.google.com/communities/116342551728637785407">#perfmatters</a> <span style="color: red;">#protip</span></b> É importante que só haja uma instância desse objeto para evitar problemas de memória! Sendo assim, implemente um <a href="https://en.wikipedia.org/wiki/Singleton_pattern">Singleton</a>, e caso precise de um <a href="http://developer.android.com/reference/android/content/Context.html">Context</a>, passe o <a href="http://developer.android.com/reference/android/content/Context.html#getApplicationContext()">getApplicationContext()</a> para ele ;)<br />
<br />
4br4ç05,<br />
nglauberNelson Glauberhttp://www.blogger.com/profile/02476858089223420894noreply@blogger.com3tag:blogger.com,1999:blog-3344721733578072661.post-14972202703824068562016-03-19T13:47:00.002-07:002016-06-07T12:35:45.197-07:00Customizando o TextInputLayout com ImageSpanOlá povo,<br />
<br />
Post curto do dia. Estava querendo customizar o erro do TextInputLayout (TIL) e só consegui graças a ajuda da <a href="https://twitter.com/lisawrayz">Lisa Wray</a>, que deu a ótima sugestão de utilizar as classes <a href="http://developer.android.com/reference/android/text/SpannableStringBuilder.html">SpannableStringBuilder</a> + <a href="http://developer.android.com/reference/android/text/style/ImageSpan.html">ImageSpan</a>, que eu não conhecia.<br />
<br />
Muitos devs Android, quando vêm algum método que recebe um objeto <a href="http://developer.android.com/reference/java/lang/CharSequence.html">CharSequence</a> como parâmetro, pensam imediatamente é o mesmo que passar uma String. Mas na verdade o CharSequence é bem mais poderoso, pois permite ir além de textos planos. A classe SpannableStringBuilder é uma subclasse de CharSequence que permite adicionar <a href="http://developer.android.com/reference/android/text/Spannable.html">Spannable</a>'s como o ImageSpan.<br />
Vejamos o código a seguir.
<br />
<pre class="brush: java" name="code">TextInputLayout textInputLayout = (TextInputLayout) editText.getParent();
if (campoInvalido()) { // sua verificação do campo vem aqui.
// Cria a imagem que será incluída no texto do erro.
// O alinhamento poderia ser ALIGN_BASELINE
ImageSpan imageSpan = new ImageSpan(
this, R.drawable.ic_erro, DynamicDrawableSpan.ALIGN_BOTTOM);
// O SpannableStringBuilder substituirá o primeiro caracter pela imagem
// por isso o " " no começo.
SpannableStringBuilder ssbErrorMessage =
new SpannableStringBuilder(" "+ getString(R.string.msg_erro));
// Adicionando a imagem como texto do erro
ssbErrorMessage.setSpan(imageSpan, 0, 1, 0);
// Habilita o erro do TIL
textInputLayout.setErrorEnabled(true);
// define a mensagem de erro
textInputLayout.setError(ssbErrorMessage);
// Atualiza o componente
textInputLayout.refreshDrawableState();
}
</pre>
O resultado ficará como a imagem abaixo.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-8KpvK5X8hqU/Vu23XjbUtAI/AAAAAAAAeho/kKzLQ-0fY1MnoWDywg6ibtQ8FpT5-d4TA/s1600/Screen%2BShot%2B2016-03-19%2Bat%2B5.32.01%2BPM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="192" src="https://4.bp.blogspot.com/-8KpvK5X8hqU/Vu23XjbUtAI/AAAAAAAAeho/kKzLQ-0fY1MnoWDywg6ibtQ8FpT5-d4TA/s320/Screen%2BShot%2B2016-03-19%2Bat%2B5.32.01%2BPM.png" width="320" /></a></div>
Dois detalhes importantes nesse exemplo:<br />
1) essa abordagem pode ser utilizada por qualquer componentes que receba um CharSequence \o/<br />
2) Podemos utilizar qualquer Bitmap como imagem ;)<br />
<br />
4br4ç05,<br />
nglauberNelson Glauberhttp://www.blogger.com/profile/02476858089223420894noreply@blogger.com0tag:blogger.com,1999:blog-3344721733578072661.post-45854158888339757912016-03-15T08:24:00.001-07:002016-06-07T12:36:06.526-07:00ViewPager sem FragmentPagerAdapter + IndicatorOlá povo,<br />
<br />
Resolvi escrever esse post porque toda vez que eu preciso disso tenho que procurar na internet. Não que seja difícil de achar, mas me vi na obrigação moral de fazer :)<br />
Quando utilizamos o ViewPager, normalmente estamos fazendo alguma tela de abas ou algo similar. Nesse caso é recomendado fazer com que cada aba/página seja um Fragment. Mas se não houver nenhuma lógica nessas páginas, criar um fragment pode ser desnecessário.<br />
Posso dar como exemplo aquelas telas de boas-vindas que mostram um breve tutorial de como utilizar a aplicação, onde cada passo é uma página com as instruções. Nesse caso, utilizar um layout (definido em um arquivo de layout) para cada página seria o suficiente, e não precisaríamos de Fragment para tal.<br />
Vamos ver como fazer isso, começando pelo arquivo de layout a seguir.<br />
<br />
<pre class="brush: xml" name="code"><LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<com.viewpagerindicator.CirclePageIndicator
android:id="@+id/indicator"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:paddingBottom="10dp"
android:paddingTop="10dp"
android:background="@color/colorPrimary"/>
</LinearLayout>
</pre>
<br />
Esse arquivo possui um ViewPager e um <a href="http://viewpagerindicator.com/">CirclePageIndicator</a> (criado por Jake Wharton) que servirá para exibir um indicador da página atual. Para utiliza-lo, basta adicionar no build.gradle a dependência do componente.
<pre class="brush: java" name="code">dependencies {
...
compile 'com.android.support:appcompat-v7:23.2.0'
compile 'com.github.JakeWharton:ViewPagerIndicator:2.4.1@aar'
}
</pre>
Vejamos agora o código que cria as páginas do ViewPager.
<br />
<pre class="brush: java" name="code">public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewPager viewPager = (ViewPager)findViewById(R.id.viewpager);
viewPager.setAdapter(new ViewSimplesAdapter());
CirclePageIndicator indicator =
(CirclePageIndicator)findViewById(R.id.indicator);
indicator.setViewPager(viewPager);
}
private class ViewSimplesAdapter extends PagerAdapter {
@Override
public int getCount() {
return 3;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
int layout;
if (position == 0){
layout = R.layout.layout_pagina1;
} else if (position == 1){
layout = R.layout.layout_pagina2;
} else {
layout = R.layout.layout_pagina3;
}
View view = LayoutInflater.from(container.getContext())
.inflate(layout, container, false);
container.addView(view);
return view;
}
@Override
public void destroyItem(ViewGroup container, int position,
Object object) {
container.removeView((View)object);
}
}
}
</pre>
A classe ViewSimplesAdapter herda de PagerAdapter e nela, temos o método getCount() que retorna a quantidade páginas que iremos exibir e mais três métodos:<br />
<ul>
<li>isViewFromObject() - basicamente determina se uma View está relacionada com o objeto retornado pelo método instantiateItem().</li>
<li>instantiateItem() carrega o arquivo de layout da página específica. Perceba que a View carregada é adicionada ao ViewGroup recebido como parâmetro e é retornada pelo método.</li>
<li>destroyItem() vai ser responsável por destruir os itens criados. É importante ressaltar que o ViewPager mantém no máximo três páginas ativas: a que está sendo exibida, a anterior e a posterior.</li>
</ul>
Qualquer dúvida, deixem seus comentários.<br />
<br />
4br4ç05,<br />
nglauberNelson Glauberhttp://www.blogger.com/profile/02476858089223420894noreply@blogger.com6tag:blogger.com,1999:blog-3344721733578072661.post-33333061403973237102016-03-10T10:42:00.000-08:002016-03-10T10:42:30.065-08:00Tchau CESAR :(<a href="https://2.bp.blogspot.com/-yjFtMBtkk3c/VthDBwAcPVI/AAAAAAAAc5Y/WTbXv_afbok/s1600/foto_1_logo_cesar.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="119" src="https://2.bp.blogspot.com/-yjFtMBtkk3c/VthDBwAcPVI/AAAAAAAAc5Y/WTbXv_afbok/s200/foto_1_logo_cesar.png" width="200" /></a>Olá povo,<br />
<br />
No dia 16 de janeiro de 2006 eu começava a trabalhar no Centro de Estudos e Sistemas Avançados do Recife. Ainda estava terminando a faculdade e lembro que meu sonho era "usar o crachá laranja".<br />
Hoje, 10 de março de 2016 é o meu último dia de trabalho no CESAR. Foram mais de 10 anos de muito desafios, aprendizados, crescimento pessoal e profissional, e muita história boa para contar.<br />
Tive a satisfação de trabalhar com muita gente boa <strike>(e outras nem tão legais assim)</strike> e fiz muitas amizades em todos os projetos em que passei <strike>eu acho</strike>.<br />
<br />
O nome do CESAR estará cravado na minha história para sempre, com muito orgulho, pois foi nessa empresa fantástica que construí a maior e melhor parte da minha carreira. Isso sem contar a parte acadêmica, já que eu cursei o mestrado no CESAR.edu.<br />
<br />
Mas como é evidente a todos que me conhecem, estou fortemente envolvido com a área de desenvolvimento para dispositivos móveis, e nos últimos meses não estava tendo a oportunidade de trabalhar com mobile e nem com desenvolvimento de software (codificação em si). Isso acabou me deixando um pouco desmotivado, então resolvi conversar com meus superiores e verificar a possibilidade de me alocar em um projeto mobile dentro da instituição. Mas infelizmente, fui informado de que não havia previsão de novos projetos com mobile no curto/médio prazo. Sendo assim, chegamos a conclusão de que meu plano de carreira não estava alinhado com as expectativas do CESAR, então resolvi procurar novos desafios em um novo lugar.<br />
<br />
Espero de todo coração estar deixando as portas abertas, pois o CESAR é um lugar incrível para trabalhar, que eu considero minha segunda casa, onde (quem sabe?) um dia terei a satisfação de voltar.<br />
Obrigado a todos vocês que fizeram parte da minha vida durante esses 10 anos, vocês me fizeram muito feliz. :)<br />
<br />
Chegou a hora de começar uma nova etapa(!) e escrever um novo capítulo da minha história.<br />
<br />
4br4ç05,<br />
nglauber<br />
<br />Nelson Glauberhttp://www.blogger.com/profile/02476858089223420894noreply@blogger.com5