terça-feira, 26 de agosto de 2014

Android NDK, OpenGL ES, and Framework

Então, estava procurando tutoriais sobre openGL ES com android NDK (C++), mas existem poucos, e os mesmos não funcionam muito bem.
Por isso resolvi postar aqui tudo que eu passei para conseguir algo funcional (não digo 100% pois não consegui testar direito, mas está compilando).

Primeiramente eu uso o AndroidStudio, pois acho melhor do que o Eclipse, para configurar o NDK no studio, basta seguir esse tutorial: (NDK + Android Studio).

Após configurar tudo direitinho, você terá um diretório "jni" com o source em C++, eu não consegui fazer o gradle compilar automaticamente, então eu uso o ndk-build na pasta jni, e ele compila e joga para a pasta de libs correta.

Bom, além de tudo isso, como estou tendo que aprender tudo na raça, irei criar um framework para o openGL ES com android NDK, irei criar um repo no github para isso e depois postarei aqui novamente.
Se alguém estiver interessado em ajudar, seja programando, ou dando opniões, sempre será bem vindo.

Qualquer dúvida na configuração pode postar aqui. Até a próxima!

quarta-feira, 20 de agosto de 2014

Android Tutoriais - ContentProvider

Bom dia pessoal,
Comecei um projeto pessoal, e para facilitar a comunicação com o banco de dados sqlite, eu pesquisei um pouco sobre ContentProvider, e achei válido postar aqui um tutorial, sendo que com um ContentProvider, toda a parte de comunicação com o banco de dados fica bem mais simples.

ContentProvider serve para comunicar com o sqlite no android e retornar cursor com os registros sem a necessidade de montar um sql para todo comando, ele também é responsável por toda operação CRUD (create, read, update, delete) do aplicativo.

Bom para começar vamos definir uma tabela, criando uma classe para isso:

public class TutorialTable {
    //Tabela do BD
    public static final String TABLE_TUTORIAL = "tutorial";
    public static final String COLUMN_ID = "_id";
    public static final String COLUMN_DESCRICAO = "descricao";

    // SQL para criar a tabela
    private static final String DATABASE_CREATE = "create table "
            + TABLE_TUTORIAL
            + "("
            + COLUMN_ID + " integer primary key autoincrement, "
            + COLUMN_DESCRICAO + " text not null "
            + ");";

    public static void onCreate(SQLiteDatabase database) {
        database.execSQL(DATABASE_CREATE);
    }

    public static void onUpgrade(SQLiteDatabase database, int oldVersion,
                                 int newVersion) {
        database.execSQL("DROP TABLE IF EXISTS " + TABLE_TUTORIAL);
        onCreate(database);
    }
}


A classe da tabela é bem simples, primeiro definimos uma variável om o nome da tabela e das colunas (para acessar mais tarde), depois definimos o SQL para criar a tabela, e o upgrade (para quando mudarmos alguma coluna, aqui para facilitar ele da DROP e depois CREATE novamente, mas se não quiser ter perda de dados, poderíamos apenas usar ALTER TABLE para adicionar e remover colunas, porém teríamos que controlar as versões, como aqui é apenas para testes vou utilizar a maneira mais fácil.


Depois criaremos uma classe que será o DatabaseHelper, que fará a criação do BD e a atualização do mesmo.

public class DatabaseHelper extends SQLiteOpenHelper {

    private static final String DATABASE_NAME = "tutorial.db";
    private static final int DATABASE_VERSION = 1;

    public IGDatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    // Método chamado ao criar o BD
    @Override
    public void onCreate(SQLiteDatabase database) {
        TutorialTable.onCreate(database);
    }

    // Método chamado na atualização do BD, se você mudar o DATABASE_VERSION ele irá chamar esse método
    @Override
    public void onUpgrade(SQLiteDatabase database, int oldVersion,
                          int newVersion) {
        TutorialTable.onUpgrade(database, oldVersion, newVersion);
    }
}

Pronto, temos nossa tabela e nosso BD funcionando, lembrando que para cada tabela é bom criar uma classe diferente, e lembrar de adicionar o onCreate e onUpgrade no databasehelper.
Agora só falta a classe que será responsável pela comunicação com o BD, que será nosso ContentProvider.

public class CustomContentProvider extends ContentProvider {
    private DatabaseHelper database;

    private static final String AUTHORITY = "com.caioketo.tutorial.contentprovider";

    // ------- Definimos URIs uma para cada tabela
    private static final String PATH_TUTORIAL = "tutorial";

    public static final Uri CONTENT_URI_TUTORIAL = Uri.parse("content://" + AUTHORITY
            + "/" + PATH_TUTORIAL);


    // ------- Configurando o UriMatcher
    private static final int TUTORIAL = 10;
    private static final int TUTORIAL_ID = 20;
    private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    static {
        sURIMatcher.addURI(AUTHORITY, PATH_PERGUNTAS, TUTORIAL);
        sURIMatcher.addURI(AUTHORITY, PATH_PERGUNTAS + "/#", TUTORIAL_ID);
    }

    @Override
    public boolean onCreate() {
        return false;
    }

    //@Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {

        // Vamos utilizar o queryBuilder em vez do método query()
        SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();

        int uriType = sURIMatcher.match(uri);
        switch (uriType) {

            case TUTORIAL_ID:
                // Adicionamos a coluna ID
                queryBuilder.appendWhere(TutorialTable.COLUMN_ID + "="
                        + uri.getLastPathSegment());
                //$FALL-THROUGH$
            case TUTORIAL:
                queryBuilder.setTables(TutorialTable.TABLE_PERGUNTA);
                break;

            default:
                throw new IllegalArgumentException("Unknown URI: " + uri);
        }
        SQLiteDatabase db = database.getWritableDatabase();
        Cursor cursor = queryBuilder.query(db, projection, selection,
                selectionArgs, null, null, sortOrder);
        // Notificamos caso exista algum Listener
        cursor.setNotificationUri(getContext().getContentResolver(), uri);

        return cursor;
    }

    public String getPATH(int pathID) {
        switch (pathID) {
            case TUTORIAL:
                return PATH_PERGUNTAS;
            default:
                return "";
        }
    }

    @Override
    public String getType(Uri uri) {
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        int uriType = sURIMatcher.match(uri);
        SQLiteDatabase sqlDB = database.getWritableDatabase();
        int rowsDeleted = 0;
        long id = 0;
        switch (uriType) {
            case TUTORIAL:
                id = sqlDB.insert(TutorialTable.TABLE_TUTORIAL, null, values);
                break;
            default:
                throw new IllegalArgumentException("Unknown URI: " + uri);
        }
        getContext().getContentResolver().notifyChange(uri, null);
        return Uri.parse(getPATH(uriType) + "/" + id);
    }


    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        int uriType = sURIMatcher.match(uri);
        SQLiteDatabase sqlDB = database.getWritableDatabase();
        int rowsDeleted = 0;
        switch (uriType) {
            case TUTORIAL:
                rowsDeleted = sqlDB.delete(TutorialTable.TABLE_TUTORIAL, selection,
                        selectionArgs);
                break;
            case TUTORIAL_ID:
                String id = uri.getLastPathSegment();
                if (TextUtils.isEmpty(selection)) {
                    rowsDeleted = sqlDB.delete(TutorialTable.TABLE_TUTORIAL,
                            TutorialTable.COLUMN_ID + "=" + id,
                            null);
                } else {
                    rowsDeleted = sqlDB.delete(TutorialTable.TABLE_TUTORIAL,
                            TutorialTable.COLUMN_ID + "=" + id
                                    + " and " + selection,
                            selectionArgs);
                }
                break;
            default:
                throw new IllegalArgumentException("Unknown URI: " + uri);
        }
        getContext().getContentResolver().notifyChange(uri, null);
        return rowsDeleted;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {

        int uriType = sURIMatcher.match(uri);
        SQLiteDatabase sqlDB = database.getWritableDatabase();
        int rowsUpdated = 0;
        switch (uriType) {
            case TUTORIAL:
                rowsUpdated = sqlDB.update(TutorialTable.TABLE_TUTORIAL,
                        values,
                        selection,
                        selectionArgs);
                break;
            case TUTORIAL_ID:
                String id = uri.getLastPathSegment();
                if (TextUtils.isEmpty(selection)) {
                    rowsUpdated = sqlDB.update(TutorialTable.TABLE_TUTORIAL,
                            values,
                            TutorialTable.COLUMN_ID + "=" + id,
                            null);
                } else {
                    rowsUpdated = sqlDB.update(TutorialTable.TABLE_TUTORIAL,
                            values,
                            TutorialTable.COLUMN_ID + "=" + id
                                    + " and "
                                    + selection,
                            selectionArgs);
                }
                break;
            default:
                throw new IllegalArgumentException("Unknown URI: " + uri);
        }
        getContext().getContentResolver().notifyChange(uri, null);
        return rowsUpdated;
    }

    private void checkColumns(String[] projection) {
        String[] available = { TutorialTable.COLUMN_DESCRICAO, PerguntasTable.COLUMN_ID };
        if (projection != null) {
            HashSet<String> requestedColumns = new HashSet<String>(Arrays.asList(projection));
            HashSet<String> availableColumns = new HashSet<String>(Arrays.asList(available));
            // check if all columns which are requested are available
            if (!availableColumns.containsAll(requestedColumns)) {
                throw new IllegalArgumentException("Unknown columns in projection");
            }
        }
    }
}


Primeiro definimos a AUTHORITY que será o path do app, para encontrar o BD, depois definimos Uris para cada tabela, e então configuramos o UriMatcher para pegar pelo ID em vez da Uri, para melhorar a velocidade. E então criamos nosso ContentProvider, os métodos são básicos como podem ver:

query(): Seria como um select, retorna um Cursor com os registros encontrados;
getPATH(): Para retornar o PATH pelo ID;
insert(): Como o nome mesmo já diz, insere registros na tabela;
delete(): Deleta registros;
update(): Atualiza os registros;
checkColumns(): esse a gente cria para verificar se a coluna existe antes de executar qualquer comando SQL, caso mude as colunas ele não vai dar erro de SQL, pois trataremos antes.

IMPORTANTE:
Após a criação do ContentProvider temos que declará-lo no AndroidManifest.XML

<provider
            android:authorities="com.caioketo.tutorial.CustomContentProvider"
            android:name=".dao.CustomContentProvider" >
</provider>

E agora sim estamos prontos para trabalhar com o BD, sempre iremos usar o ContentProvider para todas as operações, e podemos até criar um método no ContentProvider para executar um comando SQL (se nescessário) para apenas essa classe se comunicar diretamente com o BD, assim encapsulamos a comunicação.

E aqui finalizo mais um post, qualquer dúvida, sugestão, erro, etc, favor deixar comentários, e até a próxima.


terça-feira, 19 de agosto de 2014

Projetos em Pi - O Cliente [Tutorial Socket.IO + Android]

Bom, como ainda não posso começar a programar o servidor, por problemas técnicos, começarei a modelar o cliente, irei faze-lo para android, utilizando Socket.IO para comunicação.
Segue um tutorial de como utilizar o Socket.IO no android.

Tutorial Socket.IO Android

Para começar recomendo baixar e configurar o Ant para compilar o socket.io-java-client.
1- Baixe o ant-current-bin.zip (http://archive.apache.org/dist/ant/).
2- Extraia em um diretório qualquer.
3- Adicione ANT_HOME nas variáveis do sistema com o caminho para o diretório no qual vocês extraiu.

Pronto, o Ant está configurado, agora vamos baixar o socket.io-java-client. (https://github.com/Gottox/socket.io-java-client)
Na wiki do github tem bem explicadinho, qualquer dúvida coloque aqui nos comentários que eu explico melhor.

Após compilado o soket.io-java-client, você terá na pasta /jar um arquivo chamado socketio.jar, copie esse arquivo para a pasta lib do seu projeto, no android studio clique no jar com o botão direito e depois clique em "Add as library..." para adicionar ao seu projeto.
Pronto, agora estamos com tudo configurado, vamos começar a programar.

No meu projeto (que depois irei liberar no github), eu criei 2 classes para facilitar, mas você pode utilizar uma só.

Teremos a classe que fará a conexão que eu chamei de Client:


public class Client {
    private final String SERVER = "http://ip-do-servidor:porta/";
    private IOParser parser = new IOParser();
    public void Connect() {
        try {
            SocketIO socket = new SocketIO(SERVER);
            socket.connect(parser);
        }
        catch (Exception ex) {

        }
    }
}


E a outra classe que será responsável por interpretar as mensagens comunicações:

public class IOParser implements IOCallback {
    @Override
    public void onDisconnect() {

    }

    @Override
    public void onConnect() {

    }

    @Override
    public void onMessage(String s, IOAcknowledge ioAcknowledge) {

    }

    @Override
    public void onMessage(JSONObject jsonObject, IOAcknowledge ioAcknowledge) {

    }

    @Override
    public void on(String s, IOAcknowledge ioAcknowledge, Object... objects) {

    }

    @Override
    public void onError(SocketIOException e) {

    }
}

Aqui eu nem comecei a implementar, mas dá pra ter uma idéia fácil de como fazer isso.
Na classe Client você poderá adicionar depois métodos para envio de mensagens e deixar a IOParser apenas para recepção.
Bom isso é o básico, conforme vou vendo alguma coisa nova irei colocando os tutoriais aqui.

Obrigado e até a próxima!



Projetos em Pi - Reunindo funções

Bom dia para todos,
Desde a última postagem, resolvi que irei usar o LCD TFT que comprei, porém tenho que comprar mais 1 chip para poder testar tudo direitinho, enquanto não consigo comprá-lo, criei esse post para reunir as funções que colocarei nesse projeto.

O Básico

Esse projeto será para colocar o raspberry pi em um carro, com alimentação por bateria + recarregamento por painéis solares, então toda função deverá ser útil para essa situação.

As Funções

Até agora já reuni algumas funções que desejo colocar, entre elas:
-Music Player (tocar músicas que estarão em bibliotecas dentro do RPi, selecionando-as pelo celular ou tablet, conectando a saída de audio do RPi ao som do carro, e ainda sincronizando as bibliotecas com a wifi de casa, caso o alcance da wifi chegue na rua).
-Video Player (mesma coisa que as músicas, porém com vídeos, irei conectar um monitor HDMI 15'' no RPi, que também será alimentado pela mesma bateria)
-GPS Module (um módulo de GPS para navegação, através do Google Maps, ou Waze (se conseguir), pela tela TFT)
-Emuladores (colocarei emuladores de MAME, SNES, etc, para jogar pelo monitor, quando não assistindo vídeos)
-Controles PS3 (conectar os controles do ps3 para poder jogar os emuladores)

**A bateria que estou usando no projeto é uma bateria 12V de nobreak, coloquei uma bateria separada para não prejudicar (descarregar) a bateria do automóvel)

Pedido

Agora peço à todos que tiverem idéias para mais funções, colocá-las nos comentários e irei atualizando esse post com todas as funções que achar possíveis e úteis.


Obrigado à todos, e até a próxima.

sexta-feira, 15 de agosto de 2014

Projetos em Pi - Em busca da Tela

Bom dia,
Descobri que o chip que eu tenho é errado para o trabalho de ligar o monitor TFT, porém, tenho um tablet velho jogado, abri ele, tirei o LCD + Touchscreen.
Agora estou no trabalho de pesquisa para tentar conectar esse LCD no raspberry pi, se eu consegui, vai ficar até melhor do que eu esperava. Porém como o tablet é chinês, é difícil achar os datasheets do lcd, ou seja, vou ter muito trabalho para achar, e pode ser que nem ache.
Bom por hoje é só, vou continuar na pesquisa, caso encontre algo, atualizarei o blog aqui.
Até lá.

quarta-feira, 13 de agosto de 2014

Projetos em Pi - O Recomeço

Bom, após ficar um bom tempo parado, decidi hoje retornar aos meus projetos em pi.
Primeiramente, irei testar um LCD que eu comprei, (LCD + Touch), se alguém quiser verificar o modelo e schemas que vou usar estão aqui (http://marks-space.com/2013/05/23/raspberry-pi-with-a-3-2-tft-with-touch-control/).
Caso consiga colocar para funcionar o LCD com o touch, irei criar uma classe em python para controlá-lo através do socket.io, ou seja, fazer a comunicação entre a UI (botões e touch) e os componentes do projeto (músicas, controle, sensores, etc).
Hoje à tarde postarei mais atualizações, e espero não ter que parar novamente, agora vamos até o final.
Até a próxima.