Aplicativo de câmera de trabalho simples, evitando o problema de intenção nula
- todo o código alterado incluído nesta resposta; perto do tutorial android
Como já gastei muito tempo com esse problema, decidi criar uma conta e compartilhar meus resultados com você.
O tutorial oficial para o Android "Tirar fotos com simplicidade" acabou não cumprindo o que prometia. O código fornecido não funcionava no meu dispositivo: um Samsung Galaxy S4 Mini GT-I9195 executando a versão android 4.4.2 / KitKat / API Level 19.
Eu descobri que o principal problema era a seguinte linha no método chamado ao capturar a foto ( dispatchTakePictureIntent
no tutorial):
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
Resultou na intenção posteriormente capturada por onActivityResult
ser nula.
Para resolver esse problema, tirei muita inspiração das respostas anteriores aqui e de alguns posts úteis no github (principalmente este do deepwinter - muito obrigado a ele; você também pode conferir a resposta dele em um post estreitamente relacionado ).
Seguindo esses conselhos agradáveis, escolhi a estratégia de excluir a putExtra
linha mencionada e fazer o correspondente para recuperar a foto tirada da câmera no método onActivityResult (). As linhas de código decisivas para recuperar o bitmap associado à imagem são:
Uri uri = intent.getData();
Bitmap bitmap = null;
try {
bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri);
} catch (IOException e) {
e.printStackTrace();
}
Eu criei um aplicativo exemplar que apenas tem a capacidade de tirar uma foto, salvá-lo no cartão SD e exibi-lo. Acho que isso pode ser útil para pessoas na mesma situação que eu quando me deparei com esse problema, já que as sugestões de ajuda atuais se referem principalmente a postagens bastante extensas no github que fazem a coisa em questão, mas não são fáceis de supervisionar para iniciantes como mim. Com relação ao sistema de arquivos que o Android Studio cria por padrão ao criar um novo projeto, eu apenas precisei alterar três arquivos para o meu propósito:
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.android.simpleworkingcameraapp.MainActivity">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="takePicAndDisplayIt"
android:text="Take a pic and display it." />
<ImageView
android:id="@+id/image1"
android:layout_width="match_parent"
android:layout_height="200dp" />
</LinearLayout>
MainActivity.java:
package com.example.android.simpleworkingcameraapp;
import android.content.Intent;
import android.graphics.Bitmap;
import android.media.Image;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MainActivity extends AppCompatActivity {
private ImageView image;
static final int REQUEST_TAKE_PHOTO = 1;
String mCurrentPhotoPath;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
image = (ImageView) findViewById(R.id.image1);
}
// copied from the android development pages; just added a Toast to show the storage location
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmm").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath = image.getAbsolutePath();
Toast.makeText(this, mCurrentPhotoPath, Toast.LENGTH_LONG).show();
return image;
}
public void takePicAndDisplayIt(View view) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (intent.resolveActivity(getPackageManager()) != null) {
File file = null;
try {
file = createImageFile();
} catch (IOException ex) {
// Error occurred while creating the File
}
startActivityForResult(intent, REQUEST_TAKE_PHOTO);
}
}
@Override
protected void onActivityResult(int requestCode, int resultcode, Intent intent) {
if (requestCode == REQUEST_TAKE_PHOTO && resultcode == RESULT_OK) {
Uri uri = intent.getData();
Bitmap bitmap = null;
try {
bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri);
} catch (IOException e) {
e.printStackTrace();
}
image.setImageBitmap(bitmap);
}
}
}
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.simpleworkingcameraapp">
<!--only added paragraph-->
<uses-feature
android:name="android.hardware.camera"
android:required="true" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- only crucial line to add; for me it still worked without the other lines in this paragraph -->
<uses-permission android:name="android.permission.CAMERA" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Observe que a solução que encontrei para o problema também levou a uma simplificação do arquivo de manifesto do Android: as alterações sugeridas pelo tutorial do Android em termos de adição de um provedor não são mais necessárias, pois não estou usando nenhum código do meu java. Portanto, apenas algumas linhas padrão - principalmente em relação às permissões - tiveram que ser adicionadas ao arquivo de manifesto.
Além disso, pode ser valioso ressaltar que a importação automática do Android Studio pode não ser capaz de lidar java.text.SimpleDateFormat
e java.util.Date
. Eu tive que importar os dois manualmente.