Primeiras aulas do curso Android II: Integração com apps e recursos do device

Android II: Integração com apps e recursos do device

Permissões, SMS, mapa e navegador - Permissões, SMS, mapa e navegador

##Permissões, SMS, mapa e navegador

Olá, pessoal! Na primeira parte do nosso curso, criamos uma aplicação que basicamente realizava o cadastro de alunos. Fizemos uma agenda simples, mas que envolvia muitos itens do Android, principalmente a parte de banco de dados do SQLite.

Se observarmos a tela do app, temos um agenda, em que conseguíamos cadastrar alunos.

Se clicássemos sobre o nome de algum dos alunos, conseguíamos salvar informações (como site e telefone), além de poder inclusive avaliá-los.

Agora, queremos encontrar um forma de aproveitar estas informações. Por exemplo, seria interessante se pudéssemos clicar no campo em que foi preenchido um site e a partir do endereço poder acessar o navegador, para checar diretamente o site do aluno. Como poderíamos fazer isto?

Quando queríamos deletar o cadastro de um aluno, tínhamos a opção de dar um clique logo e seria aberto um menu de contexto com algumas opções específicas.

Nós poderíamos aproveitar o menu e incluir uma nova opção, como por exemplo, "visitar o site do aluno".

Voltando para o Android Studio, tínhamos o seguinte trecho de código:

Button novoAluno = (Button) findViewById(R.id.novo_aluno);
novoAluno.setOnClickListener((v) -> {
        Intent intentVaiProFormulario = new Intent(ListaAlunosActivity.this, FormularioActivity.class);
        startActivity(intentVaiProFormulario);
});

registerForContextMenu(listaAluno);

Nele, está contido o registerForContextMenu(listaAluno);, que serve justamente para informar ao Android que temos um menu de contexto. Com isto, todas as vezes em que dávamos um clique longo na nossa lista, o Android automaticamente chamava o método CreateContextMenu.

Vamos para este trecho do código:

//...

@Override
public void onCreateContextMenu(ContextMenu menu, View v, final ContextMenu.ContextMenuInfo menuInfo) {
    MenuItem deletar = menu.add("Deletar");
    deletar.setOnMenuItemClickListener(item) -> {
            AdapterView.AdapterContextMenuInfo  info = (AdapterView.AdapterContextMenuInfo) menuInfo;
            Aluno aluno = (Aluno) listaAlunos.getItemPosition(info.position);

            AlunoDAO dao = new AlunoDAO(ListaAlunoActivity.this);
            dao.deleta(aluno);
            dao.close():

            carregaLista();
            return false;


    });
}

Era aqui, em que definíamos quais itens iriam aparecer no nosso menu. Então, se queremos incluir um novo item, devemos ir até o método, e vamos chamar novamente o método menu.add - que nos passa um menu de contexto, no qual podemos adicionar os itens que queremos dentro dele. Apenas precisamos criar um comportamento para este item.

Então, para visitar o site do aluno adicionaremos menu.add e depois, o texto que será visualizado (Visitar site):


@Override
public void onCreateContextMenu(ContextMenu menu, View v, final ContextMenu.ContextMenuInfo menuInfo) {
    menu.add("Visitar site");

//...

Vamos usar o atalho do Android Studio para ele criar uma variável para mim com ALT + ENTER. Devemos escolher a opção Introduce local variable do menu que irá surgir. Em seguida, daremos um nome descritivo itemSite. Para colocarmos um comportamento nele, usaremos a ideia de listener e vamos adicionar a seguinte linha:

itemSite.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener()

Ao clicarmos ENTER, o Android Studio irá preencher automaticamente.


@Override
public void onCreateContextMenu(ContextMenu menu, View v, final ContextMenu.ContextMenuInfo menuInfo) {
    MenuItem itemSite = menu.add("Visitar site");
    itemSite.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
        @Override
        public boolean onMenuItemClick(MenuItem item) {
            return false;
        }
});

Agora, dentro deste método vamos colocar o comportamento. Nosso objetivo é sair da aplicação e entrar no navegador. Queremos ir de um activity para outro. Precisaremos criar intent e passar o contexto, além da classe do activity que queremos ir. Como em uma classe anônima não podemos usar o this, mas para o contexto usaremos o nome da classe ListaAlunoActivity.this - e assim, especificar que queremos o this da classe de fora e não da anônima. E depois, definir que queremos ir para o navegador. Para isto, vamos arriscar usar a classe Browser.class. Por enquanto, é irrelevante se esta classe de fato existe. A nova linha ficará assim:

new Intent(ListaAlunosActivity.this, Browser.class);

A linha será inserida no seguinte trecho do código:

@Override
public boolean onMenuItemClick(MenuItem item) {
    new Intent(ListaAlunosActivity.this, Browser.class);
    return false;
}

Vou criar uma variável local para Intent, com o comando ALT + ENTER e depois clicando em Introduce local variable. Em seguida, irei renomeá-la como IntentSite.

@Override
public boolean onMenuItemClick(MenuItem item) {
    Intent intentSite = new Intent(ListaAlunosActivity.this, Browser.class);
    return false;
}

Agora que temos a Intent, precisamos informar o sistema operacional. Logo, nós precisaremos chamar o método startActivity() e dentro, passaremos intentSite, que tem as informações para o Android.

@Override
public boolean onMenuItemClick(MenuItem item) {
    Intent intentSite = new Intent(ListaAlunosActivity.this, Browser.class);
    startActivity(intentSite);
    return false;
}

Se rodássemos o código, o que aconteceria é: daríamos um clique longo, que abrirá um menu de contexto. Depois, selecionaríamos a opção "Visitar o site" e automaticamente acessaríamos o site.

Nós poderíamos criar a Intent desta forma, fazendo o Listener, adicionando o new Intent e o startActivity. A maneira como fizemos anteriormente:


@Override
public void onCreateContextMenu(ContextMenu menu, View v, final ContextMenu.ContextMenuInfo menuInfo) {
    MenuItem itemSite = menu.add("Visitar site");
    itemSite.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
        @Override
        public boolean onMenuItemClick(MenuItem item) {
          Intent intentSite = new Intent(ListaAlunosActivity.this, Browser.class);
            startActivity(intentSite);
            return false;
        }
});

//...

Porém, será um trabalho repetitivo, todo item de menu terá OnMenuItemClickListener e vamos adicionar um comportamento que basicamente é um Intent e um startActivity. Para não repetirmos sempre este código, o Android disponibilizou um atalho.

Então, vamos recortar a seguinte linha e colocá-la mais acima:

Intent intentSite = new Intent(ListaAlunosActivity.this, Browser.class);

Depois, vamos retirar o Listener.

@Override
public boolean onMenuItemClick(MenuItem item) {
  Intent intentSite = new Intent(ListaAlunosActivity.this, Browser.class);
    startActivity(intentSite);
    return false;
}

Esse será o nosso código com as alterações:

@Override
public void onCreateContextMenu(ContextMenu menu, View v, final ContextMenu.ContextMenuInfo menuInfo) {
    MenuItem itemSite = menu.add("Visitar site");
    itemSite.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
    Intent intentSite = new Intent(ListaAlunosActivity.this, Browser.class);
   }
});

Já temos o Item e o Intent, agora, precisamos associar os dois. Faremos isto informando que o itemSite tem uma Intent associada com o método setIntent. Dentro, especificaremos que é a intentSite que está associada.

itemSite.setIntent(intentSite);

Com isto, alcançaremos o mesmo efeito do que fizemos antes. Quando clicarmos em cima do item, automaticamente ele dará um startActivity na Intent criada. Em teoria, ele já abrirá o site do aluno no navegador.

Agora, faltou definir alguns detalhes sobre a Browser.class. No celular, podemos ter diferentes navegadores instalados - uma das grandes vantagens do android é a opção de personalização. Se você não gosta do Browser que vem instalado no aparelho, tem opção de baixar outro e ele funcionará juntamente com o instalado inicialmente.

Imagine que já temos o nosso aplicativo, o site está rodando e queremos acessar a página cadastrada do aluno. Se temos mais de um navegador instalado, o que seria interessante acontecer neste momento? Ao clicar o endereço do site, o app poderia oferecer as opções de navegadores para o usuário selecionar.

Então, em vez de definirmos no código qual Browser queremos abrir, nós vamos dizer para o Android que queremos abrir o site. O responsável por escolher qual será o aplicativo que abrirá o site, será o usuário. Uma janela choose dialog será aberta e ele poderá escolher entre as opções de navegadores.

Para diferenciarmos, a Intent que explicita qual é a activity que seremos direcionados, chamamos de Intent Explícita.

Intent intentSite = new Intent(ListaAlunosActivity.this, Browser.class);

Quando dizemos que o usuários irá escolher e nós só sabemos qual ação queremos realizar, nós iremos utilizar uma Intent implícita. Você informa para o Android 'quero abrir um site' e ele fará o possível para executar a ação.

Vamos ver como fazemos isto no código. Se observarmos a linha da Intent, em vez de passarmos os dois parâmetros, definiremos a ação (action) de visualizar algo.

Intent intentSite = new Intent(Intent.ACTION_VIEW);

Mas se deixássemos apenas assim, o Android iria ficar se perguntando "visualizar o quê?"

A ação de visualizar pode ser usada para diversas coisas: com imagens, sites, ou outro recurso. Precisamos especificá-lo. E o que podemos usar para descrever qual site queremos ir? O endereço, a url.

Vamos na Intent, depois usaremos um método para dados obrigatórios, setData, que serve para passar um parâmetro.

intentSite.setData();

Este irá solicitar uma uri (Identificador uniforme de recursos), uma forma de identificar qualquer tipo de recurso no sistema operacional. Uma uri bastante conhecida é a url.

Poderíamos adicionar por exemplo o endereço http://www.google.com. Porém, se fizermos isto, o endereço ficará com um sublinhado vermelho, porque o que ele espera é um objeto do tipo uri e não, uma String. Sem problemas. Temos uma forma de gerar uma uri a partir de uma String : usando a classe Uri (com apenas a letra U maiúscula) juntamente com o método parse.

intentSite.setData(Uri.parse("http://www.google.com"));

Vou fechar a classe com ALT + ENTER. Vamos ver como ficou nosso código:


@Override
public void onCreateContextMenu(ContextMenu menu, View v, final ContextMenu.ContextMenuInfo menuInfo) {
    MenuItem itemSite = menu.add("Visitar site");
    Intent intentSite = new Intent(Intent.ACTION_VIEW);
    intentSite.setData(Uri.parse("http://www.google.com"));
    ItemSite.setIntent(itentSite);
});

Iremos testar o nosso app no emulador e vamos rodar a agenda. Após o aplicativo ser aberto, vamos dar um clique logo sobre a opção Daniel. Automaticamente, ele irá abrir um menu de contexto.

Depois, clicamos sobre Visitar site e ele irá abrir o navegador.

Ele abriu corretamente o site do Google. No entanto, ainda não alcançamos nosso objetivo. Nosso objetivo é clicarmos no site específico que está cadastrado no perfil do aluno. Se clicarmos sobre o nome do Jeferson, no app, veremos que está cadastrado o endereço www.alura.com.br.

Iremos alterar o nosso código. Em vez de colocarmos a string com o endereço, queremos adicionar o site do aluno aluno.getSite(). Porém, ainda não temos o aluno neste ponto.

Quando estamos fazendo o ClickListener, nós nos associávamos ao aluno no seguinte trecho:

//...

 MenuItem deletar = menu.add("Deletar");
 deletar.setOnMenuItemClickListener(new MenuItem.OnMenuItem.OnMenuItemClickListener() {
   @Override
   public boolean onMenuItemClick(MenuItem item) {
       AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
       Aluno aluno = (Aluno) listaAlunos.getItemAtPosition(info.position);
   }
});

Vamos mover duas linhas do Listener.

AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
Aluno aluno = (Aluno) listaAlunos.getItemAtPosition(info.position);

Para o CreateContextMenu...

@Override
public void onCreateContextMenu(ContextMenu menu, View v, final ContextMenu.ContextMenuInfo menuInfo) {
    AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
    Aluno aluno = (Aluno) listaAlunos.getItemAtPosition(info.position);
});

Atenção, que quando temos a referência de uma variável, dentro de uma classe anônima, e ela declarada fora, precisamos declarar esta variável como constante. Assim evitamos confusão no Java, ao alterarmos uma classe e correr o risco de alterar a outra.

Por isso, a exigência de que as variáveis que acessamos em uma classe anônima, sejam constantes no lugar onde foram declaradas. Para isto, vamos adicionar final antes de Aluno.

final Aluno aluno = (Aluno) listaAlunos.getItemAtPosition(info.position);

Nós já declaramos o aluno, veremos como ficou o nosso código até aqui:

@Override
public void onCreateContextMenu(ContextMenu menu, View v, final ContextMenu.ContextMenuInfo menuInfo) {
    AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
    final Aluno aluno = (Aluno) listaAlunos.getItemAtPosition(info.position);

    MenuItem itemSite = menu.add("Visitar site");
    Intent intentSite = new Intent(Intent.ACTION_VIEW);
    intentSite.setData(Uri.parse(aluno.getSite()));
    itemSite.setIntent(intentSite);

    MenuItem deletar = menu.add("Deletar");
    deletar.setOnMenuItemClickListener(new MenuItem.OnMenuItem.OnMenuItemClickListener() {
      @Override
      public boolean onMenuItemClick(MenuItem item) {
          AlunoDAO dao = new AlunoDAO(ListaAlunosActivity.this);
          dao.deleta(aluno);
          dao.close();

          carregaLista();
          return false;

        }
    });

}

No entanto, se voltarmos no emulador, veremos que os endereços estão cadastrados apenas com www no inicio. Isto significa, que está faltando https://, ou seja, o protocolo - justamente o que define para o Android se você está acessando uma imagem ou um site. Então, se chamamos www.caelum.com.br, o Android irá se perder. Para evitarmos este problema e para não necessitarmos que o usuário adicione o https://, vamos fazer um tratamento antes de colocarmos na Intent. Criaremos a variável site, em que vamos puxar o site do aluno, com aluno.getSite(). Com o método starsWith, iremos verificar se o endereço começa com https://. Se (if) ele não começar, teremos que concatenar o endereço. Logo, site será igual a https:// + o que o site tinha antes.

Em vez de pegarmos o site direto de aluno.getSite(), vamos chamá-lo de site.

MenuItem itemSite = menu.add("Visitar site");
Intent intentSite = new Intent(Intent.ACTION_VIEW);

String site = aluno.getSite();
if (!site.startsWith("https://")) {
     site = "https://" + site;
}  

intentSite.setData(Uri.parse(site));
itemSite.setIntent(intentSite);

Vamos rodar o aplicativo para ver o que acontece. Com as atualizações, se clicarmos novamente no endereço cadastrado no perfil de Jeferson, (www.alura.com.br), ele irá abrir a página.

Se testarmos com outros alunos, veremos que somos direcionados corretamente para o endereços cadastrados.

Então, até aqui aprendemos como criar uma Intent Implícita, que é quando queremos trocar de tela na nossa aplicação e queremos aproveitar algo que já está pronto no celular e não ter que criar um Browser do zero, mas não sabemos qual classe irá tratar desta ação. Então, deixamos para o Android a tarefa e ele irá descobrir qual activity tem disponível para realizá-la. Nós instanciavámos a Intent e especificávamos qual ação queríamos realizar.

Permissões, SMS, mapa e navegador - Trabalhando com outras informações da agenda

Nós já conseguimos colocar a funcionalidade de dar um clique longo sobre o nome do aluno e ter a opção de visitar o site cadastrado no perfil. Conseguimos aproveitar uma informação que tínhamos disponível na agenda. Mas se observarmos os perfis novamente, veremos que existem duas informações não aproveitadas: Endereço e Telefone. Vamos aproveitá-las agora.

Ao darmos um clique longo sobre o nome do aluno, queremos que apareça um menu com a opção de enviarmos um SMS para ao aluno. Também queremos uma opção, que a partir do endereço, nos indique a localização do aluno.

Voltando ao código, no menu de contexto, vamos incluir mais dois itens. Iremos usar a mesma ideia da Intent implícita, para aproveitar os apps já instalados no nosso celular e o usuário possa fazer a escolha mais interessante.

Antes do "Visitar site", vou incluir um novo menu.add("Enviar SMS"). Vou dar um ALT + ENTER para que ele se torne o itemSMS. Depois, criaremos nossa Intent que nos fará mudar de tela. Como é uma Intent implícita, diremos para o Android qual ação executar. Neste caso, nós também queremos visualizar e usaremos a ACTION_VIEW. Queremos que ele abra uma aplicação que visualize SMS, e nos permita digitar, corrigir os dados, além de enviar a mensagem.

MenuItem itemSMS = menu.add("Enviar SMS");
Intent intentSMS = new Intent(Intent.ACTION_VIEW) ;

Após criarmos a intentSMS, faltará associar as duas linhas:

itemSMS.setIntent(intentSMS);

Elas estão associadas, mas agora, precisamos especificar o que queremos visualizar. Precisamos passar a uri e informar qual recurso queremos utilizar. Será o mesmo processo que fizemos com o site: vamos adicionar intentSMS, depois o método setData, e dentro, iremos usar a uri. Lembrando que não podemos usar diretamente uma string, temos que acrescentar a classe Uri.parse e em seguida a URI.

Precisaremos conhecer a documentação do Android, para descobrir qual o protocolo utilizado para reconhecer o SMS. Neste caso, quando falamos de SMS, usamos o sms:. Logo adiante, podemos adicionar um número de telefone, por exemplo.

intentSMS.setData(Uri.parse("sms:12345678"));

O trecho do código correspondente ficará assim:

MenuItem itemSMS = menu.add("Enviar SMS");
Intent intentSMS = new Intent(Intent.ACTION_VIEW) ;
intentSMS.setData(Uri.parse("sms:12345678"));
itemSMS.setIntent(intentSMS);

Após adicionarmos as novas linhas, podemos fazer um teste e clicar no nome do aluno, selecionaremos a opção de enviar SMS. O app de mensagem já estará preenchido com o número cadastrado. Nós iremos concatenar no código o número do aluno.

intentSMS.setData(Uri.parse("sms:" + aluno.getTelefone()));

O código ficou assim:

MenuItem itemSMS = menu.add("Enviar SMS");
Intent intentSMS = new Intent(Intent.ACTION_VIEW) ;
intentSMS.setData(Uri.parse("sms:" + aluno.getTelefone()));
itemSMS.setIntent(intentSMS);

Vamos aproveitar e adicionar o segundo item, que seria o de visualizar no mapa. Faremos o mesmo procedimento: incluímos o menuadd e o nome que será exibido no menu Visualizar no mapa. Depois de atribuir a variável, adicionaremos o itemMapa.

MenuItem itemMapa = menu.add("Visualizar no mapa");

Criaremos outra Intent implícita e usaremos o ACTION_VIEW novamente. Depois, adicionamos intentMapa e associamos com intentMapa.

MenuItem itemMapa = menu.add("Visualizar no mapa");
Intent intentMapa = new Intent(Intent.ACTION_VIEW);
itemMapa.setIntent(intentMapa);

Agora, precisamos especificar neste item o que queremos visualizar. Vamos adicionar a classe Uri.parse e descobrir qual protocolo será utilizado. Na documentação, iremos descobrir que é o geo:. Como iremos trabalhar com coordenadas geográficas. Vamos ter que especificar a latitude e longitude da posição que queremos mostrar no mapa.

Por exemplo, 0,0. Ele irá abrir o GoogleMaps e irá indicar com um pininho a posição específica do aluno. Após o geo: deve aparecer as coordenadas daquele endereço do aluno selecionado no menu de contexto. Como é feita conversão do endereço? Se fôssemos fazer isto para o mundo inteiro, deveríamos ter uma tabela gigantesca. Seria um banco de dados enorme, que geralmente não fica armazenado no celular. Nós podemos consultar o Google Maps sobre a coordenada do endereço e ele irá devolver a geolocalização. Para isto, iremos usar geo:0,0?q= e depois vamos concatenar com aluno.getEndereco().

intentMapa.setData(Uri.parse("geo:0,0?q=" + aluno.getEndereco()));

Assim, ao clicarmos sobre o nome do aluno, nós iremos enviar o endereço para o Google, ele irá retornar automaticamente a coordenada e ele abrirá o Google Maps com a coordenada específica.

Vale lembrar, que quando quisermos que o app execute uma tarefa que outro aplicativo no nosso celular já faz, podemos usar uma Intent implícita. A única diferença será o protocolo usado para representar o recurso solicitado.

Vamos rodar o app e ver se os novos itens funcionam bem. Quando o aplicativo estiver aberto, daremos um clique longo sobre um dos nomes e aparecerá um menu com as novas opções.

Selecionaremos "Enviar SMS" e veremos será aberto o app de mensagem.

De volta a nossa aplicação, vamos testar a opção "Visualizar no mapa". O app do Google Maps será aberto com sucesso, já com o pininho característico sobre a localização.

Se observarmos o código das duas funcionalidades, perceberemos a semelhança. A única diferença foi o protocolo.

MenuItem itemSMS = menu.add("Enviar SMS");
Intent intentSMS = new Intent(Intent.ACTION_VIEW) ;
intentSMS.setData(Uri.parse("sms:" + aluno.getTelefone()));
itemSMS.setIntent(intentSMS);

MenuItem itemMapa = menu.add("Visualizar no mapa");
Intent intentMapa = new Intent(Intent.ACTION_VIEW);
intentMapa.setData(Uri.parse("geo:0,0?q=" + aluno.getEndereco()));
itemMapa.setIntent(intentMapa);

Podemos criar outros recursos com o nosso código, basta pesquisarmos um pouco na documentação e analisarmos quais aplicativos temos no celular. Às vezes, temos disponível bibliotecas externas. Temos muitas opções para incrementar nossas aplicações.

Permissões, SMS, mapa e navegador - Permissões no Android 6

Nós já conseguimos instalar outras funcionalidades no aplicativo: envio de SMS, visualização de endereço no mapa e acesso ao site cadastrado.

Agora, queremos utilizar novamente o telefone do aluno, mas dessa vez, para fazermos uma ligação direto do menu.

Voltando ao trecho de menu de contexto, vamos adicionar no código um novo item, com menu.add. Vamos adicionar Ligar, depois, criaremos uma variável, que chamaremos itemLigar. Em seguida, criaremos um comportamento, faremos isto usando uma Intent implícita. Mas a action que criaremos agora, será específica. ACTION_CALL. Então, o usuário poderá escolher aplicativos como Skype e telefone.

MenuItem itemLigar = menu.add("Ligar");
new Intent(Intent.ACTION_CALL);

Se vamos ligar, precisamos informar o número do telefone. Para isto, utilizaremos o método setData para informar para a Intent qual o parâmetro da nossa activity. Usaremos o método Uri.parse e o protocolo. No nosso caso, adicionaremos tel:. Logo depois, iremos concatenar o número de telefone.

intentLigar.setData(Uri.parse("tel:" + aluno.getTelefone()));

Iremos agora, associar o itemLigar com a intentLigar. O trecho do nosso código ficou assim:

MenuItem itemLigar = menu.add("Ligar");
new Intent(Intent.ACTION_CALL);
itemLigar.setIntent(intentLigar);

Vamos a funcionalidade no nosso app. Após abrirmos o menu e clicarmos sobre o nome de um aluno, veremos a opção "Ligar".

Porém, teremos um problema...

Isto está acontecendo, porque até o Android 5, teríamos que fazer exatamente o que foi feito aqui. E nossa aplicação funcionaria normalmente. Com a chegada do Android 6, a parte de permissões do sistema operacional foi alterada.

Usaremos o Facebook como exemplo. Ao instalarmos o app, ele informa várias permissões necessárias, mas não diz quando cada uma será utilizada. No modelo Android 6, a aplicação começa sem nenhuma permissão e à medida, que o app precisa acessar algum recurso, como o GPS, é o momento em que a permissão se torna necessária e o usuário precisa aceitá-la ou não.

Vamos ver o que muda no código para conseguirmos fazer a ligação: teremos que incluir o pedido de permissão para o usuário.

Antes, criando uma Intent e associando com um item, nós não conseguimos colocar comportamentos extras. Para adicionar novos comportamentos precisaremos, teremos que fazer como no Deletar, em que invés de criar uma Intent, usávamos o Listener para o MenuItem. Então, quando sobre ele, tínhamos o método onMenuItemClick e adicionávamos código Java, como no seguinte trecho:

MenuItem deletar = menu.add("Deletar");
deletar.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
    @Override
    public boolean onMenuItemClick(MenuItem item) {
        AlunoDAO dao = new AlunoDAO(ListaAlunoActivity.this);
        dao.deleta(aluno;
        dao.close();

        carregaLista();
        return false;
      }
 });

Fazíamos tudo dentro do método. Iremos fazer o mesmo com o itemLigar. Vamos adicionar o setOnMenuItemClickListener, seguido por uma classe anônima.

MenuItem itemLigar = menu.add("Ligar");
itemLigar.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
    @Override
    public boolean onMenuItemClick(MenuItem item) {
        return false;
    }
})

Já temos o template do método. Vamos recortar duas linhas do código, em que estava a nossa action.

Intent intentLigar = new Intent(Intent.ACTION_CALL);
IntentLigar.setData(uri.parse("tel:" + aluno.getTelefone()));

E depois, movê-las para o método.

MenuItem itemLigar = menu.add("Ligar");
itemLigar.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
    @Override
    public boolean onMenuItemClick(MenuItem item) {
      Intent intentLigar = new Intent(Intent.ACTION_CALL);
      IntentLigar.setData(Uri.parse("tel:" + aluno.getTelefone()));

      return false;
    }
})

Iremos apagar a setIntent, porque não estamos mais associando Intent.

itemLigar.setIntent(IntentLigar);

Vamos instanciar a Intent e depois, chamamos o startActivity, usando como parâmetro a Intent implícita (intentLigar).

MenuItem itemLigar = menu.add("Ligar");
itemLigar.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
    @Override
    public boolean onMenuItemClick(MenuItem item) {
      Intent intentLigar = new Intent(Intent.ACTION_CALL);
      IntentLigar.setData(Uri.parse("tel:" + aluno.getTelefone()));
      startActivity(intentLigar);

      return false;
    }
});

Para essa parte do projeto funcionar, você precisa ter certeza de que o Compile Sdk Version está em API 23 Android 6.0 (Marshmallow) Você pode verificar isso no próprio Android Studio em File > Project Structure, selecione seu projeto em Modules na coluna da esquerda e vá na aba Properties

Você também precisa estar com o Target Sdk Version em API 23 Android 6.0 (Marshmallow) Confira sua versão em File > Project Structure, selecione seu projeto em Modules na coluna da esquerda e vá na aba Flavors

Ainda, verifique se no arquivo build.gradle a dependência está na versão correta:

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:23.0.1'
testCompile 'junit:junit:4.12'
}

Mas a linha do startActivity ficará sublinhado em vermelho, avisando que a Intent implícita irá precisar de uma checagem de permissão.

Se não tivéssemos já adicionado a permissão no manifest...

//...
<manifest
xmls:android="http:schemas.android.com/apk/res/android"
    package="br.com.alura.agenda" >

    <uses-permission android:name="android.permission.CALL_PHONE"/>

O Android Studio iria perdir que fizéssemos. Com um ALT + ENTER, ele irá adicionar o código necessário para fazer a checagem de permissão. É bom conhecermos isto, mas nós iremos criar o código.

Primeiramente, iremos verificar se já temos fazer a checagem de permissão e vamos gerar um if. Iremos ver se já temos a permissão com a classe ActivityCompat em que temos o método CheckSelfPermission. Caso exista a permissão do usuário, já podemos usá-la. Adicionaremos também os parâmetros: o contexto ( ListaAlunosActivity.this) e a permissão que será checada ( Manifest.permission.CALL_PHONE). Com a classe PackageManager e a constante PERMISSION_GRANTED iremos conferir se a permissão já existe.

Nossa condição ficou assim:

@Override
public boolean onMenuItemClick(MenuItem item) {
    if (ActivityCompat.checkSelfPermission(ListaAlunosActivity.this, Manifest.permission.CALL_PHONE)
          != PackageManager.PERMISSION_GRANTED) {

          }
 \...

Se ainda não foi dada a permissão, teremos que pedi-la. Faremos isto com ActivityCompat com o método requestPermission. Os parâmetros iremos preencher com ListaAlunosActivity e iremos instanciar um array de strings, porque poderá ser mais de uma.

\...
itemLigar.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
    @Override
    public boolean onMenuClick(MenuItem item) {
        if (ActivityCompat.checkSelfPermission(ListaAlunosActivity.this, Manifest.permission.CALL_PHONE)   
                != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions();

        }

        Intent intentLigar = new Intent(Intent.ACTION_CALL);
        intentLigar.setData(Uri.parse("tel:" + aluno.getTelefone()));

        startActivity(intentLigar);

        return false;

      }
  });

Dentro da String, quando formos especificar, iremos usar novamente a classe manifest. O último parâmetro que ele irá pedir é o chamado request code. Por enquanto, vamos preencher com 123.

ActivityCompat.requestPermissions(ListaAlunosActivity.this, new String[]{Manisfest.permission.CALL_PHONE}, 123);

Se não tivermos a permissão, pediremos para o usuário.

Caso a condição não for verdadeira, cairemos no else, porque já temos a permissão. Então, basta realizar a ação que queríamos: iniciar a chamada telefônica.

} else {
  Intent intentLigar = new Intent(Intent.ACTION_CALL);
  intentLigar.setData(Uri.parse("tel:" + aluno.getTelefone()));
  startActivity(intentLigar);

return false;
}

Vamos verificar se o nosso código já resolve o problema. Rodaremos a aplicação no emulador. Ao clicarmos sobre "Daniel" e selecionarmos a opção "Ligar", irá surgir uma janela de permissão perguntando "você permite que a Agenda faça e gerencie chamadas telefônica".

A mensagem irá aparecer de acordo com o idioma que estiver configurado no seu celular. No emulador, apareceram as duas opções: permitir e negar. Neste caso, nós iremos permitir ("allow").

Agora, a ação de ligar poderá ser realizada. Se clicarmos novamente sobre "Daniel" e em "Ligar", poderemos fazer a ligação.

Ele abriu o activity para ligação. Aparentemente, está tudo funcionando corretamente.

Vamos conversar sobre o request code. Toda vez que precisarmos de uma permissão no Android, teremos que pedir para o usuário com o request permission. Logo após a permissão, podemos querer abrir um popup ou outra ação. Para fazer isto, o Android irá chamar o método onRequestPermissionResult, independentemente se foi dada ou não a permissão. Dentro, iremos informar todas as permissões que foram solicitadas e os resultados. Passaremos dois arrays, mas o importante é que ele irá cair dentro e poderemos inserir o código que quisermos.

@Override
public void onRequestPermissionResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantTesults) {
  super.onRequestPermissionResult(requestCode, permissions, grantResults);

}

Vamos imaginar que queremos incluir dentro // faz a ligacao.

@Override
public void onRequestPermissionResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
  super.onRequestPermissionResult(requestCode, permissions, grantResults);

  // faz a ligacao
}

Agora, imagine a situação em que seja necessário pedir permissão para o envio de SMS. No trecho referente, precisaríamos adicionar também um Listener para pedir a permissão e então, faríamos o request permission. Porém, este chamaria o onRequestPermissionResult, que no nosso caso, fará uma ligação. E não é o que queríamos...

Precisamos criar uma forma de distinguir qual a permissão que está voltando, quem fez o pedido, e o que queremos fazer de fato. É onde entra o request code. Na ligação, o parâmetro 123 é o request code.

ActivityCompat.requestPermissions(ListaAlunosActivity.this, new String[]{Manisfest.permission.CALL_PHONE}, 123);

Para termos certeza que a ligação é a ação desejada, podemos incluir um requestCode = 123.

@Override
public void onRequestPermissionResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantTesults) {
  super.onRequestPermissionResult(requestCode, permissions, grantResults);

  if (requestCode == 123) {
    // faz a ligacao
  }
}

Se o código do request do SMS fosse 124, poderíamos inserir else if que // faz o envio do SMS.

if (requestCode == 123) {
  // faz a ligacao
} else if (requestCode == 124) {
    // faz o envio do SMS

  }

Nós não vamos utilizar este método, mas é interessante conhecê-lo. Iremos retirar as linhas de acima do nosso código. E nosso app faz as chamadas telefônicas.

Além de criarmos as janelas de aplicação, o Android 6 permite ao usuário mexer nas permissões das aplicações. Então, se abrimos o menu das apps e encontramos o nosso. Veremos a seção de Permissions.

Ao clicarmos na seção, encontraremos todas as permissões que incluímos no manifest. E o usuário tem a opção de ativá-las ou desativá-las.

Tem o mesmo efeito de permitir ou não diretamente na aplicação. Por exemplo, vamos desativar a permissão no menu de "Permissions" da agenda. Com isto, quando tentamos fazer uma nova ligação na Agenda, será solicitado novamente a permissão do usuário. Caso seja negada, não será feita a chamada.

Precisamos estar preparados para este tipo de necessidade do Android 6. Nas versões anteriores, incluímos as permissões no manifest e estará resolvido.

Quando incluirmos recursos de segurança ou que gere custos para o usuário, o Android também irá pedir permissões.

Sobre o curso Android II: Integração com apps e recursos do device

O curso Android II: Integração com apps e recursos do device possui 283 minutos de vídeos, em um total de 49 atividades. Gostou? Conheça nossos outros cursos de Android em Mobile, ou leia nossos artigos de Mobile.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda Android acessando integralmente esse e outros cursos, comece hoje!

  • 1241 cursos

    Cursos de programação, UX, agilidade, data science, transformação digital, mobile, front-end, marketing e infra.

  • Certificado de participação

    Certificado de que assistiu o curso e finalizou as atividades

  • App para Android e iPhone/iPad

    Estude até mesmo offline através das nossas apps Android e iOS em smartphones e tablets

  • Projeto avaliado pelos instrutores

    Projeto práticos para entrega e avaliação dos professores da Alura com certificado de aprovação diferenciado

  • Acesso à Alura Start

    Cursos de introdução a tecnologia através de games, apps e ciência

  • Acesso à Alura Língua

    Reforço online de inglês e espanhol para aprimorar seu conhecimento

Premium

  • 1241 cursos

    Cursos de programação, UX, agilidade, data science, transformação digital, mobile, front-end, marketing e infra.

  • Certificado de participação

    Certificado de que assistiu o curso e finalizou as atividades

  • App para Android e iPhone/iPad

    Estude até mesmo offline através das nossas apps Android e iOS em smartphones e tablets

  • Projeto avaliado pelos instrutores

    Projeto práticos para entrega e avaliação dos professores da Alura com certificado de aprovação diferenciado

  • Acesso à Alura Start

    Cursos de introdução a tecnologia através de games, apps e ciência

  • Acesso à Alura Língua

    Reforço online de inglês e espanhol para aprimorar seu conhecimento

12X
R$75
à vista R$900
Matricule-se

Premium Plus

  • 1241 cursos

    Cursos de programação, UX, agilidade, data science, transformação digital, mobile, front-end, marketing e infra.

  • Certificado de participação

    Certificado de que assistiu o curso e finalizou as atividades

  • App para Android e iPhone/iPad

    Estude até mesmo offline através das nossas apps Android e iOS em smartphones e tablets

  • Projeto avaliado pelos instrutores

    Projeto práticos para entrega e avaliação dos professores da Alura com certificado de aprovação diferenciado

  • Acesso à Alura Start

    Cursos de introdução a tecnologia através de games, apps e ciência

  • Acesso à Alura Língua

    Reforço online de inglês e espanhol para aprimorar seu conhecimento

12X
R$100
à vista R$1.200
Matricule-se

Max

  • 1241 cursos

    Cursos de programação, UX, agilidade, data science, transformação digital, mobile, front-end, marketing e infra.

  • Certificado de participação

    Certificado de que assistiu o curso e finalizou as atividades

  • App para Android e iPhone/iPad

    Estude até mesmo offline através das nossas apps Android e iOS em smartphones e tablets

  • Projeto avaliado pelos instrutores

    Projeto práticos para entrega e avaliação dos professores da Alura com certificado de aprovação diferenciado

  • Acesso à Alura Start

    Cursos de introdução a tecnologia através de games, apps e ciência

  • Acesso à Alura Língua

    Reforço online de inglês e espanhol para aprimorar seu conhecimento

12X
R$120
à vista R$1.440
Matricule-se
Procurando planos para empresas?

Acesso por 1 ano

Estude 24h/dia onde e quando quiser

Novos cursos todas as semanas