Mostrando postagens com marcador Tutorial. Mostrar todas as postagens
Mostrando postagens com marcador Tutorial. Mostrar todas as postagens

terça-feira, 10 de julho de 2018

Como eu aprendi (um pouco sobre) RXJava

Olá povo,

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 ;)
Resolvi compartilhar com vocês uma lista de links sobre RXJava que me ajudaram a entender um pouquinho sobre o assunto.

Android MeetUp 3 Anos - Programação Reativa Funcional com RxJava (Ubiratan Soares)
https://www.youtube.com/watch?v=0FpphC6hL5I

Refactoring for RxJava (Ubiratan Soares)
https://www.youtube.com/watch?v=391H38-7JYk

Rxify - a simple spell for complex RxJava operators (Garima Jain)
https://speakerdeck.com/ragdroid/rxify-a-simple-spell-for-complex-rxjava-operators

Exploring RxJava 2 for Android (Jake Wharton)
https://www.youtube.com/watch?v=htIXKI5gOQU

5 Not So Obvious Things About RxJava (Jag Saund)
https://medium.com/@jagsaund/5-not-so-obvious-things-about-rxjava-c388bd19efbc

Migrating from RxJava 1.0 to RxJava 2.0 and Learn RxJava by Examples (Amit Shekhar)
https://blog.mindorks.com/migrating-from-rxjava1-to-rxjava2-5dac0a94b4aa

An Introduction to Functional Reactive Programming (Dan Lew)
http://blog.danlew.net/2017/07/27/an-introduction-to-functional-reactive-programming/

Grokking RxJava (Dan Lew)
http://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/
http://blog.danlew.net/2014/09/22/grokking-rxjava-part-2/
http://blog.danlew.net/2014/09/30/grokking-rxjava-part-3/

How to make complex requests simple with RxJava in Kotlin (Tamás Kozmér)
https://blog.mindorks.com/how-to-make-complex-requests-simple-with-rxjava-in-kotlin-ccec004c5d10

Don't break the chain: use RxJava's compose() operator (Dan Lew)
http://blog.danlew.net/2015/03/02/dont-break-the-chain/

A Complete Guide To Learn RxJava (Amit Shekhar)
https://blog.mindorks.com/a-complete-guide-to-learn-rxjava-b55c0cea3631

Multithreading with RxJava (Pierce Zaifman)
https://android.jlelse.eu/multithreading-with-rxjava-dadddc4f7a63

Adopting RxJava on Airbnb Android (Felipe Lima)
https://academy.realm.io/posts/kau-felipe-lima-adopting-rxjava-airbnb-android/

Começando com ReactiveX no Android (Ashraff Hathibelagal)
https://code.tutsplus.com/pt/tutorials/getting-started-with-reactivex-on-android--cms-24387

Programação Reativa com RxAndroid (Pedro Di Moura)
https://speakerdeck.com/pedrodimoura/programacao-reativa-com-rxandroid

Replace AsyncTask and AsyncTaskLoader with rx.Observable – RxJava Android Patterns (Ross Hambrick)
https://stablekernel.com/replace-asynctask-and-asynctaskloader-with-rx-observable-rxjava-android-patterns/

RxJava cheat sheet, with a pinch of Android (Oleg Shelajev)
https://zeroturnaround.com/rebellabs/rxjava-cheat-sheet-with-a-pinch-of-android/

RXJava2 by Example (Victor Grazi)
https://www.infoq.com/articles/rxjava2-by-example

4br4ç05,
nglauber

quinta-feira, 8 de setembro de 2011

Twitter API no Android

Olá povo,

Nunca me interessei em testar APIs para redes sociais por dois motivos: não gosto muito dessa febre; e pensava que era bem trivial ao ponto de ter milhões de tutoriais por aí. Grande engano. Quando fui adicionar suporte ao Twitter em uma aplicação que estou trabalhando, notei que existem algumas etapas 'chatinhas' para realizar essa integração. Não sei se a forma que estou utilizando aqui é a melhor, inclusive, se vocês tiverem sugestões, deixem seus comentários :)

Sem mais "delongas", vamos começar! Nesse post, vou mostrar como realizar o processo de autenticação no Twitter e 'twitar' uma mensagem. Para começar, obviamente você deve ter uma conta comum no Twitter. Para ter acesso aos recursos da API, você (como desenvolvedor) deverá cadastrar sua aplicação no site https://dev.twitter.com. Esse cadastro servirá para a equipe do Twitter saber qual aplicação (e consequentemente o login) responsável pelo cliente que está acessando o Twitter. Isso é similar ao que é feito quando utilizamos a API de Mapas do Google, onde você deve usar sua conta do Google para obter uma chave, para então utilizar o serviço.



O cadastro é bem simples, basta informar: o nome da aplicação, descrição da mesma, URL para contato e uma URL de callback. Este último é mais importante para aplicações web, onde após efetuar o login, a chave de acesso será direcionada.
Uma vez cadastrada, serão geradas alguma informações que serão utilizadas para integração da nossa aplicação com o Twitter. Nós precisaremos da "Consumer Key" e "Consumer Secret".

Como funciona a autenticação no Twitter?
Até meados do segundo semestre de 2010, o acesso ao twitter funcionava basicamente passando o login e a senha em texto plano. Por motivos óbvios de segurança, essa forma foi descontinuada, e passou-se a utilizar a forma de autenticação OAuth. Esse processo é ilustrado na figura abaixo:

Não vou entrar em muitos detalhes sobre esse protocolo, mas basicamente o cliente envia um solicitação de acesso utilizando a consumerKey e consumerSecret para o servidor, o servidor então retorna uma URL para que o usuário se autentique. Nesse momento o usuário insere seu usuário e senha do Twitter. Uma vez passadas essas informações, o servidor valida esses dados e gera um token de acesso, que será usada para toda a comunicação com o Twitter. Esse tone deve ser salvo no cliente para que ele possa se comunicar com o servidor sem ter passar o login e a senha a cada transacção.

Mãos à obra!
De posse do conceito sobre a autenticação do Twitter, vamos implementar nosso exemplo. Ele constará de apenas uma tela, onde o usuário poderá fazer o login/logout no Twitter e atualizar o seu status a partir da mesma. A tela da aplicação é exibida abaixo:


Você pode implementar a comunicação com o Twitter do zero, utilizando JSON. Mas existem algumas APIs prontas que facilitam nosso trabalho. A que utilizarei aqui é a Twitter4J. Baixe a última versão estável, descompacte em algum local do seu HD. Crie um novo projeto Android, e copie o arquivo twitter4j-core-android-X.X.X.jar que está na pasta lib do Twitter4J para a pasta lib do seu projeto Android (essa pasta não é criada por padrão, então crie na raiz do seu projeto). Em seguida, adicione esse JAR no Build-Path do Eclipe clicando com o botão direito sobre o arquivo, e depois selecionando Build Path > Add to Build path. Seu projeto deve ficar como na figura abaixo.



Vamos ao código :) Vou começar pelo AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="ngvl.android.twitter"
android:versionCode="1"
android:versionName="1.0">

<uses-sdk android:minSdkVersion="10" />
 
<uses-permission android:name="android.permission.INTERNET"/>

<application
 android:icon="@drawable/icon"
 android:label="@string/app_name">
 <activity
   android:name=".ExemploTwitterActivity"
   android:label="@string/app_name"
   android:noHistory="true">
   <intent-filter>
     <action 
      android:name="android.intent.action.MAIN"/>
     <category 
       android:name="android.intent.category.LAUNCHER"/>
   </intent-filter>
   <intent-filter>
    <action 
      android:name="android.intent.action.VIEW"/>
     <category 
       android:name="android.intent.category.DEFAULT"/>
     <category 
       android:name="android.intent.category.BROWSABLE"/>
     <data android:scheme="nglauber-android"/>
   </intent-filter>
 </activity>
</application>
</manifest>

Aqui nesse arquivo temos algumas coisas muito importantes:
- A tag uses-permission para nossa aplicação poder acessar internet;
- Foi adicionada a propriedade andróide:noHistory à tag activity, para que quando o Browser seja aberto para o usuário fazer o login, ao voltar, ele não crie outra instância dessa activity.
- Essa activity tem dois intent-filter. O primeiro é pra execução normal da aplicação e o segundo é usado pela página do Twitter. Após a autenticação, ele redirecionará para o endereço nglauber-android://?oauthverifier=sua_chave. Estamos informando que essa atividade trata esse "protocolo", dessa forma nossa activity será re-executada.

Agora vamos ver o código da Activity.
package ngvl.android.twitter;

import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.auth.AccessToken;
import twitter4j.auth.RequestToken;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

public class ExemploTwitterActivity extends Activity {

  private final String 
    consumerKey = "SUA CONSUMER KEY";
  private final String 
    consumerSecret = "SUA CONSUMER SECRET";
  private final String 
    CALLBACKURL = "nglauber-android:///";

  private Twitter twitter;

No código acima, as constantes cosumerKey e consumerSecret devem ser preenchidas com as informações do cadastro da sua aplicação no site do Twitter. Já a constante CALLBACKURL deve estar igual a que foi declarada no AndroidManifest.xml.
O atributo twitter proverá acesso ao processo de login e de atualização do status.

@Override
public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  
  twitter = new TwitterFactory().getInstance();
  twitter.setOAuthConsumer(
    consumerKey, consumerSecret);
}

No onCreate apenas inicializamos o layout da tela, obtemos a instância do Twitter através da classe TwitterFactory e setamos a consumer key e secret.
 public void clickLogin(View v) {
  try {
    AccessToken accessToken = loadAccessToken();
    if (accessToken == null) {
      twitter = new TwitterFactory().getInstance();
      twitter.setOAuthConsumer(
        consumerKey, consumerSecret);
    
      RequestToken requestToken = 
        twitter.getOAuthRequestToken(CALLBACKURL);
 
      String url = requestToken.getAuthenticationURL();
      Intent it = new Intent(
        Intent.ACTION_VIEW, Uri.parse(url));
      it.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
      startActivity(it);
    
      saveRequestData(requestToken.getToken(), 
        requestToken.getTokenSecret());
   
    } else {
      twitter.setOAuthAccessToken(accessToken);
    }
  } catch (Exception e) {
    e.printStackTrace();
    showToast(e.getMessage());
  }
} 

O método acima, iniciará o processo de login e será chamado ao clicar no botão de login. Através do método loadAccessToken (definido mais adiante), ele tenta obter um objeto AccessToken salvo anteriormente. Caso não exista, o processo de login inicia com a obtenção da url para fazermos o login no Twitter. Notem que é passada a constante CALLBACKURL como parâmetro, isso indica qual endereço será chamado quando o login for concluído. Essa é uma URL "fake" que será chamada quando o login for efetuado. Neste momento, o browser redireciona a chave para nglauber-android://?oauthverifier=sua_chave, e o "protocolo" nglauber-android está sendo tratado por nossa atividade, como definido no AndroidManifest.xml.
A flag FLAG_ACTIVITY_NO_HISTORY evita que, ao pressionar back após o login, o browser seja exibido novamente. Após chamar o browser para o login, o token e o token secret da requisição são salvos pelo método saveRequestData (veremos esse método mais adiante).

Ao clicar no botão de login, será exibida a tela de autenticação do Twitter. Aqui é solicitado autorização para que a aplicação que cadastramos no começo desse post tenha acesso a sua conta do Twitter. Digite seu login e senha e clique em "Sign In".


Após realizar o login, a página do Twitter envia a chave para nossa Activity. Tratamos o recebimento dessa chave no método onResume conforme abaixo:
 @Override
protected void onResume() {
  super.onResume();
  
  Uri uri = getIntent().getData();
  if (uri != null) {
    String oauthVerifier = 
      uri.getQueryParameter("oauth_verifier");
 
    try {
      RequestToken requestToken = loadRequestToken();
      AccessToken at = twitter.getOAuthAccessToken(
        requestToken, oauthVerifier);

      saveAccessToken(
        at.getToken(), at.getTokenSecret());

    } catch (TwitterException e) {
      e.printStackTrace();
      showToast(e.getMessage());
    }
  }
}

Quando a atividade é chamada pela página do twitter, uma chave de verificação da requisição de acesso é retornada. Com a requisição e a chave de verificação podemos criar um objeto AccessToken para acessar os recursos do Twitter. O token e o token secret do objeto AccessToken são salvos no método saveAccessToken (definido mais adiante) e, como vimos anteriormente podem ser recuperados quando clicamos no botão de login.

Abaixo temos o método que é chamado ao clicar no botão Tweet.

public void clickTweet(View v) {
  try {
    if (loadAccessToken() != null){
      EditText edt = 
        (EditText) findViewById(R.id.editText1);
      String tweet = edt.getText().toString();

      twitter.updateStatus(tweet);
      showToast("Status atualizado com sucesso!");

    } else {
      showToast("Faça o login antes de Twittar");
    }
  } catch (TwitterException e) {
      e.printStackTrace();
      showToast(e.getMessage());
  }
}

Nada de mais aqui. Apenas pega o conteúdo da caixa de texto e manda alterar o status do objeto Twitter.

Abaixo temos método auxiliares que utilizamos no nosso código. O primeiro exibe um Toast na tela.
private void showToast(String s){
  Toast.makeText(this, s, Toast.LENGTH_LONG).show();
}

Os métodos que salvam e recuperam as chaves de solicitação (RequestToken) e de acesso (AccessToken) estão definidos abaixo. Para ambos são utilizados SharedPreferences.
 
private RequestToken loadRequestToken(){
  SharedPreferences prefs = PreferenceManager.
    getDefaultSharedPreferences(this);
  String reqToken = 
    prefs.getString("request_token", null);
  String reqTokenSecret = 
    prefs.getString("request_tokensecret", null);
  
  return new RequestToken(reqToken, reqTokenSecret);
}
 
private void saveRequestData(
  String requestToken, String requestTokenSecret){

  SharedPreferences prefs = PreferenceManager.
    getDefaultSharedPreferences(this);
  SharedPreferences.Editor editor = prefs.edit();

  editor.putString(
    "request_token", requestToken);
  editor.putString(
    "request_tokensecret", requestTokenSecret);

  editor.commit();  
}
 
private AccessToken loadAccessToken() {
  SharedPreferences prefs = PreferenceManager.
    getDefaultSharedPreferences(this);
  String acToken = 
    prefs.getString("access_token", null);
  String acTokenSecret = 
    prefs.getString("access_tokensecret", null);

  if (acToken != null || acTokenSecret != null){
    return new AccessToken(acToken, acTokenSecret);
  }
  return null;
}
 
private void saveAccessToken(
  String accessToken, String accessTokenSecret) {

  SharedPreferences prefs =
    PreferenceManager.getDefaultSharedPreferences(this);

  SharedPreferences.Editor editor = prefs.edit();

  editor.putString(
    "access_token", accessToken);
  editor.putString(
    "access_tokensecret", accessTokenSecret);

  editor.commit();
}

Por fim, o método que faz o logout, limpa os valores da sharedpreference de AccessToken, o que vai forçar a realização de um novo login.
public void clickLogout(View v) {
  saveAccessToken(null, null);
}


É isso pessoal, qualquer dúvida, deixem seus comentários.

4br4ç05,
nglauber

sexta-feira, 22 de outubro de 2010

Android Live Wallpaper

Olá povo,

Engraçado, esse post aqui eu jurava que já tinha escrito, mas quando pesquisei aqui no blog, vi que tinha ficado só na imaginação :) Então vamos lá. Hoje quero falar um pouquinho de um recurso muito bacana que foi introduzido no Android 2.1: os papéis de parede animados ou simplesmente live wallpapers.

Os live wallpapers são diferentes dos papéis de parede comuns pois são dinâmicos e permitem interação com o usuário. Com isso, ao invés de termos uma imagem parada, eles podem exibir animações e até mesmo mudar de comportamento de acordo com o hora do dia, posição do telefone (baseado no acelerômetro), entre outros.

Temos alguns exemplos bem bacanas que já vêm no próprio Android. O live wallpaper "Água" mostra algumas folhas secas descendo na tela como se estivesse sobre a água, e quando o usuário toca na área de trabalho do telefone faz-se uma onda como se o usuário estivesse tocando sobre água.
Outro exemplo interessante é o "Grama". Ele mostra um gramado balançando com o vento. Com o céu ao fundo, ele muda de cor de acordo com a hora do dia.



Vou apresentar um exemplo simples de um papél de parede animado que mostrará o texto passando da tela e a cor da tela mudando aleatoriamente quando o usuário tocar na tela.
A estrutura do projeto ficará como abaixo:


Vamos começar de baixo pra cima. Vejamos com está o AndroidManifest.xml.


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

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

<service
android:name=".MeuPapelDeParede"
android:permission="android.permission.BIND_WALLPAPER">

<intent-filter>
<action
android:name="android.service.wallpaper.WallpaperService" />
</intent-filter>

<meta-data
android:name="android.service.wallpaper"
android:resource="@xml/meupapeldeparede" />
</service>

<activity
android:name=".MeuPapelDeParedeConfig"
android:exported="true">
</activity>
</application>

<uses-sdk android:minSdkVersion="7" />
<uses-feature
android:name="android.software.live_wallpaper" />
</manifest>

Notem que temos um serviço e uma atividade declaradas no manifest. O serviço é o papel de parede em si. Ele exige a permissão BIND_WALLPAPER para poder defini-lo como wallpaper do telefone. Além disso ele tem a uma ação (action) específica para obedecer à chamadas do S.O. para o pel de parede animado. A tag meta-data referencia o arquivo de configuração do wallpaper, que está em res/xml/meupapeldeparede.xml. Esse arquivo vai definir a tela de configuração do papel de parede que é a atividade declarada no nosso manifest.

<wallpaper
xmlns:android="http://schemas.android.com/apk/res/android"
android:thumbnail="@drawable/icon"
android:settingsActivity="ngvl.android.wallpaper.MeuPapelDeParedeConfig"
/>

Essa tela de configuração permitirá ao usuário configurar o texto que aparecerá no papel de parede e armazenará automaticamente em uma SharedPreference.

<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/app_name"
android:key="meupapeldeparede_config">

<EditTextPreference
android:key="texto_do_papel_de_parede"
android:title="Texto do Papel de Parede" />
</PreferenceScreen>



public class MeuPapelDeParedeConfig
extends PreferenceActivity {

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

getPreferenceManager().setSharedPreferencesName(
MeuPapelDeParede.SHARED_PREFS_NAME);

addPreferencesFromResource(R.xml.tela_de_config);
}
}

Agora vamos ver a classe que faz o trabalho legal :)

public class MeuPapelDeParede
extends WallpaperService {

// Contante da SharedPreference do WallPaper
public static final String SHARED_PREFS_NAME =
"meupapeldeparede_config";

public static final String PREF_KEY =
"texto_do_papel_de_parede";

// Handler que ficará recebendo requisições
// para redesenhar a tela
private final Handler mHandler = new Handler();

// X e Y do texto e a cor de background
private int x = 200, y = 100, bg = 0xF00;

// texto do papel de parede
private String texto;

// Retorna a Engine que pinta a tela e
// trata eventos de toque
// Utilizado pelo framework
public Engine onCreateEngine() {
return new MinhaEngine();
}

// Engine para desenhar o papel de parede
private class MinhaEngine extends Engine implements
SharedPreferences.OnSharedPreferenceChangeListener{

// Pincel pra desenhar na tela
private final Paint mPaint;

// Flag que indica se wallpaper está visível
private boolean mVisible;

public MinhaEngine() {
// Configura o pincel para a cor branca
// e coloca anti-alias pra tirar o "serrilhado"
mPaint = new Paint();
mPaint.setColor(0xffffffff);
mPaint.setAntiAlias(true);

// Se registra para ser notificado
// caso o usuário mudar o texto
SharedPreferences prefs =
MeuPapelDeParede.this.getSharedPreferences(
SHARED_PREFS_NAME, 0);

prefs.registerOnSharedPreferenceChangeListener(
this);

onSharedPreferenceChanged(prefs, null);
}

public void onCreate(SurfaceHolder surfaceHolder){
super.onCreate(surfaceHolder);
// Habilita eventos de touch
setTouchEventsEnabled(true);
}

public void onDestroy() {
super.onDestroy();
// Suspende qualquer "pintura" pendente
mHandler.removeCallbacks(threadPintura);
}

public void onVisibilityChanged(boolean visible){
// Se o display estiver apagado, não fica
// pintando a tela evita desperdiçar
// processamento
mVisible = visible;
if (visible) {
pintaPapelDeParede();
} else {
mHandler.removeCallbacks(threadPintura);
}
}

public void onSurfaceDestroyed(SurfaceHolder sh){
super.onSurfaceDestroyed(holder);
// Ao destruir o "canvas" suspende todas
// as threads pendentes
mVisible = false;
mHandler.removeCallbacks(threadPintura);
}

public void onTouchEvent(MotionEvent event) {
// Captura o X e o Y de onde o usuário
// tocou na tela.
// Gera uma cor de BG aleatóra.
if (event.getAction() ==
MotionEvent.ACTION_DOWN) {

x = (int) event.getX();
y = (int) event.getY();
bg = new Random().nextInt(0xffffff);
}
super.onTouchEvent(event);
}

// Thread que solicita a pintura da tela
private final Runnable threadPintura =
new Runnable() {
public void run() {
pintaPapelDeParede();
}
};

// Método que pinta cada frame da
// animação do papel de parede
void pintaPapelDeParede() {
final SurfaceHolder holder =
getSurfaceHolder();

Canvas c = null;
try {
c = holder.lockCanvas();
if (c != null) {
desentaTexto(c);
}
} finally {
if (c != null)
holder.unlockCanvasAndPost(c);
}

// Depois de efetuar a pintura, agenda o handler
// pra pintar o próximo frame.
// Dessa forma, teremos 40 frames por segundo
mHandler.removeCallbacks(threadPintura);
if (mVisible) {
mHandler.postDelayed(threadPintura, 1000/25);
}
}

// Desenha o texto na tela
private void desentaTexto(Canvas c) {
c.save();

c.drawColor(0xff000000 | bg);

float textWidth = 0;
float[] widths = new float[texto.length()];

mPaint.setTextSize(20);
mPaint.getTextWidths(texto, widths);

for (int i = 0; i < widths.length; i++) {
textWidth += widths[i];
}

c.drawText(texto, x, y, mPaint);
x -= 5;
if (x <= -textWidth) {
x = c.getWidth();
}

c.restore();
}

// Se o usuário mudar o texto a ser exibido,
// atualiza o texto do wallpaper
public void onSharedPreferenceChanged(
SharedPreferences prefs, String key) {
texto = prefs.getString(PREF_KEY, "Texto");

}
}
}


Pronto! Agora pra testar, basta ir na tela principal pressionar Menu > Wallpaper > Live wallpapers. Nosso papel de barede deve aparecer na lista. Abaixo temos a figuras do nosso wallpaper em execução.



4br4ç05,
nglauber

sábado, 2 de janeiro de 2010

Mac OS X Leopard no PC

Olá povo,

Desde 2005 quando Apple começou a utilizar a plataforma Intel em seus computadores o projeto OSX86 criou o Hackintosh, que consiste na alteração do sistema operacional da Apple para executar em PCs comuns. Isso se deve ao fato de que o hardware, diferentemente do que era no passado é bem similar ao do PC convencional.

Hoje, o que impede que um PC execute o Mac OS X vendido pela Apple é o fato de o sistema operacional ser homologado para um hardware específico, ou seja, ele não tem os drivers do hardware de vários modelos/fabricantes que são utilizados em um PC comum. Já o Hackintosh disponibiliza os drivers e o kernel que permitem essa execução em um PC comum. Assim como o Linux, o Hackintosh também tem distribuições, as mais conhecidas são a iATKOS, iDeneb, Kalyway e Leo4All.

Nesse post, vou mostrar como eu instalei o hackintosh no meu notebook, e falar dos problemas que tive e tenho. Quero deixar claro aos que quiserem se aventurar que você deve conhecer bem a marca e o modelo de cada componente do seu hardware e que o uma distribuição pode ser melhor para um tipo de hardware do que para outro.

Legalidade

Segundo o site Olhar Digital a utilização do Hackintosh não é ilegal. Desde que você tenha comprado uma versão do Mac OS X Original que custa R$79,00.
No entanto quero reiterar que a instalação que fiz foi apenas para fins educacionais e que não me resposabilizo por nada que der errado no seu PC :)

O computador


Requisitos

O primeiro e mais importante deles: PACIÊNCIA. Eu tive que tentar várias vezes até conseguir uma instalação bem sucedida. Então, vá com calma gafanhoto :)

É aconselhável estar com outro computador por perto para tirar as dúvidas e resolver os problemas que você venha a ter.

Utilizei a versão iATKOS 7. Infelizmente não tenho o link, mas baixei via torrent aproximadamente 3.6GB

Após baixar a imagem, grave-a no em um DVD (eu utilizei o Nero) em uma velocidade baixa (2 ou 4x) para evitar problemas de leitura.

O processo

Coloque o DVD no driver.

Configure na BIOS do seu computador para dar o boot pelo driver de DVD.

Quando o computador iniciar, ele pedirá pra pressionar qualquer tecla para iniciar o DVD ou pressionar F8 para mais detalhes. Eu aconselho pressionar F8 e digitar -v e pressionar ENTER isso fará com que o DVD inicie no modo de debug. Assim, caso algum erro ocorra... pesquise na internet como resolver :)

Se tudo correr bem, a interface gráfica será carregada com uma tela cinza com a logo da Apple. Essa tela demora em torno de 3 minutos para desaparecer, depois dela aparecerá uma tela pedindo para clicar no botão pra continuar.

Quando carregar a tela de boas vindas, você precisará criar a partição onde ficará o sistema operacional. Para isso, vá até o menu Utilities | Disc Utilities, lá selecione o seu HD e crie uma partiação Mac OS Extended. Depois de criar a partição, feche o utilitário.

Clique em Continue, abrirá um tela de licença, clique em Agree. Agora o instalador perguntará onde você deseja instalar o OSX, selecione a partição que criamos no passo anterior e clique em continuar.

A tela Install Summary é o passo mais importante. Clique em Customize, nessa tela selecionaremos o que será instalado. Lembrando que isso pode variar de máquina pra máqunia. Abaixo listo o que EU, selecionei (não desmarquei nenhuma opção padrão):
- Bootloader > Chamaleon v2
- X86 Patches > Kernel > 9.7.0 Kernel Vodoo
- Drivers > System > SATA/IDE > VIA SATA/IDE
- Languages > Português > Português
Clique em Done. Voltará para a tela de Install Summary. Clique em Install.

O processo de instalação inciará e demorará cerca de 45 minutos. Quando a barra de progresso chegar ao fim, será exibida uma tela de sucesso e solicitará que o computador seja reinicializado. Quando o computador reiniciar, retire rapidamente o DVD do driver para evitar o boot pelo DVD novamente.

Se tudo correr bem, aparecerá a tela do Chamaleon e o boot do Mac OS iniciará automaticamente. Isso demorará cerca de 2 minutos.

Quando a interface gráfica inciar, para concluir a instalação serão feitas algumas configurações como: localização regional, teclado, se você deseja enviar informações sobre seu Mac (não faça isso :) , seu tipo de conexão com a internet (configure isso depois), informações pessoais (nome, sobrenome, etc), onde você utiliza o Mac, criará a conta (usuário e senha) para acessar o computador, timezone, e por último data e hora.

Se você chegou até aqui, a instalação foi concluída com sucesso e você é um sortudo :)

Abaixo o screenshot do meu Mac :)





Abaixo mais algumas informações...

Problemas Resolvidos

Windows XP e o OS X no mesmo HD em partições diferentes (óbvio): consegui colocando a partição do OS X como a primeira do disco, de outra forma a instalação dava problema ao reiniciar o computador após a instalação.

Dual Boot com o Windows XP: foi simples, bastou instalar o Windows XP em outra partição, depois de ter instalado o Mac OS. Com isso o bootloader do Windows sobrepos o Chamaleon (gerenciador de boot utilizado no OSX86). Depois foi só seguir esse tutorial.

Drivers ausentes: Wireless e Bateria. Não consegui utilizar o driver disponibilizado no site da Realtek. Ao invés disso, baixei um torrent de um CD-PACK com vários drivers, entre eles o driver da minha placa, que funcionou perfeitamente.

A placa de vídeo: a que o OS X detectou suporta apenas a resolução 1024x768. No entanto, seguindo esses links (link1 e link2), dá pra modificar.

Telado ABNT: o teclado do note é o padrão brasileiro, daí eu baixei o Brasileiro.bundle na internet (não lembro de onde) e funcionou. Entretanto a interrogação, que no meu teclado fica entre o Alt e o Ctrl da direita, não funcionou. Daí eu baixei o Spark e criei um atalho para a interrogação.

Problema NÃO Resolvido

Não tive tempo de procurar o driver da placa de rede Wired. É uma SiS, mas não lembro o modelo ao certo.

Programas legais

Para que a mudança pro mundo Mac não seja tão dolorosa, instalei no OSX alguns programas que utilizo com frequência no Windows:

- Eclipse Galileo
- Real VNC
- Microsoft Office
- MSN Messenger
- Android SDK

E por último...

Espero que tenham gostado e que esse post seja últil pra alguém. E se alguém resolver algum dos problemas que citei acima ou tiver alguma sugestão de melhoria mail-me.

4br4ç05,
nglauber

domingo, 30 de agosto de 2009

Utilizando Banco de Dados SQLite no Android

Olá povo,

Entre as muitas coisas legais que o Android trouxe, o suporte nativo ao banco de dados SQLite foi uma das mais importantes, comercialmente falando. Com ele podemos utilizar os bons e famosos comandos SQL (SELECT, INSERT, UPDATE, DELETE, etc) no mundo mobile. Quem programou em J2ME sabe o quão complicado é implementar um repositório "digno" que suporte uma boa quantidade de registros nessa plataforma (não me falem de RMS... :)

Nesse POST, vou colocar trechos de código que utilizam esses conceitos e tentarei explicá-los. A parte da interface gráfica, vou explicar apenas o que for relevante para o uso com banco de dados.

Para criarmos um banco de dados SQLite, podemos:
  • utilizar a ferramenta SQLite Expert Personal e colocar o arquivo do banco de dados na pasta /data/data/pacote_da_minha_aplicacao/databases;
  • ou via código, utilizando uma classe "helper" do SQLite.
Utilizaremos a segunda abordagem, pois na vida real não queremos colocar esse arquivo manualmente no aparelho. Pois, caso seja necessária uma atualização, teremos que atualizar N aparelhos.

Mãos à obra! Vamos criar uma classe que herda de android.database.sqlite.SQLiteOpenHelper. Ela será responsável por criar o banco quando a aplicação for instalada (método onCreate) e atualizá-lo para novas versões da aplicação (método onUpgrade).

public class SQLiteHelper extends SQLiteOpenHelper {
  private String scriptCreate;
  private String scriptDelete;

  public SQLiteHelper(Context ctx, String nomeBd, 
      int versaoBanco, String scriptCreate, 
      String scriptDelete) {

      super(ctx, nomeBd, null, versaoBanco);
      this.scriptCreate = scriptCreate;
      this.scriptDelete = scriptDelete;
  }

  public void onCreate(SQLiteDatabase db) {
      db.execSQL(scriptCreate);
  }

  public void onUpgrade(SQLiteDatabase db, 
      int oldVersion, int newVersion) {

      db.execSQL(scriptDelete);
      onCreate(db);
  }
}


Agora vamos criar a classe que realizará as operações de abrir, selecionar, inserir, atualizar e excluir registos no banco de dados:

public class RepositorioCarro {
  private SQLiteDatabase db;
  private SQLiteHelper dbHelper;
  private static final String SCRIPT_DB_DELETE = 
    "DROP TABLE IF EXISTS carros";
  private static final String SCRIPT_DB_CREATE =
    "create table carros (_id integer primary "+
    "key autoincrement, nome text not null, "+
    "placa text not null, ano text not null);";

  public RepositorioCarro (Context ctx){
    dbHelper = new SQLiteHelper(ctx, "curso", 1, 
      SCRIPT_DB_CREATE, SCRIPT_DB_DELETE);
  }

  private long inserir(Carro c){
    ContentValues cv = new ContentValues();
    cv.put("nome", c.getNome())
    cv.put("placa", c.getPlaca());
    cv.put("ano", c.getAno());
    db = dbHelper.getWritableDatabase();
    long id = db.insert("carros", null, cv);
    db.close();
    return id;
  }

  private long atualizar(Carro c){
    ContentValues cv = new ContentValues();
    cv.put("nome", c.getNome());
    cv.put("placa", c.getPlaca());
    cv.put("ano", c.getAno());

    db = dbHelper.getWritableDatabase();
    long rows = db.update("carros", cv, "_id = ?", 
       new String[]{ String.valueOf(c.getId())});
    db.close();
    return rows; // qtde. de linhas afetadas 
  } 

  public int excluir(int id){
    db = dbHelper.getWritableDatabase();
    int rows = db.delete("carros", "_id = ?", 
      new String[]{ String.valueOf(id) });
    return rows; // qtde. de linhas afetadas
  }

  public List<Carro> buscarCarroPorNome(String nome){
    List<Carro> lista = new ArrayList<Carro>();

    String[] columns = new String[]{
       "_id", "nome", "placa", "ano"};
    String[] args = new String[]{nome+"%"};

    db = dbHelper.getWritableDatabase();
    Cursor c = db.query("carros", columns, 
       "nome like ?", args, null, null, "nome");

    c.moveToFirst();
    while(!c.isAfterLast()){
      Carro carro = fillCarro(c);
      lista.add(carro);
      c.moveToNext();
    }
    c.close();
    db.close();
    return lista;
  }

  private Carro fillCarro(Cursor c) {
    Carro carro = new Carro();
    carro.setId((int)c.getLong(0));
    carro.setNome(c.getString(1));
    carro.setPlaca(c.getString(2));
    carro.setAno(c.getString(3));
    return carro;
  }
}


Como vocês podem ver, estou usando uma classe Carro também. Não vou listá-la aqui por se tratar de um POJO com os campos: id, nome, placa e ano do veículo.

Na classe acima, o construtor recebe uma referência do contexto da aplicação. Ela é repassada para nossa classe SQLHelper, que por sua vez, se encarregará de criar o nosso banco de dados se ele não existir. A classe android.database.sqlite.SQLiteOpenHelper (superclasse do nosso helper) tem o método getWritableDatabase() que retorna uma instância da classe android.database.sqlite.SQLiteDatabase, que será nosso canal de comunicação com o banco de dados.

As operações de inserir e atualizar utilizam: uma string que representa o nome da tabela no banco de dados; e um objeto da classe ContentValues, que nada mais é do que um conjunto par/valor com os parâmetros do comando SQL. Mas notem que o método update recebe ainda uma String que representa a cláusula WHERE do comando UPDATE (do SQL), bem como os parâmetros da mesma, que estão mapeados via caracter '?'.
"Por tabela", o método excluir vocês já devem ter entendido :)

O último método é o buscarCarroPorNome(String). Toda consulta SQL, retorna um objeto do tipo Cursor. Ele é bem parecido com o ResultSet do JDBC. Os parâmetros do método query são, respectivamente: String nome da tabela, String[] colunasDaTabela, String clausulaWhere, String[] paramsWhere, String groupBy, String having, String orderBy.

Para exibir os dados, precisaremos criar um Adapter, que converterá um Carro em uma linha de uma lista. Crie o arquivo de layout list_item.xml e coloque-o na pasta res/layout:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout android:layout_height="wrap_content" 
xmlns:android="http://schemas.android.com/apk/res/android" 
android:layout_width="fill_parent" 
android:id="@+id/carroRow">

<TextView android:layout_width="wrap_content" 
android:layout_height="wrap_content" 
android:id="@+id/txtNome" 
android:layout_weight="1" 
android:textSize="20dp"/>

<TextView android:layout_width="wrap_content" 
android:layout_height="wrap_content" 
android:id="@+id/txtAno" 
android:layout_marginRight="10px"/>

<TextView android:layout_width="wrap_content" 
android:layout_height="wrap_content" 
android:id="@+id/txtPlaca"  
android:layout_marginRight="10px"/>

</LinearLayout>


E agora, vamos criar nosso adapter.

public class CarroListAdapter extends BaseAdapter {
  private Context context;
  private List<Carro> lista;

  public CarroListAdapter(Context ctx, 
    List<Carro> carros) {

    context = ctx;
    lista = carros;
  }

  public int getCount() {
    return lista.size(); 
  }

  public Object getItem(int arg0) {
    return lista.get(arg0);
  }

  public long getItemId(int arg0) {
    return lista.get(arg0).getId();
  }

  public View getView(int pos, View convertView, 
    ViewGroup parent) {

    Carro c = lista.get(pos);
    LayoutInflater inflater = 
      (LayoutInflater)context.getSystemService(
        Context.LAYOUT_INFLATER_SERVICE);

    View view = inflater.inflate(
      R.layout.list_item, null);

    ((TextView)view.findViewById(R.id.txtNome)).
      setText(c.getNome());

    ((TextView)view.findViewById(R.id.txtAno)).
      setText(c.getAno());

    ((TextView)view.findViewById(R.id.txtPlaca)).
      setText(c.getPlaca());

    return view;
  }
}


Crie uma classe que herde de ListActivity e no onCreate, digite o seguinte código:
RepositorioCarro carrosDB = new RepositorCarro(this);
List<Carro>lista = carrosDB.buscarCarroPorNome("");
setAdapter(new CarroListAdapter(this, lista));

Para utilizar os demais métodos é facinho, não é? Basta criar objetos do tipo Carro, no qual os valores serão preenchidos por widgets (componentes de UI) e passá-los como parâmetros para os métodos do RepositorioCarro.

Espero que tenham entendido e gostado :)

Editado em 03/11/2011
Sempre que fizermos uma operação no banco de dados é importante que encerremos a conexão com ele. Tinha faltado isso nesse post, e agora lembrei de corrirgir.

4br4ç05
nglauber