Virtual Nails – Realidade aumentada no android

29 de Julho de 2011 1 comentário

Mês passado fiz um curso de programação para Android com o Fernando Anselmo. Excelente curso, mas o mais legal foi no fim, fazer uma espécie de projeto final, sem a burocracia de uma monografia. Essencialmente “mão-na-massa”.

Meu projeto é (ainda está em desenvolvimento) um aplicativo que utiliza realidade aumentada para que as mulheres possam ver como fica a cor de um esmalte em sua mão antes de aplicá-lo.

O escopo desse artigo é tão somente a implementação desse projeto na plataforma android. Os conceitos e áreas de aplicação da realidade aumentada estão muito bem explicados aqui e aqui.

A proposta do aplicativo é você poder, através da câmera do seu celular, ver em tempo real usando o preview da câmera ou mesmo tirar uma foto da sua mão (sugestão dada na apresentação do projeto no curso), posicionar melhor a imagem de cada unha em cima de cada dedo e escolher a cor do esmalte.

O principal problema foi conseguir a rotação correta da câmera no meu celular (um motorola Atrix). A imagem da câmera aparecia girada em 90 graus (o telefone estava apontado para cima e a imagem da mão aparecia deitada), o que é, aparentemente, um problema que acontece em vários modelos de celular, como podemos ver aqui.
Eu percebi que o aplicativo da máquina fotográfica que vem no celular não apresentava esse problema então baixei o código fonte dele para ver como ele trabalha a rotação da imagem. Todo o código fonte, não só do android, mas também dos pacotes de aplicativos de cada uma das versões pode ser baixado de um repositório GIT aqui. No windows eu uso o mysysgit para fazer o clone do repositório.
Descobri então, que na versão 2.2 ou API level 8 foi introduzido o método setDisplayOrientation() na classe Camera que recebe como parâmetro o número de graus a rodar a imagem. Esse método simplificou o processo, mas ainda estou trabalhando na correção da rotação da imagem para as versões anteriores do android.

 

Vamos ao código.
Não vou colocar o código completo aqui. Apenas algumas partes que merecem algum tipo de observação.
Criei um repositório no GitHub para esse projeto: acesse aqui para baixar o projeto completo.

O projeto é composto pelo arquivo XML de layout, o arquivo XML de strings, algumas imagens e mais três classes, sendo elas MainActivity.java que controla a aplicação, CameraOverlay.java, que cria a surface view e controla a câmera e TouchScreenView.java que cria as imagens e permite arrastar e posicionar cada uma delas.

O arquivo de layout main.xml:

<FrameLayout android:id="@+id/cameraLayout"
	android:layout_weight="1"
	android:layout_width="fill_parent"
	android:layout_height="fill_parent">
</FrameLayout>

No arquivo de layout, a única coisa diferente é o uso do FrameLayout para “empilhar” os objetos na tela. A forma como o FrameLayout gera a tela permite que as imagens das unhas fiquem por cima do surfaceView da Câmera.

A classe MainActivity.java:

private void montaLayout() {
// TODO Auto-generated method stub
mCameraOverlay = new CameraOverlay(this);
fl = (FrameLayout) findViewById(R.id.cameraLayout);
fl.addView(mCameraOverlay);
Button btnFoto = (Button) findViewById(R.id.btnFoto);
btnFoto.setOnClickListener(new OnClickListener() {
	public void onClick(View v) {
		mCameraOverlay.mCamera.takePicture(shutterCallback, rawCallback, null);
	}
});

Button btnPreview = (Button) findViewById(R.id.btnPreview);
btnPreview.setOnClickListener(new OnClickListener() {
	public void onClick(View v) {
		mCameraOverlay.restartPreview();
	}
});

mTs = new TouchScreenView(this);
fl.addView(mTs);
}

ShutterCallback shutterCallback = new ShutterCallback() {
	public void onShutter() {
		Log.d(TAG, "onShutter callback");
	}
};

/** Callback chamado quando tira a foto */
PictureCallback rawCallback = new PictureCallback() {
	public void onPictureTaken(byte[] data, Camera camera) {
		Log.d(TAG, "onPictureTaken - raw");
	}
};

A activity simplesmente faz o layout do main.xml, e chama o método montaLayout(), que inicialmente instancia a classe CameraOverlay (que estende a SurfaceView) e adiciona seu conteúdo ao FrameLayout. Em seguida define os listeners dos botões de foto (que chama o método takePicture da classe Camera passando os callbacks como parâmetro) e limpar a tela para então instanciar a TouchScreenView (que deriva de View) e adicionar ao FrameLayout, por cima da SurfaceView da CameraOverlay. Por último, cria  os callbacks para os botões de tirar foto (onde poderíamos salvar a foto, se fosse o caso) e limpar tela.

A classe CameraOverlay:

class CameraOverlay extends SurfaceView implements SurfaceHolder.Callback {
	private static final String TAG = "VirtualNails";

	SurfaceHolder mHolder;
	public Camera mCamera;
	private Parameters mParameters;
CameraOverlay(Context context) {
	super(context);
	// Cria um Surfaceholder.Callback para
	// notificacao da criação, modificação e destruição da surface
	mHolder = getHolder();
	mHolder.addCallback(this);
	mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}

public void surfaceCreated(SurfaceHolder holder) {
	// Surface criada. pega a camera e chama o preview
	mCamera = Camera.open();
	setPreviewDisplay(holder);
}
public void surfaceDestroyed(SurfaceHolder holder) {
	// Surface vai ser encerrada, entao paramos o preview da camera
	// TODO: CORRIGIR: o surface destroyed eh chamado antes do surfaceChanged quando muda a rotação da tela,
      // causando um blink na tela
	Log.v(TAG, "surfaceDestroyed");
	stopPreview();
	closeCamera();
	holder = null;
}

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
	// usa o setDisplayOrientation para resolver o bug dos telefones motorola
	// que a imagem aparece virada em 90 graus -- so disponivel a partir da versao 2.2

	//TODO: Resolver esse problema para versões anteriores do android

	Log.v(TAG, "surfaceChanged");
	mParameters = mCamera.getParameters();

	Configuration config = getResources().getConfiguration();
    if (config.orientation == Configuration.ORIENTATION_PORTRAIT)
    {
    	  mCamera.setDisplayOrientation(90); //disponivel apenas na versao 2.2 ou >
    	  mParameters.setPreviewSize(h, w); //TODO: testar em um telefone que não seja motorola para ver se distorce
    }
    else
    	mParameters.setPreviewSize(w, h);

	mCamera.setParameters(mParameters);

	try {
		startPreview();
	}
	catch (Exception e) {
		// TODO: handle exception
	}
}

A classe CameraOverlay extemde a SurfaceView e implementa a SurfaceHolder.Callback.
A SurfaceView fica por trás da janela onde é instalada, e “abre um buraco” na view, permitindo mostrar a superfície. A interface SurfaceHolder permite o acesso à superfície debaixo dela, e precisa necessariamente ser um callback, para garantir que a tela está pronta antes de, por exemplo, inicializarmos a câmera.
No surfaceCreated() é onde criamos o objeto Camera, à partir do método open() da classe Camera e no surfaceChanged() fazemos as configurações dos parâmetros iniciais da câmera e adicionamos os parâmetros que iremos utilizar. Eu utilizei o setDisplayOrientation() para corrigir um problema do meu telefone como expliquei acima (ainda preciso testar em outros telefones). Por último e iniciamos o preview da imagem.
É importante liberar a câmera ao encerrar o aplicativo pois não é um recurso compartilhado.

Por último temos a classe TouchScreenView.java:

@Override
public boolean onTouchEvent(MotionEvent event) {
	// TODO Auto-generated method stub

	float x = event.getX();
	float y = event.getY();

	Log.i(TAG, "onTouchEvent X/Y: " + x + "/" + y);

	switch (event.getAction()){
		case MotionEvent.ACTION_DOWN:
			selecionadoPolegar = imgPolegar.copyBounds().contains((int) x, (int) y);
			selecionadoIndicador = imgIndicador.copyBounds().contains((int) x, (int) y);
			selecionadoMedio = imgMedio.copyBounds().contains((int) x, (int) y);
			selecionadoAnular = imgAnular.copyBounds().contains((int) x, (int) y);
			selecionadoMindinho = imgMindinho.copyBounds().contains((int) x, (int) y);
			break;
		case MotionEvent.ACTION_MOVE:
			if (selecionadoPolegar){
				this.xPolegar = (int)x - (larguraImgPolegar/2);
				this.yPolegar = (int)y - (alturaImgPolegar/2);
			}
			else if (selecionadoIndicador){
				this.xIndicador = (int)x - (larguraImgIndicador/2);
				this.yIndicador = (int)y - (alturaImgIndicador/2);
			}
			else if (selecionadoMedio){
				this.xMedio = (int)x - (larguraImgMedio/2);
			this.yMedio = (int)y - (alturaImgMedio/2);
			}
			else if (selecionadoAnular){
				this.xAnular = (int)x - (larguraImgAnular/2);
				this.yAnular = (int)y - (alturaImgAnular/2);
			}
			else if (selecionadoMindinho){
				this.xMindinho = (int)x - (larguraImgMindinho/2);
				this.yMindinho = (int)y - (alturaImgMindinho/2);
			}
			break;
		case MotionEvent.ACTION_UP:
			selecionadoPolegar =
			selecionadoIndicador =
			selecionadoMedio =
			selecionadoAnular =
			selecionadoMindinho = false;
			break;
	}
	invalidate();

	return true;

}

Nela carrego as imagens fixas. Ainda estou trabalhando nessa mudança, para poder criar o efeito do brilho do esmalte desenhando diretamente no canvas, e passar somente o valor hexadecimal da cor quando esta for modificada.

A classe faz override no método onDraw(), o que já permite desenhar cada imagem no canvas com seu respectivo tamanho e no onTouchEvent identificamos quem foi selecionado à partir do copyBounds().contains() passando a área do retângulo onde o evento está ocorrendo e em seguida setamos as coordenadas novas da imagem e chamamos o método invalidate() que faz a tela ser redesenhada, com as coordenadas novas.

Anúncios
Categorias:Android, Tecnologia

Consulta recursiva no MS-SQL – Auto-relacionamento

24 de Junho de 2011 8 comentários

Conversando com um amigo essa semana, que estava às voltas para implementar um sistema que iria precisar, dentre outras coisas, representar a hierarquia dos cargos na tabela de funcionários  e me lembrei de algo que tinha feito há alguns anos.

Existe desde a versão 2005 do MS-SQL, um recurso chamado CTE (Common Table Expressions) que é uma técnica bem legal para resolver o velho problema do auto-relacionamento utilizado nos casos em que se precisa representar uma hierarquia no banco de dados.
Quem já implementou uma solução para esse tipo de problema, sabe que precisávamos de tabelas temporárias, cursores e mais um bocado de lógica para controlar o processo.

Um cenário onde essa situação pode ser encontrada é a tabela de funcionários, onde temos o Presidente da empresa no nível 1, os diretores no nível 2 tendo o presidente como chefe, os gerentes no nível 3 tendo algum diretor como chefe e por aí vai…

Outra aplicação é para os casos de marketing de redes, onde fulano cadastra ciclano que cadastra beltrano e você precisa, além de reconstruir essa cadeia de forma eficiente, poder identificar quem está vinculado a quem nos vários níveis.
Ex: O camarada do primeiro nível ganha 10% de comissão para cada item que ele vender, mais 5% para cada item que as pessoas que ele cadastrou venderem, mais 1% que as pessoas cadastradas pelas que ele cadastrou venderem.

Vamos à implementação:
A tabela que vai armazenar essa estrutura pode ficar assim:


O script com a criação da tabela e a inclusão de alguns nomes (eu deixei apenas o primeiro nome, então não estranhem algumas repetições) que tirei do banco de um local onde trabalhava e implementei essa solução pode ser baixado aqui.

declare @pkInicio int
set @pkInicio = 1 --eu inicializo com 1 que eh o primeiro na hierarquia. Entao vai mostrar todo mundo.
--criação do CTE.
WITH funcionarioTb_CTE (funcionarioPk, funcionarioFk, nome, iteracao) AS
(
SELECT funcionarioPk, null, nome, 0 --o primeiro item FK tem que ser NULL ... nao tem ninguem acima do filtro especificado
FROM funcionarioTb WHERE funcionarioPk = @pkInicio --filtro que determina o primeiro item da hierarquia
UNION ALL -- o union all vai juntar o primeiro nivel com os demais chamados pela query recursiva
SELECT b.funcionarioPk, b.funcionarioFk, b.nome, a.iteracao + 1 -- query que chama o CTE passando o numero da iteracao, faz um join da query atual do CTE com a tabela
FROM funcionarioTb_CTE AS a, funcionarioTb AS b -- original chamando todos os que estao abaixo daquele funcionario naquele nivel
WHERE a.funcionarioPk = b.funcionarioFk
)
--inicia de fato a consulta com uma chamada para a query recursiva
SELECT funcionarioPk, funcionarioFk, nome
FROM funcionarioTb_CTE  order by funcionarioPk
option (maxrecursion 10) -- o maxrecursion é um recurso de segurança. Determina o numero máximo de níveis que o CTE atinge. Retorna uma mensagem de erro se atingido.
--"The statement terminated. The maximum recursion 2 has been exhausted before statement completion."

É isso. Se você mudar o @pkInicio para outro número na hierarquia, vai ver que só são listados os registros abaixo do número informado.
Para simplificar, observe a árvore abaixo (Se você usou o  script para criar o banco, alguns nomes a mais aparecerão, mas você vai conseguir enxergar esses):
Com o @pkInicio = 1 (Leonardo), teremos como alguns nomes retornados o seguinte: nivel 1: Leonardo – nivel 2:  Ana, Moaci, Lira – nivel 3:  Adriana, Carlos, Lazara, Alex – nivel 4: Marcos, Sonia
Com o @pkInicio = 34 (Lira), teremos como alguns nomes retornados o seguinte: nivel 1: Lira – nivel 2:  Lazara, Alex – nivel 3: Marcos, Sonia
Com o @pkInicio = 40 (Lazara), teremos como alguns nomes retornados o seguinte: nivel 1: Lazara – nivel 2: Marcos, Sonia

Categorias:Banco de dados

Instalação básica do Elastix com o Vono

12 de Junho de 2011 26 comentários

Esta semana eu tive que implementar um PABX VoIP para a empresa onde trabalho. (É uma coisa bem bacana para se utilizar em casa também)
Existem algumas distribuições do asterisk com TrixBox, AsteriskNow e Elastix que foi a que eu escolhi.
Após pesquisar um pouco, descobri que o Elastix é provavelmente a distribuição do Asterisk mais utilizada hoje em dia. Tem uma interface web bem bacana e aparentemente é mais estável.

Primeiro fui até a Elastic Hosts e montei um meu novo e superpotente servidor de 500 mhz processador, 256 mb RAM and 10 GB HD na nuvem(isso é realmente muito bacana. Provavelmente não vou precisar mais do que isso, mas se a empresa crescer, eu precisar de mais espaço ou a máquina ficar lenta, eu posso simplesmente mover uma chavinha e aumentar a potência da máquina).

Após criar a máquina e o HD de 10 gb, Eu subi uma ISO (imagem de CD) do Elastix e me conectei via VNC para iniciar a instalação à partir do boot (funciona perfeitamente!!!).

É claro que você pode utilizar uma máquina sua para a instalação. À partir daqui, os passos serão os mesmos.

A instalação do Elastix é muito simples, mas se você precisar de ajuda, aqui tem um vídeo explicativo.

Depois de instalar, os primeiros passos são mudar a senha de administrador e colocar IP fixo. Se você usar a Elastic Hosts, vai precisar adicionar um IP nas subscriptions. Para mudar a senha de administrador, vá na aba System -> User Management e no menu esquerdo clique em usuários. O endereço IP deve ser udado no menu Network, também abaixo da aba System.

Começando:

Primeiro um pouco de planejamento. A ideia é fazer as coisas mais simples antes, então, primeiro vamos configurar as extensions (que são os ramais). No meu caso, eu criei Recepcionista, tecnica, sala de reunioes, administrativo e gerencia. Depois criei um tronco (trunk) para permitir a comunicação com o mundo externo e em seguida as rotas de entrada (inbound) e saída (outbound) para podermos receber e fazer chamadas. Para tal, eu criei as extensões 1001, 1002, 1003, 1004 e 1005 respectivamente. Isto é feito na aba PBX, no menu esquerdo Extensions. De início, vamos preencher o número da extensão(extension number), o nome que irá aparecer quando ligarmos para alguém (display name), o CID (Caller ID), que é utilizado para as chamadas internas e o SIP Alias para referência de chamadas SIP diretas. Eu utilizei o mesmo número da extensão para esses. Não esqueça de utilizar uma senha forte para o secret pois você não quer ninguém fazendo ligações na sua conta telefônica. Agora, já devemos estar conseguindo nos comunicar internamente, entre as extensões. Eu testei isso configurando um cliente sip em meu celular com uma extensão e outro cliente sip em meu computador com outra extensão. Não se esqueça de clicar em APPLY the changes na barra vermelha na parte superior da tela para que suas mudanças sejam aplicadas. Você deve clicar em APPLY changes toda vez que quiser testar uma modificação.

Agora vamos criar o tronco. O tronco pode ser ZAP, o que permite fazermos chamadas via linhas telefônicas comuns ou SIP, que permite a conexão com um provedor VoIP. Como estamos utilizando o Vono, vamos criar um tronco SIP.

Segue  que eu preenchi para criar o tronco SIP:
Trunk description: Vono
Outbound caller ID: “Empresa”  <XXXXXXXXXX> (tem que ter as  “” e os caracteres <> . O X deve ser trocado para o seu número de telefone)

Dial Rules: X.
(como nós só teremos o tronco do Vono, vamos mandar tudo por esse tronco. O X significa qualquer dígito entre 0 e 9e o . funciona como um curinga. Você também pode usar N – para dígitos entre 2 e 9, Z – para qualquer dígito diferente de 0 e o | que não envia para o tronco qualquer caractere que estiver antes do | e [] para varias combinações como [2-6] – qualquer dígito entre 2 e 6)

PEER Details

type=peer
username=YOUR_USERNAME
secret=YOUR_PASSWORD
port=5060
insecure=very
host=vono.net.br
fromuser=YOUR_USERNAME
fromdomain=vono.net.br
dtmfmode=rfc2833
disallow=all
context=from-vono
allow=alaw&ulaw&gsm&ilbc
canredirect=no
canreinvite=no

note que o parâmetro context está marcado como from-vono. Tem um pequeno truque para podermos pegar o caller ID, que vamos ver mais pra frente.

Register string:
username:password@vono.net.br/username

Após aplicar essas alterações, você já deve estar registrado. No meu caso, a aba System, no menu dashboard, a atividade Communication mostra que eu tenho 1 tronco sendo: 0 registered, 0 not registered e 1 unknown. Para verificar se o seu foi registrado, conecte no seu servidor via SSH e digite

asterisk -r

para acessar o CLI do asterisk e depois digite

sip show registry

O que deve mostrar um estado registered para o host vono.net.br:5060.

Agora para podermos receber ligações, vamos criar o contexto from-vono e as rotas de entrada (inbound routes).
Para criar o contexto from-vono vá no menu Tools abaixo da aba  PBX e clique em Asterisk File Editor no menu esquerdo. Filtre por extensions_custom.conf e insira o seguinte texto:

[from-vono]
exten => _.,1,Set(NUMENTRADA=${SIP_HEADER(X-Vono-DDR)})
exten => _.,n,GotoIf($["${NUMENTRADA}" = ""]?nop)
exten => _.,n,Goto(from-trunk,${NUMENTRADA},1)
exten => _.,n(nop),Goto(from-trunk,${EXTEN},1)

Basicamente aqui a gente pega o Caller ID do cabeçalho SIP e se ele não for em branco, passamos oara o from-trunk padrão, ou se não passamos a extensão. Veja mais aqui.

Para criar rotas de entrada, (inbound routes) clique em Inbound Routes no menu esquerdo, na aba PBX. Aqui a gente pode usar o número de entrada (DID) como um filtro e determinar para qual extensão redirecionamos a ligação, dependendo do número de entrada discado. Neste caso, aceitaremos todas as chamadas de entrada sem filtros pois só teremos um número sendo chamado. Deixei os outros parâmetros preenchidos como padrão e coloquei o deistino da ligação (destination) para o ramal da recepcionista. Aqui também poderíamos colocar os grupos de chamada (ring group), que irão tocar todos os telefones que estiverem em um mesmo grupo de chamada.

Agora temos que criar as rotas de saída (outbound routes) para poder fazer chamadas para fora. Para isso, clique em Outbound routes no menu esquerdo. Aqui podemos definir regras para fazer a chamada por um tronco A se o número discado for um celular, pelo tronco B se for uma ligação internacional, sempre escolhendo a melhor rota de forma a fazer as chamadas de forma mais barata. A forma como identificamos que tipo de número está sendo discado é através de um padrão de discagem (dial pattern). Novamente, como só temos um tronco, utilizei um padrão genérico X..

É isso. Agora aplique as suas mudanças e você já deve ter um PABX VoIP funcional. Pode colocar o seu celular como um ramal e fazer ligações, entre os seus ramais, sem pagar nada.

Categorias:Asterisk, Tecnologia

O problema da latinha de cerveja

2 de Junho de 2011 Deixe um comentário

Beer can tower

Então, uma situação da vida real.
Há algumas semanas, nós já bebíamos um pouco além da conta quando uma mente brilhante inventou de empilhar as latinhas de cerveja.
A imagem mostra como é difícil (especialmente depois de umas e outras) montar uma pirâmide perfeita quando se tem muitas cervejas.

A idéia é que você entre com o número total de latas de cerveja e o algoritmo vai retornar quantas latas você precisa colocar na base para ter uma pirâmide perfeita ou o mais próximo possível.

Segue a minha solução proposta para o problema (implementado em C++).

Se você tiver uma outra solução, posta aí

#include <cstdlib>
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
    int entrada;

    int latas=0;
    int latasBase=1;

    cout<<"Digite o numero total de latinhas: ";
    cin>>entrada;

    while ((latas + latasBase) < entrada)
    {
          latas = latasBase + latas;
          latasBase++;
    }

    printf("Numero de latas na base = %d\n", latasBase);

    return EXIT_SUCCESS;
}
Categorias:Algoritmos, Tecnologia

Passos pequenos

26 de Maio de 2011 Deixe um comentário

Semana passada estive no evento MOTODEV App Summit em São Paulo.

Um evento bem bacana da Motorola para apresentar o telefone Atrix e o tablet XOOM.
Foi um evento bem técnico, com um bocado de linhas de código e apresentação dos novos recursos do Anrdoid 3.0 (Fragments é muito bacana).

Eles também apresentaram o MOTODEV Studio, que é um IDE baseado no Eclipse. Faz basicamente tudo o que você pode fazer com o Eclipse para desenvolvimento para Android e tem mais uns features bem legais. Os que chamaram a minha atenção:
– O Editor de arquivos de localização (Localization Files Editor): que apresenta os recursos do seu projeto em um grid e possibilita você traduzir a sua aplicação automaticamente para qualquer língua, usando o Google Translator;
– A validador de aplicações (App Validator): No meu primeiro teste, ele já me avisou que eu tinha algumas imagens faltando na minha pasta  /res/drawable-ldpi, e ainda sugeriu uma forma de corrigir  (“Fix suggestion: Add the appropriate “remove_item.png” drawable to the “drawable-ldpi” folder.”). Estou bem curioso para ver o que ele apresenta como sugestão de correção em aplicações mais complexas..
– Gerenciamento de banco de dados (Database management): Um aplicativo para gerenciamento da sua base de dados SqlLite integrado no IDE do Eclipse.
Outros recursos também estão disponíveis e você pode encontrar aqui.

Então, depois do evento resolvi testar o software.
Aqui vão os passos que eu fiz para montar o ambiente de desenvolvimento:

Estou usando o Ubuntu 10.10 (maverick), mas o setup pro Windows é ainda mais fácil.
O primeiro passo é baixar o MOTODEV Studio aqui.
Como estou usando uma VM nova, eu baixei o pacote FULL, mas se você já tem o Eclipse configurado, pode instalar como um Plug-in.
Antes de instalar o Motodev Studio, eu precisei instalar o Java JDK, que pode ser baixado aqui ou voce pode usar o apt-get do Ubuntu:
adiciona o repositório partner (se você não quiser adicionar este repositório, você pode usar o openjdk-6 no lugar)

$ sudo add-apt-repository "deb http://archive.canonical.com/ lucid partner"

–atualiza a lista dos sources

$ sudo apt-get update

–instala o java

$ sudo apt-get install sun-java6-jre sun-java6-plugin

–se o seu sistema tiver mais de uma versão do Java, com este comando você escolhe a java 6 para ser utilizada

$ sudo update-java-alternatives -s java-6-sun

–é bom exportar o diretório do java para a variável PATH para poder acessar de qualquer lugar. Para isso edite o seu arquivo .bashrc (na raiz da sua pasta $HOME) e insira o seguinte:

export JAVA_HOME=/usr/lib/jvm/java-6-sun
export PATH=$PATH:$JAVA_HOME/bin

Depois você precisa baixar o Android SDK aqui.
Descompacte para o seu diretório preferido. (Eu usei /usr/lib/<android-sdk-version>
E atualize a variavel PATH no .bashrc

export SDK_ROOT=/usr/lib/android-sdk-linux_x86
export PATH=$PATH:$SDK_ROOT/tools:$SDK_ROOT/platform-tools

Pronto. Pode abrir o MOTODEV Studio agore e ele irá rodar um assistente para a instalação dos componentes (plataformas, exemplos, etc.)

Categorias:Android, Tecnologia