Mostrando postagens com marcador Foto. Mostrar todas as postagens
Mostrando postagens com marcador Foto. Mostrar todas as postagens

terça-feira, 7 de junho de 2016

Carregando imagens da galeria do Android

Olá povo,

Nesse post rápido vou mostrar como carregar imagens da galeria de mídia do Android. O único detalhe a observar nesse post é que estamos carregando imagens tanto locais quando as que estão em algum serviço da nuvem como DropBox e Google Photos. Por isso, para começar coloque as permissões necessárias no AndroidManifest.xml.
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/>
Lembrando que para o Android 6 (API Level 23) você deve checar essas permissões em tempo de execução.

Em seguida, inicie uma nova activity utilizando a ação ACTION_GET_CONTENT.
private static final int IMAGE_GALLERY_REQUEST = 1;
...
public void selectImageClick(View view) {
    if (ActivityCompat.checkSelfPermission(this, 
            Manifest.permission.READ_EXTERNAL_STORAGE) ==
            PackageManager.PERMISSION_GRANTED) {
        Intent intent = new Intent();
        intent.setType("image/*");
        intent.setAction(Intent.ACTION_GET_CONTENT);
        startActivityForResult(
                Intent.createChooser(
                        intent,
                        getString(R.string.select_picture_title)),
                IMAGE_GALLERY_REQUEST);
    } else {
        ActivityCompat.requestPermissions(this,
                new String[]{ Manifest.permission.READ_EXTERNAL_STORAGE },
                IMAGE_GALLERY_REQUEST);
    }
}
Ao selecionarmos uma imagem, o método onActivityResult será chamado.
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == IMAGE_GALLERY_REQUEST && resultCode == RESULT_OK){
        new LoadImageTask(this).execute(data.getData());
    }
}

Como falamos anteriormente, carregaremos as imagens do sistema de arquivos ou da web. Por isso, devemos realizar essa operação fora da UI thread, então definimos a classe LoadImageTask.
class LoadImageTask extends AsyncTask<Uri, Void, Bitmap> {

    WeakReference<PickImageActivity> mActivity;

    public LoadImageTask(PickImageActivity activity) {
        this.mActivity = new WeakReference<>(activity);
    }

    public PickImageActivity getActivity() {
        return mActivity.get();
    }

    @Override
    protected Bitmap doInBackground(Uri... params) {
        if (getActivity() != null) {
            try {
                return BitmapFactory.decodeStream(
                        getActivity().getContentResolver().openInputStream(params[0]));
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        super.onPostExecute(bitmap);
        if (getActivity() != null){
            getActivity().showImage(bitmap);
        }
    }
}
Temos algumas coisas interessantes aqui. A primeira é essa classe não deve ser uma inner class da Activity/Fragment, pois uma inner class sempre contém uma referência para sua outer class. Uma vez que ela não é uma inner class, devemos passar a referência da activity/fragment para essa classe. Estamos utilizando uma WeakReference para armazenar a instância da activity, o motivo disso é evitar o leak de memória, já que se o usuário sair da tela antes da imagem ser baixada aquela memória ficará alocada desnecessariamente durante algum tempo.
Outra coisa interessante é que estamos utilizando o método openInputStream() da classe ContentResolver para obter a imagem propriamente dita.
A imagem retornada terá o seu tamanho original, se você preferir, pode utilizar a técnica de redimensionamento de imagem que eu mostrei nesse post.
Por fim, no onPostExecute, invocamos o método showImage da Activity.
public void showImage(Bitmap bitmap) {
    ImageView imageView = (ImageView)findViewById(R.id.imageView);
    if (imageView != null){
        imageView.setImageBitmap(bitmap);
    }
}
Qualquer dúvida, deixe seu comentário ;)

 4br4ç05,
nglauber

quarta-feira, 16 de maio de 2012

Facebook API no Android

Olá povo,

Até a presente data, eu não tenho conta (de verdade) no Facebook. Por isso que até agora não tinha colocado nada relacionado aqui no blog. Mas depois de tantos alunos me perguntando como integrar a aplicação com o Facebook, resolvi deixar registrado como utilizar essa rede social.

Registrando a aplicação do Facebook

O processo é bem similar ao que mostrei no post sobre integração com o Twitter. Devemos primeiro criar/registrar nossa aplicação no Facebook. Para tal, acesse: https://developers.facebook.com/apps, faça o login com uma conta do facebook (aconselho criar uma conta de teste) e clique na opção Create New App. A janela abaixo será exibida:
Digite o nome da aplicação e clique em Continue.

Nota: Por questões de segurança o Facebook pede que você coloque uma informação que te identifique. No meu caso, eu tive que preencher o número do meu celular. Uma vez que cadastrei, o Facebook envia um código de ativação via SMS. Clique no link que foi enviado para o seu aparelho ou digite o código de confirmação para concluir o seu cadastro.

Uma vez que a aplicação está cadastrada, habilite a opção Native Android App.

Nós precisaremos preencher o campo Android Key Hash que é o identificador da nossa aplicação. Ele é gerado a partir do arquivo *.keystore que é usado pra assinar nossa aplicações Android. Aqui, utilizaremos o debug.keystore.
Para gerar esse hash, precisaremos do OpenSSL. Se você estiver no Windows, baixe o openssl-for-windows (http://code.google.com/p/openssl-for-windows/) e descompacte o ZIP na unidade C:

Ainda se você estiver no Windows, abra o prompt de comando, e digite o comando abaixo para adicionar o diretório do openssl no path.
set PATH=%PATH%;C:\openssl-0.9.8k_WIN32\bin

Para gerar a chave digite esse comando:
keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore | openssl sha1 -binary | openssl base64

Nota 1: o comando keytool é do JDK. Dessa forma, a pasta bin do JDK deve estar no seu PATH, se não tiver, adicione ou digite esse comando dentro da pasta bin do JDK.
Nota 2: se você estiver usando Windows, substitua ~/.android/debug.keystore por C:\Users\seuusuario\.android\debug.keystore (se o nome do seu usuário tiver espaço, coloque o caminho entre aspas)

Copie a hash que foi gerada e cole no campo Android Key Hash e conclua o cadastro da aplicação no Facebook.

Importanto o projeto do Facebook SDK

O código do Facebook SDK está disponível em um repositório GIT, mas você pode baixá-lo no formato ZIP. Acesse https://github.com/facebook/facebook-android-sdk/ e selecione a opção Download this repository as a ZIP file". Terminado o download, descompacte onde desejar.

Vamos criar agora um novo projeto Android com o código que acabamos de baixar. No Eclipse, selecione File | New | Android Project, coloque o nome do projeto de facebooksdk e selecione a opção Create project from existing code. Selecione o diretório onde você descompactou o código do facebooksdk e clique em Finish.

Nota: Se o projeto der erros em relação a anotações @Override, você pode removê-las.

Criando um projeto de exemplo

Vamos criar uma aplicação simples que realizará o login no facebook e atualizará o status do usuário. Crie um novo projeto Android e ao concluir o assistente, vamos adicionar a referência do projeto facebooksdk ao nosso projeto. Para tal, clique com o botão direito sobre o projeto e selecione Properties. No lado esquerdo, selecione Android, e na parte inferior adicione a referência ao projeto clicando em Add... e selecionando o facebooksdk.

Abra o AndroidManifest.xml e adicione a permissão de INTERNET.
<uses-permission 
  android:name="android.permission.INTERNET"/>

Agora, deixe a Activity da sua aplicação da seguinte forma:
public class ExFacebookActivity extends Activity {
 
  private static final String APP_ID = "ID_SUA_APP";
 
  private static final String
    ACCESS_EXPIRES = "access_expires";
  private static final String
    ACCESS_TOKEN = "access_token";

  private Facebook facebook;
  private SharedPreferences prefs;

  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    facebook = new Facebook(APP_ID);
    prefs = getPreferences(MODE_PRIVATE);
    // Carrega a accessToken pra saber se o usuário
    // já se autenticou.
    loadAccessToken();

    // Caso não tenha se autenticado, abre o login
    if(!facebook.isSessionValid()) {

      // Chama a tela de login do facebook
      facebook.authorize(this, 
        new String[] {"publish_stream"},
          new DialogListener() {
            // Login com sucesso, salva o accessToken
            public void onComplete(Bundle values) {
              saveAccessToken();
            }
            // Os métodos abaixo devem ser 
            // implementados para tratatmento dos
            // fluxos alternativos
            public void onFacebookError(
              FacebookError error) {}
    
            public void onError(DialogError e) {}
    
            public void onCancel() {}
          });
    }
  }
    
  public void onActivityResult(
    int requestCode, int resultCode, Intent data) {
    super.onActivityResult(
      requestCode, resultCode, data);
    // A API do Facebook exige essa chamada para 
    // concluir o processo de login.
    facebook.authorizeCallback(
      requestCode, resultCode, data);
  }
  
  // Evento de clique do botão para atualizar o status
  public void updateStatusClick(View v){
    EditText edt = (EditText)
      findViewById(R.id.editText1);
    updateStatus(edt.getText().toString()); 
  }

  private RequestListener requestListener = 
    new RequestListener() {
      public void onMalformedURLException(
        MalformedURLException e, Object state) {
        showToast("URL mal formada");
      }
      public void onIOException(
        IOException e, Object state) {
        showToast("Problema de comunicação");
      }
      public void onFileNotFoundException(
        FileNotFoundException e, Object state) {
        showToast("Recurso não existe");
      }
      public void onFacebookError(
        FacebookError e, Object state) {
        showToast("Erro no Facebook: "+ 
          e.getLocalizedMessage());
      }
      public void onComplete(
        String response, Object state) {
        showToast("Status atualizado");
      }
    };

  // Método que efetivamente atualiza o status
  private void updateStatus(String status) {
    AsyncFacebookRunner runner = 
      new AsyncFacebookRunner(facebook);
  
    Bundle params = new Bundle(); 
    params.putString("message", status);
    runner.request("me/feed", params, "POST", 
      requestListener, null);
  }
 
  private void showToast(final String s){
    final Context ctx = ExemploFacebookActivity.this;
    runOnUiThread(new Runnable() {
      public void run() {
        Toast.makeText(ctx, s, 
          Toast.LENGTH_SHORT).show();
      }
    });
  }

  private void saveAccessToken() {
    SharedPreferences.Editor editor = prefs.edit();
    editor.putString(
      ACCESS_TOKEN, facebook.getAccessToken());
    editor.putLong(
      ACCESS_EXPIRES, facebook.getAccessExpires());
    editor.commit();
  }

  private void loadAccessToken() {
    String access_token = 
      prefs.getString(ACCESS_TOKEN, null);
    long expires = prefs.getLong(ACCESS_EXPIRES, 0);
    if(access_token != null) {
      facebook.setAccessToken(access_token);
    }
    if(expires != 0) {
      facebook.setAccessExpires(expires);
    }
  }
}
A classe acima está comentada, então vou apenas ressaltar os pontos principais. No onCreate da Activity verificamos se o usuário está autenticado. Essa verificação é feita através do AccessToken que é salvo em uma SharedPreferences (veja mais aqui). Caso o usuário não esteja autenticado, chamamos o método autorize passando as permissões que a nossa aplicação requer para acessar determinados recursos. A lista completa de permissões está aqui. Como essa comunicação é feita em uma Thread separada (para não travar a GUI) é utilizado um RequestListener para saber o status da solicitação (sucesso ou falha).
O método updateStatus vai enviar uma mensagem para seu "wall" do Facebook. Nesse método, criamos uma AsyncFacebookRunner que realizará o processo em uma Thread separada. Criamos um objeto Bundle para passar os parâmetros da solicitação, que no nosso caso é apenas a mensagem.
O método request recebe como parâmetro:
- o recurso do facebook que queremos acessar, nesse caso "me/feed" (a lista completa pode ser vista aqui);
- o bundle com os parâmetros; - o método HTTP (GET ou POST); - um RequestListener similar ao que declaramos no login.

Por fim, vou colocar o layout da aplicação:
<LinearLayout 
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:orientation="vertical" >

  <EditText
    android:id="@+id/editText1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

  <Button
    android:id="@+id/button1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Atualizar Status" 
    android:onClick="updateStatusClick"/<

</LinearLayout>
Agora é só executar a aplicação. Ao iniciar pela primeira vez, será exibida a tela de login. Após fazer a autenticação, a tela de login é fechada e voltará automaticamente para aplicação. Aí é só digitar sua mensagem e postar no Facebook :)
Se quiser postar uma foto, basta substituir o método updateStatus por esse aqui:
private void sendPhoto(String status) {
  AsyncFacebookRunner runner = 
    new AsyncFacebookRunner(facebook);
  
  Bitmap image = BitmapFactory.decodeResource(
    getResources(), R.drawable.ic_launcher);

  ByteArrayOutputStream baos = 
    new ByteArrayOutputStream();   

  image.compress(
    Bitmap.CompressFormat.PNG, 100, baos); 

  byte[] bytes = baos.toByteArray();  

  Bundle params = new Bundle(); 
  params.putByteArray("picture", bytes);  
  params.putString("message", status);
     
  runner.request("me/photos", params, 
    "POST", requestListener, null);
} 
Como vocês devem conhecer o Facebook melhor que eu, vocês já devem imaginar que existem muitas outras possibilidades nessa API. Eu só quis mostrar o suficiente para começar. Quem quiser se aprofundar, dá uma olhada aqui.

Qualquer dúvida, deixem seus comentários.

4br4ç05,
nglauber

domingo, 21 de agosto de 2011

Galeria de mídia do Android

Olá povo,

Neste post vou mostrar como tirar uma foto com a aplicação de câmera, salvar a imagem em um diretório temporário e em seguida adiciona-la à galeria de mídia do aparelho. E por último, vou mostrar como carregar uma imagem da galeria.
Vamos iniciar mostrando como chamar a aplicação de câmera.
public void tirarFotoClick(View v) {
  String nomeFoto = DateFormat.format(
    "yyyy-MM-dd_hhmmss", new Date()).toString();

  caminhoFoto = new File(
    Environment.getExternalStoragePublicDirectory(
      Environment.DIRECTORY_PICTURES),
    nomeFoto);

  Intent it = new Intent(
    MediaStore.ACTION_IMAGE_CAPTURE);  
  it.putExtra(MediaStore.EXTRA_OUTPUT, 
    Uri.fromFile(caminhoFoto));
  startActivityForResult(it, 0);
}
A aplicação de câmera é chamada através da ação ACTION_IMAGE_CAPTURE passado no construtor da Intent. Um detalhe interessante aqui é o parâmetro EXTRA_OUTPUT, ele serve para indicar um caminho para a foto ser salva. Sem ele, a imagem será salva com tamanho e qualidade inferior a que a câmera realmente tirou.

caminhoFoto é um atributo do tipo File.
A chamada do método startActivityForResult indica que queremos tratar o resultado da activity que estamos chamando. Esse tratamento é feito no método onActivityResult.
protected void onActivityResult(
  int requestCode, int resultCode, Intent data) {
  super.onActivityResult(
    requestCode, resultCode, data);

  if (resultCode == RESULT_OK && requestCode == 0) {
    ImageView img = (ImageView)
      findViewById(R.id.imageView1);
   
    // Obtém o tamanho da ImageView
    int targetW = img.getWidth();
    int targetH = img.getHeight();
    
    // Obtém a largura e altura da foto
    BitmapFactory.Options bmOptions =
      new BitmapFactory.Options();
    bmOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(
      caminhoFoto.getAbsolutePath(), bmOptions);

    int photoW = bmOptions.outWidth;
    int photoH = bmOptions.outHeight;
    
    // Determina o fator de redimensionamento
    int scaleFactor = Math.min(
      photoW/targetW, photoH/targetH);
    
    // Decodifica o arquivo de imagem em 
    // um Bitmap que preencherá a ImageView
    bmOptions.inJustDecodeBounds = false;
    bmOptions.inSampleSize = scaleFactor;
    bmOptions.inPurgeable = true;
    
    Bitmap bitmap = BitmapFactory.decodeFile(
      caminhoFoto.getAbsolutePath(), bmOptions);
    img.setImageBitmap(bitmap);
  }
}
Quando chamamos a câmera, no startActivityForResult, passamos o valor 1 como segundo parâmetro, esse valor representa o código da requisição (requestCode). Esse valor é retornado no método onActivityResult para saber se o resultado que estamos tratando é daquela requisição. No if que fizemos, além do requestCode, checamos o resultCode. O resultCode descreve o resultado da operação, que neste caso é tirar a foto. Se o resultado for RESULT_OK, é porque uma foto foi tirada.
[EDITADO em 12/05/2013] 
A foto que é tirada com as câmeras atuais requer muita memória para ser alocada. Sendo assim, verificamos o tamanho da ImageView para carregar a imagem deste tamanho.
O atributo caminhoFoto que foi atribuído no método tirarFotoClick é utilizado para carregar a imagem e atribuir em um ImageView. Agora vamos ver como salvar essa imagem na galeria de mídia.
public void salvarFotoClick(View v) {
  if (caminhoFoto != null && caminhoFoto.exists()) {
    MediaStore.Images.Media.insertImage(
      getContentResolver(), 
      caminhoFoto.getAbsolutePath(), 
      caminhoFoto.getName(), "");

    Toast.makeText(this, 
      "Imagem adicionada a galeria.",
      Toast.LENGTH_SHORT).show();
  }
}
A classe MediaStore.Images.Media tem um "método mágico" que permite que possamos inserir um Bitmap na galeria. Essa operação é feita graças a um ContentProvider disponibilizado pela aplicação de galeria. O próximo passo será como abrir a galeria de mídia para selecionarmos uma imagem.
public void galleryButtonClick(View v) {

  Intent intent = new Intent(
    Intent.ACTION_GET_CONTENT);
  intent.setType("image/*");
  startActivityForResult(intent, 2);
}
O código acima abrirá a aplicação de galeria. Notem que agora passamos o valor 2 para o startActivityForResult. Da mesma forma que fizemos para tirar foto, usaremos o método onActivityResult para tratar a imagem selecionada.
// adicione esse código no onActivityResult
if (resultCode == RESULT_OK && requestCode == 2) {
  Uri selectedImage = data.getData();
  String[] filePathColumn = { 
    MediaStore.Images.Media.DATA };

  Cursor cursor = getContentResolver().query(
    selectedImage, filePathColumn, null, null, null);
  cursor.moveToFirst();

  int columnIndex = cursor.getColumnIndex(
    filePathColumn[0]);
  String filePath = cursor.getString(columnIndex); 
  cursor.close();

  Bitmap yourSelectedImage = 
    BitmapFactory.decodeFile(filePath);

  imageView1.setImageBitmap(yourSelectedImage);
}
A galeria retorna o endereço da imagem selecionada através de um objeto Uri que é obtido em data.getData(). Com essa informação podemos acessar o ContentProvider da galeria para obter o caminho da imagem. Com esse caminho passamos para a classe BitmapFactory que retornará um objeto Bitmap para preencher a imageView.

Qualquer dúvida, deixem seus comentários.

4br4ç05,
nglauber