Exemplo de API de Câmera do PhoneGap*

Este artigo descreve o projeto e a implementação de uma aplicação de exemplo da utilização da API de Câmera do PhoneGap. A aplicação demonstra a utilização da API para capturar fotos com a câmera do dispositivo, e para utilizar fotos armazenadas em um álbum de fotos. Ela possui as seguintes características: permite que o usuário tire fotos com a câmera do dispositivo ou selecione fotos armazenadas em um álbum de fotos existentes no dispositivo. O usuário pode alterar as configurações utilizadas para tirar as fotos, como a qualidade da imagem e dimensões, entre outras.

A aplicação usa a biblioteca jQuery e jQuery Mobile (para estilos).

O código fonte pode ser encontrado aqui.

Aparência final

A aplicação possui 3 telas (também chamadas de páginas):

  1. Uma página onde o usuário possa escolher a fonte da imagem (câmera, biblioteca de fotos ou album de fotos), acessar as configurações de captura de imagem e visualizar as configurações em uso. Na parte de baixo da página existe um link para a licença da aplicação.
  2. Página de configuração de captura de imagem (alterações nas configurações podem ser armazenadas ou descartadas).
  3. Página de resultado da captura, onde a imagem capturada é apresentada.

Segue abaixo as capturas da tela principal e da tela de configurações.

Considerações de Projeto e Estrutura Geral

A aplicação utiliza a API de Câmera do PhoneGap para acessar a câmera do dispositivo para capturar novas fotos e para acessar os álbuns de fotos armazenadas no dispositivo. A aplicação utiliza o método Camera.getPicture(onCaptureSuccess, onCaptureError, [cameraOptions]) para capturar fotos e o método Camera.cleanup(onSuccess, onError) (somente o iOS) para remover arquivos temporários criados pela aplicação. A versão do PhoneGap utilizada neste artigo é a 2.0.0.

Objeto Settings

Camera.getPicture() pode ser chamada com uma variedade de parâmetros opcionais que definem as características da foto, como qualidade, altura e comprimento, entre outros (por favor, consulte a documentação de referência para as opções de Câmera). A aplicação de exemplo permite que o usuário configura as opções de câmera (cameraOptions): estes parâmetros podem ser configurados na página de Configurações, e armazenados no objeto settings (uma variável global da classe Settings). Segue abaixo um trecho de código demonstrando a declaração da classe Settings (por favor, consulte a documentação de referência para mais informações):

       // Armazena cameraOptions (parâmetros opcionais para personalizar as configurações de câmera) com o qual camera.getPicture() é chamado

var settings;

// Classe representando o armazenamento de cameraOptions com o qual camera.getPicture() é chamad

function Settings() {

    // Opções de abertura :

    this.destinationType = Camera.DestinationType.FILE_URI;     // cameraOptions: Tipo de destino 

    this.sourceType = Camera.PictureSourceType.PHOTOLIBRARY;    // cameraOptions: Tipo de Origem

    this.mediaType = Camera.MediaType.PICTURE;                  // cameraOptions: mediaType

    // Qualidade da foto e opções de edição:

    this.quality = 40;                                          // cameraOptions: qualidade

    this.targetWidth = 500;                                     // cameraOptions: largura

    this.targetHeight = 500;                                    // cameraOptions: altura

    this.allowEdit = true;                                      // cameraOptions: permite a edição

    this.correctOrientation = true;                             // cameraOptions: corrigir a orientação

    // Opções de gravação:

    this.encodingType = Camera.EncodingType.JPEG;               // cameraOptions: tipo de codificação

    this.saveToPhotoAlbum = true;                               // cameraOptions: salvar no álbum de fotos

    // Específico do iOS (para especifivcar a localização do popover no iPad):

    this.popoverOptions = new CameraPopoverOptions(220, 600, 320, 480, Camera.PopoverArrowDirection.ARROW_DOWN);    // cameraOptions: popoverOptions

}

Página de Configurações

Configurações da câmera podem ser alteradas pelo usuário na página de configurações. Somente três opções estão disponíveis:

  1. destinationType - está sempre configurado para Camera. DestinationType.FILE_URI. Este valor é recomendado por evitar a sobrecarga de memória; 
  2. popoverOptions - é uma opção específica do iPad. Ele define o tamanho e a posição da janela popover; 
  3. sourceType - é setado para o valor apropriado cada vez que um dos três botões para tirar foto é selecionado e pressionado pelo usuário na tea principal da aplicação.

As configurações são acessadas e alteradas através do formulário HTML5 que faz parte da página de Configurações:

<!-- Página de edição das configurações -->

<div data-role="page" id="settings_page" data-theme="a">

    <div data-role="header">

        <a href="#home_page" id="settings_cancel_button" data-role="button" data-mini="true" class="ui-btn-left">Cancel</a>

        <h3>Picture Settings</h3>

        <a href="#home_page" id="settings_ok_button" data-role="button" data-theme="b" data-mini="true" class="ui-btn-right">Save</a>

    </div>

    <div data-role="content">

        <form name="settings_form" id="settings_form">

            .....                <!-- todos os elementos do formulário são definidos aqui -->

      </form>

   </div>

</div>

As alterações feitas pelo usuário no formulário podem aceitas ou recusadas. Para gravar as alterações, a função applySettings() é chamada. Ela armazena as configurações selecionadas no objeto setttings.

// Lê opções personalizadas das opções de câmera do settings_form e as armazena no objeto settings (cameraOptions storage)

function applySettings() {

    var settingsBatch = getElement("settings_form");

    if (settingsBatch == null) {

        return;

    }

    var newQuality = parseInt(settingsBatch.elements["quality_input"].value, 10);

    if (!isNaN(newQuality) && (newQuality <= 100) && (newQuality >= 0)) {

        settings.quality = newQuality;

    }

    var newWidth = parseInt(settingsBatch.elements["width_input"].value, 10);

    if (!isNaN(newWidth) && (newWidth <= 1500) && (newWidth >= 50)) {

        settings.targetWidth = newWidth;

    }

    var newHeight = parseInt(settingsBatch.elements["height_input"].value, 10);

    if (!isNaN(newHeight) && (newHeight <= 1500) && (newHeight >= 50)) {

        settings.targetHeight = newHeight;

    }

    settings.allowEdit = settingsBatch.elements["edit_input"].checked;

    settings.correctOrientation = settingsBatch.elements["orient_input"].checked;

    settings.saveToPhotoAlbum = (settingsBatch.elements["save_input"].options[settingsBatch.elements["save_input"].selectedIndex].value == "true") ? true : false;

    settings.encodingType = parseInt(settingsBatch.elements["encod_input"].options[settingsBatch.elements["encod_input"].selectedIndex].value, 10);

    settings.mediaType = parseInt(settingsBatch.elements["media_input"].options[settingsBatch.elements["media_input"].selectedIndex].value, 10);

    .....

}

No caso de não aceitar as alterações às configurações, o objeto settings não é atualizado e é utilizado para recuperar os valores no formulário. Adicionalmente, como os elementos dos formulários estão embutidos em wrappers da UI do jQuery Mobile (usado para deixar a interface do aplicativo mais bonita), os elementos jQM devem ser atualizados (refresh) para mostrar as alterações feitas através da programação. Esta ação é feita pela função abaixo:

       // Aplica as opções de câmera armazenadas no objeto settings ao settings_form e atualiza os elementos de visíveis do formulário.

// Deve ser usada quando o usuário altera o estado dos elementos do formulário mas não pretende armazenar as alteraçõess

function restoreSettings() {

    $("#quality_input").val(settings.quality).slider("refresh");

    $("#width_input").val(settings.targetWidth).slider("refresh");

    $("#height_input").val(settings.targetHeight).slider("refresh");

    if (settings.allowEdit) {

        $("#edit_input").attr("checked", true).checkboxradio("refresh");

    } else {

        $("#edit_input").removeAttr("checked").checkboxradio("refresh");

    }

    if (settings.correctOrientation) {

        $("#orient_input").attr("checked", true).checkboxradio("refresh");

    } else {

        $("#orient_input").removeAttr("checked").checkboxradio("refresh");  

    }

    var saveSwitch = $("#save_input");

    saveSwitch[0].selectedIndex = ((settings.saveToPhotoAlbum === true) ? 1 : 0);

    saveSwitch.slider("refresh");

    $("#encod_input").val(settings.encodingType).selectmenu("refresh");

    $("#media_input").val(settings.mediaType).selectmenu("refresh");

}

Captura de Foto

O processo de captura de foto é iniciado pressionando qualquer um dos três botões da tela principal do App. Ao clicar no botão, o método onCapture é chamado. Ele ajusta o valor apropriado da opção sourceType da câmera (que pode ser PHOTOLIBRARY, CAMERA ou SAVEDPHOTOALBUM) e chama o método Camera.getPicture() com as opções definidas no objeto settings. Trechos do código da página principal e da função onCapture() são mostrados abaixo:

         index.html:

         <!-- Home Page (menu page) -->

        <div data-role="page" id="home_page" data-theme="a">

            ....

            <div data-role="content">

                <a href="#" data-role="button" id="open_camera_button">Capture a photo with camera</a>

                <a href="#" data-role="button" id="open_lib_button">Open Photo Library</a>

                <a href="#" data-role="button" id="open_alb_button">Open saved photo album</a>

                .....

            </div>

            .....

        </div>

camera,js:

// Chama o método camera.getPicture() com as cameraOptions personalizadas pelo usuário

function onCapture(e) {

    var callerId = getTargetId(e, "a");

    switch (callerId) {

        case "open_camera_button":

            settings.sourceType = Camera.PictureSourceType.CAMERA;

            break;

        case "open_lib_button":

            settings.sourceType = Camera.PictureSourceType.PHOTOLIBRARY;

            break;

        case "open_alb_button":

            settings.sourceType = Camera.PictureSourceType.SAVEDPHOTOALBUM;

            break;

        default:

            return;

    }

    navigator.camera.getPicture(onCaptureSuccess, onCaptureError, { quality : settings.quality, 

                                                                    destinationType : settings.destinationType, 

                                                                    sourceType : settings.sourceType, 

                                                                    allowEdit : settings.allowEdit, 

                                                                    encodingType : settings.encodingType,

                                                                    targetWidth : settings.targetWidth,

                                                                    targetHeight : settings.targetHeight,

                                                                    mediaType: settings.mediaType,

                                                                    saveToPhotoAlbum : settings.saveToPhotoAlbum,

                                                                    correctOrientation: settings.correctOrientation,

                                                                    popoverOptions : settings.popoverOptions

                                                                  });

}

// Mostra a foto capturada por camera.getPicture()

function onCaptureSuccess(imageData) {

    var photo = getElement("pic");

    photo.style.display = "block";

    photo.src = imageData;

    $.mobile.changePage("#result_page", "slideup");

}

// função de callback do camera.getPicture() que mostra uma mensagem de erro  

function onCaptureError(message) { }

Caso a imagem seja capturada com sucesso, ela será apresentada na tela de Resultado.

Remoção de Arquivos Temporários

No iOS, as imagens capturadas são armazenadas no diretório temporário da aplicação. De acordo com esta convenção, é de responsabilidade do desenvolvedor a limpeza de quaisquer arquivos temporários que não são mais necessários. Esta aplicação remove os arquivos temporários cada vez que sai da tela de resultado (também está implementada no modo "exit" do botão voltar, mas atualmente não tem efeito algum). A remoção é feita através do método removeTemporaryFiles() que chama a função Camera.cleanup(onSuccess, onError). Cameraa.cleanup() deve ser usada apenas no iOS, e a detecção do iOS no dispositivo pode ser feita pela função isIOS():

// Remove todos os arquivos temporários criados pela aplicação. Deve ser usada quando os arquivos temporários não são mais necessários

function removeTemporaryFiles() {

    if (isIOS()) {

        navigator.camera.cleanup(onSuccess, onError); 

    }

    function onSuccess() {  }

    function onError(message) {  }

}

// Determina quando o dispositivo atual está executando o iOS

function isIOS() {

    var iDevices = ["iPad", "iPhone", "iPod"];

    for (var i = 0; i < iDevices.length ; i++ ) {

        if( navigator.platform.indexOf(iDevices[i]) !== -1){ 

            return true; 

        }

    }

    return false;

}

Nota:

Atualmente camera.cleanup() parece não estar removendo arquivos o iPad, iOS 5 e 6 (mesmo que a função onSuccess() seja chamada, bem como outras funções de operação de remoção de arquivos do PhoneGap). O diretório temporário é removido quando a aplicação é encerrada (ex. quando o dispositivo é desligado).

Botão Voltar

Como os dispositivos com Android possuem o botão voltar, seu comportamento precisa ser redefinido: se um usuário pressionar o botão voltar com a tela principal da aplicação aberta, a aplicação é encerrada, pulando todo o histório de páginas abertas (aqui, o método removeTemporaryFiles é incorporado one qualquer ação pode ser adicionada se necessário, mas nesta versão da aplicação ele não tem nenhum efeito no Android). Se a página de configurações for fechada pelo botão voltar, o formulário de configurações é restaurado de seu estado anterior:

// Sobrescreve o comportamento padrão do botão voltar do dispositivo

function onBackPress(e) {

    // Pula o histórico de páginas e sai da aplicação se a tela principal (home page) estiver ativa

    if($.mobile.activePage.is("#home_page")){

        e.preventDefault();

        removeTemporaryFiles();

        navigator.app.exitApp();

    }

    else {

        // Não armazena o novo cameraOptions e restaura o estado anterior dos elementos visuais do formulário de configurações 

        if ($.mobile.activePage.is("#settings_page")) {

            restoreSettings();

        }

        navigator.app.backHistory();

    }

}

onDeviceReady()

Chamadas à API do PhoneGap (por exemplo, chamadas à função Camera.getPicture()) tornam-se seguras apenas depois que a biblioteca do Cordova (PhoneGap) for completamente carregada e estiver pronta para trabalhar com o dispositivo. Quando isso acontece, o evento "deviceready" é disparado. Por este motivo, muitas das inicializações e vínculos a eventos são postergados até este momento, e são executados pela função onDeviceReady() que é o callback do evento "deviceready":

index.html

<body onload="onLoad()"> ... </body>

camera.js

// Chamada no bodyLoad 

function onLoad() {

    document.addEventListener("deviceready", onDeviceReady, false);

    ....

}

// Chamada quando o Cordova estiver completamente carregado (e as chamada à API podem ser feitas com segurança)

function onDeviceReady() {

    // Sobrescreve o comportamento padrão do botão voltar

    document.addEventListener("backbutton", onBackPress, false);

    fillSettingsInfo("settings_info");

    

   // Associa botões da aplicação à suas funções

    ...              // Associa callbacks de elementos e eventos 

}

Tornando a Aplicação mais Bonita

Os estilos aplicados à tabela de informações de configuração estão armazenados no arquivo camera.css. Outros estilos de elementos são definidos com a ajuda da biblioteca jQuery Mobile.

Resultado dos Testes

A tabela abaixo apresenta o suporte aos CameraOptions support pelos sistemas operacionais testados.

Notação:

+ -- a opção é totalmente suportada;

- -- a opção não é suportada.

Comentários adicionais estão entre parênteses:

Dispositivos Android 2.x e Android 4.x  Dispositivos Apple iOS* 5
quality +

+ (afeta apenas imagens temporárias. Imagens armazenadas no álbum de fotos sempre são em alta qualidade)

destinationType  + +
sourceType  + (mas Camera.PictureSourceType.PHOTOLIBRARY e Camera.PictureSourceType.SAVEDPHOTOALBUM abrem a mesma fonte) +
allowEdit  - +
encodingType - (a imagem é sempre salva em JPG) - (a imagem é sempre salva em JPG)
targetWidth + +
targetHeight + +
mediaType + +
correctOrientation

+ (mas no Android 2.3 a opção funciona apenas na primeira vez que a imagem é capturada com o método getPicture(). Problemas possíveis: as informações EXIF são corrompidas durante a chamada do método)

- (imagens sempre possuem a orientação correta, mesmo quando a correção está desabilitada)
saveToPhotoAlbum + (funciona apenas se Camera.PictureSourceType.CAMERA e Camera.DestinationType.FILE_URI forem selecionados) +
popoverOptions - (não aplicável: é uma opção exclusiva do iPad) + (é uma opção específica do iPad)

Nota:

Atualmente cameraOptions não produz efeito algum e não pode ser testada em um Emulador Ripple.

Dispositivos Testados

  • Dispositivos Móveis:
    • Apple iPad* 2 tablet (iOS 5.1.1)
    • Smartphone Sony Ericsson* Go (Android 2.3.7)
    • Samsung Galaxy Tab* 2 tablet (Android 4.0.3)
Kategorien:
Nähere Informationen zur Compiler-Optimierung finden Sie in unserem Optimierungshinweis.