terça-feira, 31 de janeiro de 2012

Fragmentos para todos

Olá povo,

Uma das novidades do Android 3 (Honeycomb), foi a adição do conceito de Fragments. Com essa versão foi escrita exclusivamente para tablets, os "fragmentos" permitem a separação de uma Activity em pedaços de modo a aproveitar os grande tamanho de tela desse tipo de dispositivo. Os Fragments funcionam como sub-activities (dentro de uma Activity), e têm seu próprio ciclo de vida, tratando também os eventos dos componentes nele contidos.

Você pode estar se perguntando: ótimo, mas isso é só pro Android 3 ou superior, então não roda no 2.x. Você estaria certo se a Google não tivesse disponibilizado uma API de compatibilidade, que permite usufruir de alguns recursos do Honeycomb em aparelhos 1.6 (Donut) ou superior. Essa biblioteca está disponível no Android SDK Manager, o mesmo lugar onde você baixa os pacotes necessários para o desenvolvimento Android. Essa API é basicamente um JAR que deve ser adicionado no seu projeto Android.

A Figura abaixo mostra a opção para baixar a API de compatibilidade no Android SDK Manager.

EDITADO EM 10/08/2012
Na versão atual do plugin, essa lib é adicionada automaticamente ao projeto.

Uma vez baixado, o JAR vá até a pasta do SDK e procure pelo arquivo android-support-v4.jar. Ele pode estar na pasta extras/android/compatibility/v4 ou android-compatibility/v4 (dependendo do S.O.).
Crie um novo projeto Android selecionando a versão 1.6 ou superior, e na raiz do projeto, crie uma pasta lib. Copie o JAR para essa pasta e depois adicione-o ao Build-Path do projeto (botão direito sobre JAR, Build Path > Add to build path). Em seguida, clique com o botão direito sobre o projeto e clique em propriedades. Por fim, selecione Java Build Path e na aba Order and Export, marque o android-support-v4.jar.

Antes de começarmos a codificar, vou explicar como será o exemplo. Será uma aplicação clássica de Fragments, onde teremos uma lista, e ao clicar em um dos elementos dessa lista, os detalhes daquele item serão exibidos. Como exemplo, listarei as versões do Android e ao clicar, exibir os detalhes da respectiva versão.

Vou começar criando o layout principal da aplicação. Crie o arquivo res/layout/titles.xml e deixe-o conforme abaixo:
<LinearLayout 
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:orientation="horizontal" >

  <fragment
    android:id="@+id/titles"
    android:layout_width="0px"
    android:layout_height="fill_parent" 
    android:layout_weight="1"
    class="ngvl.android.fragments2.TitlesFragment" />

  <FrameLayout
    android:id="@+id/details"
    android:layout_width="0px"
    android:layout_height="fill_parent"
    android:layout_weight="1" />
</LinearLayout>

Aqui temos um LinearLayout horizontal com um Fragment que está apontando para a classe TitlesFragment e está ocupando metade da tela (determinado pela propriedade layout_weight). E um FrameLayout para exibir os detalhes da opção selecionada.

O arquivo acima dará erro porque não criamos a classe TitlesFragment. Ela é mostrada abaixo:
public class TitlesFragment extends ListFragment {

  private final String EXTRA_INDEX = "index";
  private int selectedIndex;
 
  @Override
  public void onActivityCreated(
    Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
  
    ListView lv = getListView();
  
    setListAdapter(
      new ArrayAdapter<String>(
      getActivity(),
      android.R.layout.simple_list_item_checked,
      Dados.titles));
  
    lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
  
    if (savedInstanceState != null){
      selectedIndex = 
        savedInstanceState.getInt(EXTRA_INDEX);
      lv.setSelection(selectedIndex);
    }

    if (isDualPane()){
      showDetails(selectedIndex);
    } else {
      getActivity().findViewById(
        R.id.details).setVisibility(View.GONE);
    }
  
    lv.setItemChecked(selectedIndex, true);  
  }

  private void showDetails(int index) {
    selectedIndex = index;
  
    if (isDualPane()) {  
      DetailsFragment details = 
        (DetailsFragment)getFragmentManager()
          .findFragmentById(R.id.details);
   
      if (details == null || 
        details.getShowIndex() != index){

        details = DetailsFragment.newInstance(index);
    
        FragmentTransaction fragTrans =
          getFragmentManager().beginTransaction();
        fragTrans.replace(R.id.details, details);
        fragTrans.setTransition(
          FragmentTransaction.TRANSIT_FRAGMENT_FADE);
        fragTrans.commit();
      }
    } else {
      Intent it = new Intent(getActivity(), 
        DetailsActivity.class);

      it.putExtra(EXTRA_INDEX, index);
      startActivity(it);
    }
  }
 
  @Override
  public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt(EXTRA_INDEX, selectedIndex);
  }
 
  @Override
  public void onListItemClick(ListView l, 
    View v, int position, long id) {
    super.onListItemClick(l, v, position, id);
    showDetails(position);
  }
 
  private boolean isDualPane(){
    Configuration config =
      getActivity().getResources().getConfiguration();

    return config.orientation == 
      Configuration.ORIENTATION_LANDSCAPE;
  }
}

Nosso fragmento herda de ListFragment, que é bem similar a classe ListActivity, e exibe informações em formato de lista. Então, no método onActivityCreated (o nome por sí só já diz o que ele faz :) obtemos a referência para a ListView e setamos um Adapter para ela. Os dados que são passados para esse Adapter vêm de um array de Strings contendo as versões do Android definidos na classe Dados. Parte dessa classe é mostrada abaixo:
public class Dados {
  static public String[] titles = {
    "Android 1.5",
    "Android 1.6",
    // Mais itens aqui
  }

  static public String[] description = {
    "Primeira versão do Android, Cupcake",
    "Donut já suporta Fragments",
    // Mais descrições aqui
  }
}

Em seguida, verificamos se existe um estado salvo da nossa Activity. Quando entramos a primeira vez, esse estado estará vazio, mas quando girarmos o aparelho, o método onSaveInstance será chamado e nós salvaremos a posição da lista que está checada. Se já existir um estado saldo, marcamos a opção que estava selecionada antes de girar o aparelho e fazemos o scroll até ela.
Logo após, verificamos se a aplicação é dualPane (dois painéis) esse método (que está no fim da classe) verifica se a orientação do aparelho está como landscape. Em caso positivo exibiremos a lista e os detalhes no mesmo momento, caso contrário exibiremos apenas a lista, então ocultamos o FrameLayout de detalhes.
O método showDetails verifica se a tela é dualPane, em caso positivo, instancia o fragmento de detalhes e o exibe utilizando a class FragmentTransition. Caso não seja dualPane, ou seja, está em Portrait, exibimos a Activity de detalhes.

Com a utilização de Fragments, a Activity passa a controlar o Fragment, e o Fragment trata eventos de UI. Com isso a Activity fica bem simples como podemos ver abaixo:
public class TitleActivity extends FragmentActivity {
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.titles);
  }
}

O único detalhe é que essa clase herda de FragmentActivity. Lembrando que ela é uma classe da API de compatibilidade, no Android 3.x podemos continuar usando Activity mesmo. Agora vamos analisar o fragmento de detalhes
public class DetailsFragment extends Fragment {

  private static final String EXTRA_INDEX = "index";
 
  public static DetailsFragment newInstance(int index) {
    DetailsFragment fragment = new DetailsFragment();
  
    Bundle params = new Bundle();
    params.putInt(EXTRA_INDEX, index);
    fragment.setArguments(params);
  
    return fragment;
  }
 
  public int getShowIndex() {
    return getArguments().getInt(EXTRA_INDEX);
  }

  @Override
  public View onCreateView(LayoutInflater inflater,
    ViewGroup container, Bundle savedInstanceState) {
  
    TextView txt = new TextView(getActivity());
    txt.setText(Dados.description[getShowIndex()]);
  
    return txt;
  }
}

A classe tem um Factory Method (chamado newInstance) que cria um DetailsFragment já passando os "extras" necessários. O método onCreateView define o que será exibido no Fragment, para simplificar, criamos apenas um TextView, mas você poderia usar o parâmetro inflater para carregar um arquivo de Layout que desejasse.
Por fim, vamos mostrar a Activity de detalhes.
public class DetailsActivity extends FragmentActivity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
  
    if (isDualPane()){
      finish();
      return;
   
    } else if (savedInstanceState == null) {
  
      DetailsFragment details = new DetailsFragment();
      details.setArguments(getIntent().getExtras());
   
      getSupportFragmentManager()
        .beginTransaction()
        .add(android.R.id.content, details)
        .commit();
    }
  }
 
  private boolean isDualPane(){
    Configuration config =
      getResources().getConfiguration();

    return config.orientation ==
      Configuration.ORIENTATION_LANDSCAPE;
  }
}

Nessa Activity, basicamente verificamos se a orientação é landscape, em caso positivo, essa Activity é fechada, e consequentemente a TitlesActivity é exibida mostrando os títulos e os detalhes do mesmo. Já se Activity não é dual pane (ou seja, é portrait) e foi passado o índice para ela, carregamos o Fragment e o adicionamos à tela.

A imagem abaixo mostra nossa aplicação rodando em landscape.

Esse artigo foi baseado nesse post do Blog do Android Developers.

Qualquer dúvida, deixem seus comentários,

4br4ç05,
nglauber

sábado, 14 de janeiro de 2012

Android + iOS = Adobe Air

Olá povo,

Atualmente a disputa entre as plataformas para dispositivos móveis está bastante acirrada. Android e iOS travam uma verdadeira batalha pela preferência dos usuários, tanto no mercado de tablets quanto de smartphones. E nós, como desenvolvedores, temos de estar atentos a essa peleja.
Imagine que você recebe uma demanda de um projeto em que um dos requisitos, é rodar em iOS e Android. Nesse caso, o primeiro pensamento normalmente é: "putz! vou ter que fazer duas aplicações?".
Quem me conhece, sabe que meu projeto de mestrado foi o desenvolvimento de uma ferramenta que traduz aplicações iOS para Android. Mas infelizmente ela ainda está longe de ficar pronta :( Mesmo assim, durante as pesquisas do mestrado, analisei algumas ferramentas "concorrentes" da minha como o PhoneGap e Titanium. Entretanto, dentre todas, a que gostei mais foi o Adobe Air. Entre os pontos positivos da tecnologia estão: a produtividade, utilizando uma ferramenta RAD com diversos recursos; a linguagem de programação é dinâmica e bastante fácil de aprender; e o aspecto visual que se mantém igual no Android e no iOS.

A aplicação é desenvolvida em Flex e/ou ActionScrpit com o Flash Builder (ou Flash Professional CS5). Nesse post, vou mostrar como iniciar o desenvolvimento e fazer o bom e velho "Hello World". Para começar, você precisa fazer o download do Flash Builder 4.6 no site da Adobe. Ele é uma IDE baseada em Eclipse, onde você pode baixar um trial de 60 dias, e após esse tempo deve ser adquirido. Faça o download e instale o Flash Builder Trial no seu computador e vamos começar a brincadeira :)

Acesse o menu File > New > Flex Mobile Project. Na janela exibida abaixo, digite o nome do projeto e o local onde o projeto será salvo e clique em Next.

Na próxima janela do assistente, selecionamos para quais plataformas iremos desenvolver: iOS, Android e/ou BlackBerry. Deixei aqui Android e iOS. Na aba "Application Template" selecionamos o formato de aplicação que devemos desenvolver, aqui deixei uma aplicação Blank. As outras opções permitem criar aplicações que naveguem entre telas ou com abas respectivamente.
Na aba "Permission", selecionamos as permissões necessárias para acessar recursos de aplicações Android (para iOS ela não é usada). E em "Platform Settings", selecionamos para quais aparelhos iOS iremos desenvolver (iPad, iPod, iPhone).
Na seção "Application settings", marcamos se a aplicação deve mudar de orientação (portrait e landscape) automaticamente; se será tela cheia; e se a aplicação será esticada para se ajustar a telas de diferentes densidades. Deixe as configurações como abaixo e clique em Next.

Na próxima tela poderíamos selecionar se estamos utilizando alguma tecnologia servidor. Mas como não é o caso, clique em Next.
Na última tela do assistente, vamos deixar as opções padrão, exceto pelo campo Application ID, onde preencheremos com o esquema similar ao que usamos em Java. ex:"ngvl.air.exemploblog". Outro campo interessante dessa tela, é o "Main Application File" onde definimos o arquivo que será o ponto de partida da nossa aplicação. Aqui, deixamos HelloWorldBlog.mxml e clicamos em Finish.

Ao iniciar o projeto, o arquivo HelloWorldBlog.mxml é aberto em modo texto, mas vamos primeiro edita-lo no modo design. Para tal, clique no botão na parte superior do editor de código, e no combo "Device" selecione um aparelho de sua preferência.

Na paleta de componentes, localizada do lado esquerdo, arraste para a tela de design: um Label, um TextInput e um Button. De um duplo-clique nos componentes Label e Button para alterar o texto. E deixe conforme abaixo:

Agora vá para o modo Source e deixe conforme abaixo;
<?xml version="1.0" encoding="utf-8"?>
<s:Application
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
applicationDPI="160">

<fx:Script>
<![CDATA[
protected function botaoFilhaoClick(
event:MouseEvent):void
{
lblTexto.text = edtTexto.text;
}
]]>
</fx:Script>
<fx:Declarations>
</fx:Declarations>
<s:Label
id="lblTexto"
x="39" y="48"
text="Olá mundo Adobe Air"/>
<s:TextInput
id="edtTexto"
x="39" y="71"/>
<s:Button
x="39" y="112"
label="Vai filhão!"
click="botaoFilhaoClick(event)"/>
</s:Application>

Em seguida, clique com o botão direito sobre o projeto e selecione Run As... > Mobile Application. Aparecerá a janela abaixo. Selecione a plataforma que deseja testar (aqui usei Android) e em Launch Method você pode escolher entre rodar em um simulador ou em um dispositivo real. Aqui, selecionei para rodar no Desktop, e então o emulador com o tamanho de tela do Motorola Defy +. Clique em Run.

A figura abaixo mostra nossa aplicação em execução no simulador do Adobe Air. Ao clicar no botão, o texto digitado na caixa de texto é exibido no Label.


Bem povo, esse é apenas um pontapé inicial. Espero colocar mais posts sobre Adobe Air aqui no blog. Aproveito para agradecer ao meu grande amigo Eric Cavalcanti(@ericoc), com quem tive aulas de Adobe Air e me ajudou a iniciar nessa tecnologia.

Dúvidas? Deixem seus comentários.

4br4ç05,
nglauber

segunda-feira, 2 de janeiro de 2012

Widgets + Service

Olá povo,

Eu já coloquei aqui no blog dois posts sobre widgets (veja aqui e aqui), mas para quem não os viu, um AppWidget é uma aplicação que roda na HomeScreen dos aparelhos Android. E um dos grandes problemas ao se desenvolver esse tipo de aplicação é que o tratamento dos eventos que são disparados por ele. Eles podem ser feitos com uma subclasse de AppWidgetProvider (que herda de BroadcastReceiver), entretanto eles não mantêm estado. Por ser um BroadcastReceiver, o seu programa só interage com ele durante a execução do método onReceive.

Alguns colegas de trabalho (que não vou citar nomes :) usam classes com atributos estáticos para manter os estado dos AppWidgets. Mas digamos que essa não é a maneira mais elegante :) Por isso, hoje vou mostrar como integrá-los com o componente Service do Android.

O componente Service permite a sua execução em segundo plano mesmo que não haja nenhuma tela da aplicação aberta. Seu ciclo de vida inicia quando é disparada uma Intent para a classe do serviço utilizando o método startService. Caso o serviço ainda não esteja sendo executado, o mesmo será iniciado e ficará ativo até que o método stopService seja chamado ou que o próprio serviço chame o método stopSelf.

Tendo em vista essa característica do serviço, vou utilizá-lo nesse exemplo para guardar o estado do widget, que ficará fazendo chamadas ao serviço para obter informações e se atualizar. Nosso exemplo será um widget que mostrará alguns links favoritos que nós poderemos passar clicando nos botões de navegação.

Vamos começar pelo código do Service, que está na classe MeuServico

public class MeuServico extends Service {

// Sites favoritos
private String[] sites = {
"nglauber.blogspot.com",
"developer.android.com",
"www.unibratec.edu.br",
"www.especializa.com.br",
"www.cesar.edu.br" };

// índice do site que deve ser mostrado no widget
private int indice;

@Override
public int onStartCommand(Intent intent, int flags,
int startId) {
// O Widget vai chamar esse método cada vez que
// clicarmos no botão
// Obtendo o botão clicado (próx. ou ant.)
String acao = intent.getStringExtra(
MeuWidget.EXTRA_ACAO);

// Se foi próximo, incrementa o índice
if (MeuWidget.ACAO_PROXIMO.equals(acao)) {
indice++;

if (indice > sites.length - 1) {
indice = 0;
}

// Se foi anterior, decrementa o índice
}else if(MeuWidget.ACAO_ANTERIOR.equals(acao)){
indice--;
if (indice < 0) {
indice = sites.length - 1;
}
}
// Se veio alguma ação, atualize o widget
if (acao != null) {
RemoteViews views = new RemoteViews(
this.getPackageName(), R.layout.main);

// Atualizando TextView pra exibir o site
views.setTextViewText(
R.id.txtSite, sites[indice]);

// ID do widget que deve ser atualizado
int appWidgetId = intent.getIntExtra(
AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);

// Obtendo a instância do AppWidgetManager
// para atualizar o Widget
AppWidgetManager appWidgetManager =
AppWidgetManager.getInstance(this);

// atualiza o widget
appWidgetManager.updateAppWidget(
appWidgetId, views);
}

return super.onStartCommand(
intent, flags, startId);
}

@Override
public IBinder onBind(Intent arg0) {
// Não usado aqui
return null;
}
}

O código acima, cria um serviço que tem uma lista de links favoritos, e um índice que controlará qual link deve ser exibido. O widget então fará requisições ao serviço, que serão tratadas pelo método onStartCommand, que mostrará um link da lista baseado na posição do array.
É importante ter em mente que o widget não roda no processo da nossa aplicação, e sim da HomeScreen, então para ter acesso aos componentes gráficos usamos a classe RemoteViews e atualizamos o seu conteúdo com a classe AppWidgetManager.

Uma vez que o Service está pronto, vamos começar a criar o widget. Assim como uma Activity, ele terá um arquivo de layout. Utilizei o próprio res/layout/main.xml que é criado com o projeto. Ele ficou conforme abaixo:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal"
android:background="#000000"
android:layout_margin="20dp">
<Button
android:id="@+id/btnAnterior"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:text="<" />
<TextView
android:id="@+id/txtSite"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_gravity="center"
android:layout_weight="1"
android:text="Clique para navegar nos favoritos"/>
<Button
android:id="@+id/btnProximo"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:text=">" />
</LinearLayout>

Nada de especial no Layout acima, então vamos ver como fica o código do Widget.

public class MeuWidget extends AppWidgetProvider {
public static final String EXTRA_ACAO = "acao";
public static final String ACAO_ANTERIOR="anterior";
public static final String ACAO_PROXIMO = "proximo";

@Override
public void onUpdate(Context context,
AppWidgetManager appWidgetManager,
int[] appWidgetIds) {

super.onUpdate(context,
appWidgetManager, appWidgetIds);

// As view estão na aplicação Home e não na
// nossa. Por isso, para obter as referências
// dos componentes é usada a classe RemoteViews
RemoteViews views = new RemoteViews(
context.getPackageName(), R.layout.main);

// Esse método recebe a lista dos widgets que
// devem ser atualizados, pois podem haver várias
// instâncias do mesmo na Home.
// Então devemos atualizar todos.
for (int i = 0; i < appWidgetIds.length; i++){
// Configurando evento dos botões
setButtonClicks(context, appWidgetIds[i], views);
}
appWidgetManager.updateAppWidget(
appWidgetIds, views);
}

private void setButtonClicks(
Context context, int appWidgetId,
RemoteViews views) {
// Os eventos de click só podem disparar
// PendingIntents. No nosso caso,
// dispararemos PendingIntents para o nosso
// serviço. Para setar o evento, passa o id
// do componentes e a PendingIntent
views.setOnClickPendingIntent(
R.id.btnProximo,
servicePendingIntent(
context, ACAO_PROXIMO, appWidgetId));

views.setOnClickPendingIntent(
R.id.btnAnterior,
servicePendingIntent(
context, ACAO_ANTERIOR, appWidgetId));
}

private PendingIntent servicePendingIntent(
Context ctx, String acao, int appWidgetId){

// Criando a Intent para chamar o serviço
Intent serviceIntent = new Intent(
ctx, MeuServico.class);

// Passando a ação (proximo ou anterior)
serviceIntent.putExtra(EXTRA_ACAO, acao);

// Passando o ID do widget que clicou
serviceIntent.putExtra(
AppWidgetManager.EXTRA_APPWIDGET_ID,
appWidgetId);

// O requestId da PendingIntent deve ser único,
// então gero um número aleatório
int requestId = new Random().nextInt();

// criando a PendingIntent para o serviço
PendingIntent pit = PendingIntent.getService(
ctx, requestId, serviceIntent, 0);
return pit;
}
}

No código acima, o método onUpdate é chamado quando o widget é adicionado à tela. Note que a referência aos componentes é feita através da classe RemoteViews, este é outro conceito que não deve ser esquecido: o widget roda na aplicação Home, logo, essas Views lá. Outro detalhe interessante é que os componentes só podem disparar PendingIntents ao serem clicados. E no nosso caso, essas PendingIntents estão chamando o Service, mudando apenas os parâmetros que são passados (próximo e anterior).

Agora vamos criar o arquivo de configuração do widget. Na pasta res, crie a pasta xml (minúsculo) e adicione o arquivo meuwidget.xml e coloque o conteúdo abaixo:

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/main"
android:minHeight="72dp"
android:minWidth="294dp" >
</appwidget-provider>

Assim como todo BroadcastReceiver ele deve ser declarado no AndroidManifest.xml, só que ele deve tratar a ação APPWIDGET_UPDATE e deve ter uma tag meta-data referenciando o arquivo de configuração que criamos anteriormente.

<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="br.edu.cesar.aula12"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk android:minSdkVersion="10" />

<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >

<receiver
android:name="MeuWidget"
android:label="Meu Widget">
<!-- IntentFilter obrigatória
para todo widget -->
<intent-filter>
<action
android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
</intent-filter>
<!-- Arquivo de configuração do widget -->
<meta-data android:resource="@xml/meuwidget"
android:name="android.appwidget.provider"/>
</receiver>

<!-- Meu Serviço -->
<service android:name="MeuServico"/>
</application>
</manifest>

Mande rodar a aplicação. Nada irá aparecer, pois você não tem nenhuma Activity no projeto. Para ver se o Widget está funcionando, dê um clique-longo na Home ou clique na tecla "Menu" e em seguida, selecione "Add". No pop-up que for exibido, selecione "Widgets", e depois "Meu Widget". A mensagem inicial do widget será exibida, depois é clicar nos botões para ver o resultado.

Como vocês observaram dexei o código comentado, mas quem tiver dúvidas, deixem seus comentários.

4br4ç0s,
nglauber