22 February 2016

How it all started (O começo)

Back in 2002, when I used to play for hours with former Micromedia's Flash™ and used to think my future would be working with some sort of digital design, I sketched a few lines that would be years later an inspiration for my first personal portfolio website.
It was the drawing of a kind of loft apartment. At that time I had a few Flash jobs done and I wished to showcase them in a website. From that point, I had a continuous flow of ideas, like having an elevator as the website's preloader and having some interactivity with users.
As the ideas were coming, I had to bring life to things, so I had to learn some scripting and that was the way I found to make myself a programmer.
What you'll see clicking the image preview below (Adobe Flash™ plugin required), is my former website like it was, back in 2007. Enjoy all it's interactivity and use the help at the top right corner as necessary. In spite its old fashioned website style, I still find myself having fun rediscoverying all of those "easter eggs" that I myself have put in it.

Em 2002, eu costumava brincar bastante com Flash™ (ex-Macromedia) e até pensava em trabalhar com algum tipo de design digital algum dia. Foi quando esbocei algumas linhas que seriam anos mais tarde uma inspiração para o meu primeiro site de portfólio pessoal.
Tratava-se do desenho de uma espécie de loft. Naquela época, eu tinha alguns trabalhos em Flash e eu queria apresentá-los em um site. Desde então, comecei a ter várias ideias, como a de que o preloader do site fosse um elevador e de ter alguma interatividade com os usuários.
À medida em que as idéias surgiam, eu tinha que trazer vida às coisas, foi quando tive que aprender um pouco de script e me tornar um programador.
O que você vai ver, clicando na imagem abaixo (requer instalação do plugin Adobe Flash™), é o meu ex-website como ele era em 2007. Desfrute de toda a sua interatividade e, se necessário, use a ajuda no canto superior direito. Apesar de um site antigo, eu ainda me divirto redescobrindo todos os "easter eggs" que eu mesmo coloquei nele.




09 February 2016

Android: Reading SQLite databases selected by the user



Hello people!
After long hours, days and months... I'm back in the saddle again!
I'm here to describe a problem I was facing regarding an android application development. The exception I was fighting is the same as described in this thread: android-could-not-open-database

But my problem was quite different because the file I was trying to open is chosen via an Intent. I mean, the scenario is that an user chooses a database file to overwrite the current one used by the application. This is a manual backup/ recovery procedure where the user first backs-up its data and sends via email or whatever it wants to. After, he/ she grabs the file from the storage (after downloading it from email or whatever) and overwrite the current database.

But this procedure must be obviously validated regarding the file type and the version of the (SQLite) database. The version of the backed-up database must be the same as the current one for the application keep working.
The exception was thrown when I was trying to get the SQLiteDatabase from the chosen file:
SQLiteDatabase db = SQLiteDatabase.openDatabase(file.getPath(), null, SQLiteDatabase.OPEN_READONLY);
And the exception was:
SQLiteCantOpenDatabaseException: unknown error (code 14): Could not open database 
It's important to say that this procedure worked fine using an emulator, but it failed in my real device (Motorola XT1058 with Android 5.5 Lollipop).
I had in my manifest the permissions set correctly:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
And the file I was trying to read from SQLite was obtained in the result of the Intent used to get the file from the user's folder:

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case FILE_SELECT_CODE:
                if (resultCode == RESULT_OK) {
                    Uri uri = data.getData();
                    try {
                        InputStream fileInputStream = getContentResolver().openInputStream(data.getData());
                        BackupFile file  = new BackupFile(fileInputStream, uri.getPath());
                        BackupData backupData = new SQLiteBackupData(context, file);
                        DataSecurityEvent dataSecurityEvent = new SQLiteRecovery(backupData);
                        new BackupDataAsyncTask(context, dataSecurityEvent).execute();
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                        ToastOnMiddle.makeText(context, context.getString(R.string.recover_fail), 
                           Integer.parseInt(context.getString(R.string.app_toast_time))).show();
                    }

                }
                break;
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

Note that the file used by the recovery validation procedure was the same chosen by the user. After hours I could figure out that this was the problem. My device didn't make use of an external storage and the downloaded files was stored internally. That was the difference between the device and emulator. Then, I changed my code to create a temporary file and fill its content with the content of the file grabbed by the user. This way, the file managed afterwards by the recovery component was located in the temp folder and no permission problem would happen. This is how the code looks like after the refactor:

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case FILE_SELECT_CODE:
                if (resultCode == RESULT_OK) {
                    Uri uri = data.getData();
                    try {
                        InputStream fileInputStream = getContentResolver().openInputStream(data.getData());
                        File tempFile = File.createTempFile(uri.getLastPathSegment(), null, context.getCacheDir());

                        FileChannel originChannel = ((FileInputStream)fileInputStream).getChannel();
                        FileOutputStream destOutput = new FileOutputStream(tempFile);
                        FileChannel destChannel = destOutput.getChannel();
                        originChannel.transferTo(0, fileInputStream.available(), destChannel);

                        BackupFile file  = new BackupFile(new FileInputStream(tempFile), tempFile.getPath());
                        BackupData backupData = new SQLiteBackupData(context, file, new SQLiteBackupDataValidator(file));
                        DataSecurityEvent dataSecurityEvent = new SQLiteRecovery(backupData);
                        List<DataSecurityEvent> events = new ArrayList<DataSecurityEvent>(0);
                        events.add(dataSecurityEvent);
                        new BackupDataAsyncTask(context, events).execute();
                    } catch (IOException e) {
                        e.printStackTrace();
                        ToastOnMiddle.makeText(context, context.getString(R.string.recover_fail), 
                           Integer.parseInt(context.getString(R.string.app_toast_time))).show();
                    } catch (BackupDataException bde) {
                        ToastOnMiddle.makeText(context, getExceptionMessageValue(bde.getMessage()), 
                           Integer.parseInt(context.getString(R.string.app_toast_time))).show();
                    }
                }
                break;
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

Note that I used FileChannel to fill the temp file with the selected file content.
I don't know exactly yet why this problem occurs when trying to create or open a SQLite database directly from an internal storage folder. If someone can explain in the comments below, I would be very glad! =)
Lesson learned: Do not get a database file from a file chooser intent and try to use it directly to create or open a SQLite database. You better off using a temporary file instead.