Translate

quinta-feira, 20 de outubro de 2016

ANDROID - Aplicativo para tirar fotos

Roteiro para desenvolvimento de aplicativos para tirar fotos.


Direto ao assunto, alguns passos para desenvolver uma aplicação que tire/use fotos.


Pedindo permissão para usar a câmera




Se uma função essencial da sua aplicação é tirar fotos, restringir a sua visibilidade no Google Play a dispositivos que possuem uma câmera. Para anunciar que a sua aplicação depende de ter uma câmera, colocar uma tag <uses-feature> no seu arquivo de manifesto:

<manifest ... >
    <uses-feature android:name="android.hardware.camera"
                  android:required="true" />
    ...</manifest>


Se seu aplicativo usa, mas não exige uma câmera, a fim de função, em vez disso "set android:required to false". Ao fazê-lo, o Google Play permitirá que dispositivos sem uma câmera possam fazer o download do aplicativo. É então a sua responsabilidade de verificar a disponibilidade da câmara em tempo de execução chamando hasSystemFeature(PackageManager.FEATURE_CAMERA). Se a câmera não estiver disponível, então você deve desativar seus recursos de câmera.

Tirando uma foto com app da Câmera






A maneira Android de delegar ações para outras aplicações é invocar uma Intent que descreve o que você quer fazer. Este processo envolve três partes: O Intent em si, uma chamada para iniciar o externo Activity, e algum código para lidar com os dados da imagem quando o foco retorna para sua atividade.
Aqui está uma função que chama a intenção de capturar uma foto.
static final int REQUEST_IMAGE_CAPTURE = 1;
private void dispatchTakePictureIntent() {
    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
        startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
    }
}
Note-se que o startActivityForResult() método é protegida por uma condição que exige resolveActivity(), que retorna o primeiro componente da atividade que pode lidar com a intenção. Executar esta verificação é importante porque se você chamar startActivityForResult() usando uma intenção que nenhum aplicativo pode manipular, seu aplicativo irá falhar. Então, enquanto o resultado não é nulo, é seguro usar a intenção.

Obtendo o Thumbnail





Se o tirar uma foto não é o ambição principal  da sua aplicação, então você provavelmente pode obter a imagem   a partir do aplicativo de câmera e fazer algo com ela.
A aplicação Câmara Android codifica a foto no retorno Intent entregue a onActivityResult() como um pequeno Bitmap nos extras, sob a chave "data". O código a seguir recupera esta imagem e exibi-lo em um ImageView.
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
        Bundle extras = data.getExtras();
        Bitmap imageBitmap = (Bitmap) extras.get("data");
        mImageView.setImageBitmap(imageBitmap);
    }
}
Nota: Esta imagem é uma miniatura de "data" e pode ser bom para um ícone, mas não muito mais. Lidar uma imagem de tamanho normal pode demorar um pouco mais e demandar mais  trabalho.



Salvando a foto em tamanho “normal”


A aplicação de Câmera do Android salva uma foto de tamanho “normal” em  um arquivo. Você deve informar um nome de arquivo dentro dos padrões no qual o aplicativo da câmera deve salvar a foto.

Geralmente, as fotos que o usuário capta com a câmera do dispositivo são guardadas no dispositivo de armazenamento público  acessíveis por todos os aplicativos. O diretório apropriado para fotos compartilhadas é fornecida por getExternalStoragePublicDirectory(), com o argumento DIRECTORY_PICTURES. Porque o diretório fornecido por este método é compartilhada entre todos os aplicativos, para ler e escrever exige a permissão READ_EXTERNAL_STORAGE e WRITE_EXTERNAL_STORAGE, respectivamente. A permissão de gravação permite implicitamente leitura, por isso, se você precisa escrever no  armazenamento público, então você precisa solicitar apenas uma autorização:
<manifest ...>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    ...</manifest>
No entanto, se você gostaria que suas fotos para permanecessem no privado,  só para o seu aplicativo, você pode passar a usar o diretório fornecido pela getExternalFilesDir(). No Android 4.3 e inferior, escrever neste diretório também exige a permissão WRITE_EXTERNAL_STORAGE. A partir do Android 4.4, a permissão não é mais necessária porque o diretório  está acessível para outras aplicações, para que possa declarar a permissão deve ser solicitada apenas nas versões mais baixas do Android, adicionando o atributo maxSdkVersion:
<manifest ...>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
                     android:maxSdkVersion="18" />
    ...</manifest>
Nota: Os arquivos que você salvar nos diretórios fornecidos pelo getExternalFilesDir() ou getFilesDir() serão apagados quando o usuário desinstalar a sua app.
Depois de decidir o diretório para o arquivo da foto, você precisa criar um nome de arquivo exclusivo e que não haja possibilidade de ser sobre-escrito. Você pode desejar também salvar o caminho em uma variável para uso posterior. Aqui está uma solução exemplo, em um método que retorna um nome de arquivo exclusivo para uma nova foto usando uma combinação de  data e hora:
String mCurrentPhotoPath;
private File createImageFile() throws IOException {
    // monta no nome para o arquivo de imagem
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    String imageFileName = "JPEG_" + timeStamp + "_";
    File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
    File image = File.createTempFile(
        imageFileName,  /* prefixo */
        ".jpg",         /* sufixo*/
        storageDir      /* pasta*/
    );

    // salva o arquivo: caminho para usar com ACTION_VIEW  
    mCurrentPhotoPath = "file:" + image.getAbsolutePath();
    return image;
}
Com este método disponível para criar um arquivo de foto, você pode agora criar e invocar um Intent como este:
static final int REQUEST_TAKE_PHOTO = 1;
private void dispatchTakePictureIntent() {
    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    //  Certifique-se de que há uma camera "activity" para lidar com a intent    if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
        // Criar o arquivo onde a foto deve ir
        File photoFile = null;
        try {
            photoFile = createImageFile();
        } catch (IOException ex) {
            // Erro enquanto tentava criar o arquivo
            ...
        }
        // se o arquivo foi criado com sucesso continua
        if (photoFile != null) {
            Uri photoURI = FileProvider.getUriForFile(this,
                                                  "com.example.android.fileprovider",
                                                  photoFile);
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
            startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
        }
    }
}
Nota: Nós estamos usando getUriForFile(Context, String, File) que retorna um content:// URI.Para aplicações mais recentes visando Android N e superior, passando a file:// URI entre um limite de pacote faz com que um FileUriExposedException. Portanto, apresentamos agora uma forma mais genérica de armazenamento de imagens usando um FileProvider.
Agora, você precisa configurar o FileProvider. No manifesto do aplicativo, adicionar um provedor para sua aplicação:
<application>
   ...
   <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.example.android.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths"></meta-data>
    </provider>
    ...</application>
Certifique-se de que a cadeia autoridades corresponde ao segundo argumento para getUriForFile(Context, String, File).Na seção de meta-dados da definição do fornecedor, você pode ver que o provedor espera caminhos elegíveis para ser configurado em um arquivo de recurso dedicado, res/xml/file_paths.xml. Aqui está o conteúdo necessário para este exemplo particular:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="my_images" path="Android/data/com.example.package.name/files/Pictures" />
</paths>
O componente do caminho corresponde ao caminho que é devolvido pelo getExternalFilesDir() quando chamado com Environment.DIRECTORY_PICTURES. Certifique-se de substituir com.example.package.name com o nome do pacote real do seu aplicativo. Além disso, confira a documentação de FileProvider para uma extensa descrição do caminho especifica que você pode usar além external-path.


Adicionando a Foto a galeria



Quando você cria uma foto através de uma intenção, você deve saber onde sua imagem está localizada, por isso  você disse em primeiro lugar  onde ela será salva. Para todos os outros, aplicativos talvez a maneira mais fácil de fazer a sua foto acessível é a partir de provedor de mídia do sistema.
Note: Se você salvou sua foto para o diretório fornecido pela getExternalFilesDir(), o scanner de mídia não pode acessar os arquivos porque eles são privados para a sua aplicação.
O seguinte método de exemplo demonstra como invocar scanner de mídia do sistema para adicionar sua foto ao banco de dados do Provedor de mídia, tornando-o disponível no aplicativo Gallery Android a outros aplicativos.
private void galleryAddPic() {
    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    File f = new File(mCurrentPhotoPath);
    Uri contentUri = Uri.fromFile(f);
    mediaScanIntent.setData(contentUri);
    this.sendBroadcast(mediaScanIntent);
}

Decodificar uma imagem dimensionada




Gerenciar várias imagens de tamanho normal pode ser complicado com a memória limitada. Se seu aplicativo em execução causar uma falta de memória após a exibição de apenas algumas imagens, você pode reduzir drasticamente a quantidade de heap dinâmica usada pela expansão do JPEG em uma matriz de memória que já está dimensionado para corresponder ao tamanho da exibição de destino. O seguinte exemplo demonstra o método desta técnica.

private void setPic() {
    // Obtém as dimensões para o View
    int targetW = mImageView.getWidth();
    int targetH = mImageView.getHeight();

    // Obtém as dimensões do bitmap
    BitmapFactory.Options bmOptions = new BitmapFactory.Options();
    bmOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
    int photoW = bmOptions.outWidth;
    int photoH = bmOptions.outHeight;

    // Determinar o quanto de reduzir a imagem
    int scaleFactor = Math.min(photoW/targetW, photoH/targetH);

    // Decodificar o arquivo de imagem em um tamanho de bitmap para preencher o view
    bmOptions.inJustDecodeBounds = false;
    bmOptions.inSampleSize = scaleFactor;
    bmOptions.inPurgeable = true;

    Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
    mImageView.setImageBitmap(bitmap);
}


Baseado nas informações do site oficial do Android Development