Boas-vindas a mais um conteúdo da Alura! Meu nome é Junior Martins e serei seu instrutor.
Audiodescrição: Junior se autodeclara como um homem de pele clara, cabelos longos e escuros que estão presos. Ele usa um óculos de armação preta, e veste uma camiseta preta. Ao fundo, há uma iluminação em degradê que vai do azul para o rosa.
Boas-vindas a mais um conteúdo que explicará como adquirir as habilidades necessárias para criar aplicativos como o "Confere Aí", que será utilizado durante o curso. Esse aplicativo usará algumas bibliotecas do Google para fornecer experiências mais ricas para as pessoas usuárias.
Por exemplo, ao apontar a câmera para um código, o aplicativo poderá extrair as informações desse QR Code. Analisaremos bibliotecas que não apenas utilizam QR Codes, mas também vários outros tipos de código, permitindo que essas informações sejam transformadas em ações dentro do aplicativo.
Ao escanear um código com outra biblioteca, somos direcionados para uma tela de detalhes com uma ação específica. Essa ação pode abrir um site, exibir um mapa ou agendar algo no aplicativo de agenda do dispositivo.
Esse conhecimento é fundamental para criar aplicativos que guiam as pessoas usuárias por meio de informações obtidas apenas ao apontar a câmera. Esse conteúdo é identificado por um processo que roda nativamente no Android, sem necessidade de conexão com servidores, garantindo uma maior privacidade.
Esse conhecimento é útil para quem deseja criar aplicativos que oferecem uma experiência mais rica para a pessoa usuária, mantendo a privacidade. Isso é possível utilizando apenas o Android e algumas bibliotecas de Machine Learning ("Aprendizado de Máquina") fornecidas pela Google.
Para prosseguir com esse conteúdo, é importante ter alguns pré-requisitos. Primeiro, é necessário saber utilizar o Jetpack Compose, especialmente em relação ao gerenciamento de estados e viewmodel. Além disso, é fundamental ter conhecimento sobre as bibliotecas de câmera do Android.
Recomendamos especialmente que tenha concluído o curso anterior sobre detecção de objetos, onde explicamos melhor o funcionamento dessa biblioteca e sua integração com o Jetpack Compose.
Vamos lá!
Temos o seguinte contexto: nosso projeto é um aplicativo pensado para ser utilizado em eventos em grandes espaços.
A ideia desse modelo específico é que o Confere Aí possa ser apontado para vários códigos espalhados pelo evento físico e, a cada novo código que o aplicativo identificar, ele será capaz de extrair algumas informações e fornecer ações dentro do app.
Por exemplo, ao entrar no evento e apontar o app para um QR Code, a pessoa pode se conectar ao Wi-Fi, visualizando a senha e o nome da rede. Se apontar para outro tipo de código, pode ver a localização de um lugar específico, agendar uma palestra na agenda do celular e realizar outras ações que acompanharemos no passo a passo.
Esse projeto já tem grande parte de sua implementação concluída, incluindo o design e a estrutura. Ele já está em funcionamento, e agora vamos explorá-lo mais a fundo. O código que recebemos, ao qual também terá acesso, segue uma estrutura muito semelhante à de outros projetos usados durante esta formação.
Temos algumas pastas que organizam diferentes partes do projeto. Por exemplo, a pasta de modelos, que chamamos de emblemas (Emblems.kt
), será explorada com mais detalhes. Há também a pasta de di
, que lida com a injeção de dependências, e outras pastas que incluem extensions
.
Dentro da pasta de ui
está o core do nosso app, que contém os componentes visuais, formas customizadas e todas as screens.
ui
components
screens
camera
CameraAnalyzer.kt
CameraContainer.kt
CameraScreen.kt
CameraScreenUiState.kt
CameraViewModel.kt
detail
googleScan
home
Na pasta "screens
", destacamos a pasta de camera
. Nela, encontramos o CameraAnalyzer
e o CameraScreen
, que contêm a maior parte do código com o qual iremos trabalhar. Nessa seção, construímos a câmera, capturamos o feed para realizar análises e incluímos partes do ViewModel
e do UIStage
.
Temos também algumas outras telas, que são as telas de detalhe (detail
), uma tela específica para uma análise da câmera do Google (DetailCodeScreen.kt
), que nós vamos abordar um pouco mais no futuro.
Também temos a pasta home
, que inclui o HomeNavHost
. Este componente gerencia toda a navegação do projeto e será uma parte importante do nosso trabalho.
utils
ActionHandler.kt
HandleBarcode.kt
PermissionUtils.kt
Além disso, há a pasta utils
, que contém funções auxiliares que utilizaremos ao longo do projeto. Por exemplo, o arquivo de permissões (PermissionUtils
) solicita autorização para usar a câmera quando necessário. Também temos a MainActivity
, que inicia o aplicativo.
Para começar a trabalhar na exibição das informações no app, utilizaremos uma biblioteca.
Para instalar essa biblioteca, utilizaremos um processo diferente do habitual, pois ela oferece uma instalação mais simples e dinâmica. Ao contrário de outras bibliotecas que baixam todos os arquivos diretamente para o dispositivo, esta apenas indica que o recurso precisa ser baixado. Quando formos utilizar esse recurso, ele será baixado diretamente do Google Play Services.
Para isso, descemos um pouco na documentação até a seção "Para usar o modelo no Google Play Services" e copiamos a seção que contém o código implementation com.google.android
. No momento, a versão disponível é a 18.3.0
.
Trecho copiado e parcialmente transcrito:
implementation 'com.google.android.gms:play-services-mlkit-barcode-scanning:18.3.1'
Copiamos a dependência e retornamos ao Android Studio para instalar a biblioteca. No canto esquerdo do projeto, clicamos em build.gradle.kts
. Em seguida, deslizamos até o final do arquivo e colamos a dependência.
O código pode apresentar alguns erros. Para corrigi-los, trocamos as aspas simples copiadas por aspas duplas, tanto no início quanto no final. Adicionamos um parêntese antes e depois das aspas duplas.
build.gradle.kts
// código omitido
implementation ("com.google.android.gms:play-services-mlkit-barcode-scanning:18.3.1")
}
Após adicionar o código ao projeto, utilizamos o atalho "Alt + Enter" e selecionamos a opção "Replace with New Library Catalog declaration for play-services-mlkit-barcode-scanning".
build.gradle.kts
// código omitido
implementation (libs.play.services.mlkit.barcode.scanning)
}
Em seguida, clicamos em "Sync Now" no canto superior direito e aguardamos o projeto baixar a dependência para podermos utilizá-la.
Após isso, é necessário fazer uma configuração adicional para essa biblioteca desagrupada. Enquanto o download está em andamento, retornamos à documentação e copiamos o trecho do passo 3. Se optarmos por usar o modelo do Google Play Services, precisamos adicionar o trecho que começa com meta-data
e vai até android:value="barcode"
.
Linhas copiadas (trecho da documentação parcialmente transcrito):
<meta-data
android:name="com.google.mlkit.vision.DEPENDENCIES"
android:value="barcode" >
Copiamos essas duas linhas da documentação e retornamos ao projeto no Android Studio, que já terminou de baixar a dependência. Localizamos no canto superior esquerdo a seção para listar os arquivos e, dentro da pasta manifests
, abrimos o arquivo AndroidManifest.xml
.
No final desse arquivo, ainda dentro da tag application
e especificamente na linha 45, pulamos algumas linhas e colamos o código copiado da documentação.
Esse código informa ao app que há uma dependência a ser baixada em um momento específico, e o próprio Android será responsável por esse download. Precisamos apenas ajustar a parte final, que atualmente tem apenas o sinal de fechamento da tag.
Devemos adicionar uma barra para completar o fechamento corretamente.
AndroidManifest
<!-- código omitido -->
<meta-data
android:name="com.google.mlkit.vision.DEPENDENCIES"
android:value="barcode" />
</application>
</manifest>
Com essa correção, o erro é resolvido e o projeto começa a ser configurado para utilizar a biblioteca.
Para entender melhor o que é essa biblioteca, podemos consultar a documentação. No canto esquerdo da página, há um botão que exibe a navegação geral. Ao clicar nele, veremos uma seção chamada "Visão geral" além da seção de "Android". Clicamos em "Visão Geral".
Essa biblioteca é chamada de "leitura de códigos de barras", mas sua funcionalidade vai além disso. O nome é mais simples, e a própria Google fornece exemplos do que é possível fazer com ela.
Deslizando um pouco, observamos que a biblioteca lê a maioria dos formatos padrão, não se limitando apenas a códigos de barras. Ela suporta vários tipos de códigos, alguns dos quais podem ser novos para você, mas são muito úteis para transmitir informações de forma resumida através de uma única imagem ou código.
A biblioteca oferece várias detecções automáticas de estruturas de dados que podemos interpretar e usar no app para extrair informações. Por exemplo, podemos obter e-mails, números de telefone, mensagens, números de livros, informações de conexão Wi-Fi, tipos de geolocalização e outras informações.
Tudo isso é processado localmente no dispositivo, o que permite execuções rápidas e garante a privacidade das pessoas que utilizam o app.
Ao deslizar um pouco mais para baixo, em "Resultados de exemplo", encontramos exemplos de códigos de barras no canto esquerdo das imagens e as informações que esses códigos retornam ao serem analisados. Por exemplo, podemos ver as dimensões do código e o valor bruto.
Mais abaixo, no canto esquerdo, há uma imagem de um QR Code e as informações específicas extraídas desse QR Code.
A biblioteca fornece o valor bruto e informações específicas para cada tipo de QR Code que analisamos. No topo da documentação da biblioteca, há mais informações que podemos consultar durante o desenvolvimento. É importante mencionar que existem três tipos principais: iOS, Android e o leitor de códigos do Google.
Usaremos a biblioteca do Android que já instalamos. O projeto está configurado, e agora começaremos a trabalhar nele. Até mais!
Com a biblioteca de detecção instalada, é hora de verificar o que no aplicativo utiliza essa biblioteca.
O aplicativo já está em funcionamento e exibe um botão verde com um QR Code no centro da tela. Ao clicar nesse botão, somos direcionados para uma tela de câmera. Neste ponto, pode ser que seja necessário conceder permissões para a câmera; para isso, clicamos em "Permitir".
Desejamos que, ao movimentar o celular ou usar o emulador com a câmera ativa e visualizar um QR Code, o aplicativo exiba uma informação na tela. Isso permitirá realizar a primeira detecção e começar a compreender como a biblioteca funciona.
Para fazer isso, utilizaremos nossa própria documentação, que já revisamos anteriormente. Na documentação, há uma seção sobre a "Configurar o leitor de código de barras", que fornece informações importantes. Por exemplo, é mencionado que devemos configurar uma variável chamada options
para definir algumas formatações que podem ou não ser aplicadas durante a detecção.
Ao continuar a leitura, encontramos uma lista de códigos que podemos ou não identificar, mas isso não é relevante para nós no momento. Mais adiante, encontramos uma seção resumida que informa que, a partir da versão 17.1.0
do modelo, podemos habilitar todos os possíveis códigos de barras com uma opção específica.
val options = BarcodeScannerOptions.Builder()
.setBarcodeFormats(...)
.enableAllPotentialBarcodes() // Optional
.build()
Copiamos o exemplo de código fornecido, retornamos ao projeto no Android Studio e localizamos a pasta "app/src/main/java/com/alura/confereai/ui/screens/camera/CameraScreen.kt
". Colamos o código acima do cameraAnalyzer
, na linha 59.
Realizamos a importação do BarcodeScannerOptions
teclando "Alt + Enter" sobre ele. Por padrão, o código não reconhece o setBarcodeFormats
, então removemos essa linha.
CameraScreen.kt
// código omitido
val options = BarcodeScannerOptions.Builder()
.enableAllPotentialBarcodes() // Optional
.build()
// código omitido
O código que copiamos prepara algumas opções para configurar o funcionamento do modelo. Neste caso, essas opções são usadas para habilitar a detecção de todos os tipos possíveis de códigos. Criamos uma variável para isso, mas ainda não temos o detector.
Para utilizar o detector, retornamos à documentação. Ao continuar a leitura, encontramos a seção sobre preparar a imagem de entrada, intitulada "Prepare the Input Image" ("Preparar a Imagem de Entrada").
Deslizando um pouco mais, encontramos uma seção com a classe private class YourImageAnalyzer
. O importante para nós é que ela contém um código com val mediaImage = imageProxy.image
.
private class YourImageAnalyzer : ImageAnalysis.Analyzer {
override fun analyze(imageProxy: ImageProxy) {
val mediaImage = imageProxy.image
if (mediaImage != null) {
val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
// Pass image to an ML Kit Vision API
// ...
}
}
}
Copiamos esse código, começando a partir dessa linha e incluindo o bloco if
.
Voltando ao nosso projeto, o cameraAnalyzer
corresponde ao trecho da documentação. Neste caso, é o cameraAnalyzer
localizado na linha 57. Pulamos algumas linhas e colamos o código que acabamos de copiar.
CameraScreen
// código omitido
val cameraAnalyzer = remember {
CameraAnalyzer { imageProxy ->
val mediaImage = imageProxy.image
if (mediaImage != null) {
val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
// Pass image to an ML Kit Vision API
// ...
}
imageProxy.close()
}
}
// código omitido
O que estamos fazendo aqui é o seguinte: pegamos o imageProxy
, que representa cada frame da câmera que vamos identificar, e armazenamos a imagem desse frame na variável mediaImage
. Se essa imagem não for nula, construímos uma imagem que será analisada.
Realizamos essa ação na linha 61. Por padrão, o InputImage
não será reconhecido, então posicionamos o cursor sobre ele, pressionamos "Alt + Enter" e selecionamos "import class 'InputImage'". Dentro de fromMediaImage()
, estamos passando a imagem e também o possível grau de rotação dela.
Com isso, temos a imagem disponível para análise, mas ainda não temos o scanner.
Voltamos à documentação e deslizamos até o final para encontrar a seção sobre como "Acessar uma instância do BarcodeScanner".
Trecho de código retirado da documentação:
val scanner = BarcodeScanning.getClient()
// Or, to specify the formats to recognize:
// val scanner = BarcodeScanning.getClient(options)
Para realizar o processamento, configuramos o scanner usando val scanner = BarcodeScanning.getClient()
. Se tivermos opções, também as passamos. Copiamos essa linha val scanner
e retornamos ao nosso projeto.
Acima do cameraAnalyzer
, na linha 57, adicionamos val scanner = BarcodeScanning.getClient()
. Importamos a classe BarcodeScanning
com "Alt + Enter". Nesse momento, as opções já estão configuradas para o scanner funcionar. Agora, passamos as opções dentro do getClient()
.
Para seguir a abordagem do Compose, colocamos remember
antes de BarcodeScanning
e envolvemos o BarcodeScanning
com chaves.
// código omitido
val scanner = remember {
BarcodeScanning.getClient(options)
}
val cameraAnalyzer = remember {
CameraAnalyzer { imageProxy ->
val mediaImage = imageProxy.image
if (mediaImage != null) {
val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
// Pass image to an ML Kit Vision API
// ...
}
imageProxy.close()
}
}
// código omitido
Agora, o nosso scanner está pronto para ser utilizado.
Dentro do cameraAnalyzer
, após preparar a imagem na linha 69, removemos os comentários de exemplo das linhas 69 e 70. Em seguida, chamamos o scanner
que declaramos anteriormente
O scanner
possui o método process()
, que recebe a nossa imagem de entrada. Passamos a image
para esse método, que inclui os métodos típicos das bibliotecas do MLKit. Utilizamos o onCompleteListener
para começar a editar o complete. Ele oferece algumas opções; selecionamos a opção addOnCompleteListener
.
// código omitido
val mediaImage = imageProxy.image
if (mediaImage != null) {
val image =
InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
scanner.process(image).addOnCompleteListener {
}
}
imageProxy.close()
}
}
// código omitido
No final do addOnCompleteListener
, pulamos uma linha após o image
com parênteses e formatamos o código com "Ctrl + Alt + L". Dentro desse bloco, movemos o código que estava na linha 76, imageProxy.close()
, para dentro do bloco addOnCompleteListener
.
// código omitido
val mediaImage = imageProxy.image
if (mediaImage != null) {
val image =
InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
scanner.process(image)
.addOnCompleteListener {
imageProxy.close()
}
}
// código omitido
Independentemente do resultado da análise, desejamos fechar o frame quando o processo for completado, ou seja, quando o método for chamado. Por isso, utilizamos imageProxy.close()
.
Se tudo ocorrer conforme o esperado, teremos um método de sucesso. Pulamos uma linha e chamamos o addOnSuccessListener()
. Dentro desse bloco, temos uma informação que chamamos de result
. Nesse result
, encontramos uma (mutable)list<Barcode>
ao inicarmos a digitação.
Para entender melhor essa informação, vamos imprimi-la na tela, exibindo pelo menos algumas das informações retornadas. Utilizamos o viewModel
, que contém o método setTextMessage()
para essa finalidade.
O método setTextMessage
recebe uma string, então primeiro precisamos armazenar essa string. Criamos uma variável chamada message
e a definimos como o result
.
Como o result
é uma lista, pegamos o primeiro item com first()
e, desse first, extraímos o rawValue
. Conforme a documentação, o rawValue
fornecerá informações sobre o que foi identificado. Colocamos a message
dentro do setTextMessage
.
Como a message
pode ser nula, usamos .toString
por enquanto para garantir que não haja problemas.
// código omitido
scanner.process(image)
.addOnSuccessListener { result ->
val message = result.first().rawValue
viewModel.setTextMessage(message.toString())
}
.addOnCompleteListener {
imageProxy.close()
}
// código omitido
// código omitido
val options = BarcodeScannerOptions.Builder()
.enableAllPotentialBarcodes() // Optional
.build()
val scanner = remember {
BarcodeScanning.getClient(options)
}
// código omitido
Para recapitular, começamos declarando as opções necessárias para realizar a detecção. Em seguida, criamos o scanner
, que analisa cada frame da imagem para verificar se há algum código utilizável.
// código omitido
val cameraAnalyzer = remember {
CameraAnalyzer { imageProxy ->
val mediaImage = imageProxy.image
if (mediaImage != null) {
val image =
InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
scanner.process(image)
.addOnSuccessListener { result ->
val message = result.first().rawValue
viewModel.setTextMessage(message.toString())
}
.addOnCompleteListener {
imageProxy.close()
}
// código omitido
Dentro do cameraAnalyzer
, pegamos uma imagem de cada frame, preparamos essa imagem para análise e a passamos para o scanner usando scanner.process()
. Adicionamos dois callbacks: um para completar a operação, que fecha a imageProxy
com imageProxy.close
, e outro para exibir o resultado na tela usando setTextMessage
, caso haja algum resultado útil.
Com isso, podemos rodar o projeto e verificar se, ao apontar a câmera para um código QR, alguma informação aparece na tela. Teclamos "Shift + F10" e o emulador é exibido com o nosso aplicativo do Confere aí.
No entanto, ao tentar realizar a primeira análise clicando no botão verde, o aplicativo abre e fecha automaticamente. Isso acontece devido a um detalhe na implementação: estamos tentando acessar o primeiro resultado (result.first
) sem garantir que a lista não esteja vazia. O result.first
só pode ser acessado se a lista contiver elementos.
Abaixo do nosso result
, adicionamos uma condição if
para verificar se result.isNotEmpty
. Envolvemos todo o bloco de código dentro dessa condição.
// código omitido
scanner.process(image)
.addOnSuccessListener { result ->
if (result.isNotEmpty()) {
val message = result.first().rawValue
viewModel.setTextMessage(message.toString())
}
}
.addOnCompleteListener {
imageProxy.close()
}
// código omitido
Rodamos o projeto novamente.
Se a lista contiver itens e não estiver vazia, extraímos um valor. O aplicativo já está funcionando e, ao clicar no mesmo botão de antes, ele não apresenta mais falhas. Ao apontá-lo para um código QR, a informação desejada aparece na tela.
Se afastarmos o celular do QR Code, a informação desaparece, e ela volta a aparecer quando colocamos o celular novamente na frente do código.
Por enquanto, visualizamos apenas uma informação na tela referente a um código QR de um site. Agora, vamos analisar como isso ocorreu e aprender a extrair mais informações de outros códigos!
O curso Android com IA: utilizando bibliotecas Google para extrair informações de códigos QRCode possui 123 minutos de vídeos, em um total de 42 atividades. Gostou? Conheça nossos outros cursos de IA para Mobile em Inteligência Artificial, ou leia nossos artigos de Inteligência Artificial.
Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:
Impulsione a sua carreira com os melhores cursos e faça parte da maior comunidade tech.
1 ano de Alura
Assine o PLUS e garanta:
Formações com mais de 1500 cursos atualizados e novos lançamentos semanais, em Programação, Inteligência Artificial, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.
A cada curso ou formação concluído, um novo certificado para turbinar seu currículo e LinkedIn.
No Discord, você tem acesso a eventos exclusivos, grupos de estudos e mentorias com especialistas de diferentes áreas.
Faça parte da maior comunidade Dev do país e crie conexões com mais de 120 mil pessoas no Discord.
Acesso ilimitado ao catálogo de Imersões da Alura para praticar conhecimentos em diferentes áreas.
Explore um universo de possibilidades na palma da sua mão. Baixe as aulas para assistir offline, onde e quando quiser.
Acelere o seu aprendizado com a IA da Alura e prepare-se para o mercado internacional.
1 ano de Alura
Todos os benefícios do PLUS e mais vantagens exclusivas:
Luri é nossa inteligência artificial que tira dúvidas, dá exemplos práticos, corrige exercícios e ajuda a mergulhar ainda mais durante as aulas. Você pode conversar com a Luri até 100 mensagens por semana.
Aprenda um novo idioma e expanda seus horizontes profissionais. Cursos de Inglês, Espanhol e Inglês para Devs, 100% focado em tecnologia.
Transforme a sua jornada com benefícios exclusivos e evolua ainda mais na sua carreira.
1 ano de Alura
Todos os benefícios do PRO e mais vantagens exclusivas:
Mensagens ilimitadas para estudar com a Luri, a IA da Alura, disponível 24hs para tirar suas dúvidas, dar exemplos práticos, corrigir exercícios e impulsionar seus estudos.
Envie imagens para a Luri e ela te ajuda a solucionar problemas, identificar erros, esclarecer gráficos, analisar design e muito mais.
Escolha os ebooks da Casa do Código, a editora da Alura, que apoiarão a sua jornada de aprendizado para sempre.